Shrink various loading-related enums to shrink CachedResource
[WebKit-https.git] / Source / WebCore / html / parser / CSSPreloadScanner.cpp
1 /*
2  * Copyright (C) 2008, 2010, 2013, 2014 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/
4  * Copyright (C) 2010 Google Inc. All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26  */
27
28 #include "config.h"
29 #include "CSSPreloadScanner.h"
30
31 #include "HTMLParserIdioms.h"
32 #include <wtf/SetForScope.h>
33
34 namespace WebCore {
35
36 CSSPreloadScanner::CSSPreloadScanner()
37     : m_state(Initial)
38     , m_requests(nullptr)
39 {
40 }
41
42 CSSPreloadScanner::~CSSPreloadScanner() = default;
43
44 void CSSPreloadScanner::reset()
45 {
46     m_state = Initial;
47     m_rule.clear();
48     m_ruleValue.clear();
49 }
50
51 void CSSPreloadScanner::scan(const HTMLToken::DataVector& data, PreloadRequestStream& requests)
52 {
53     ASSERT(!m_requests);
54     SetForScope<PreloadRequestStream*> change(m_requests, &requests);
55
56     for (UChar c : data) {
57         if (m_state == DoneParsingImportRules)
58             break;
59
60         tokenize(c);
61     }
62 }
63
64 inline void CSSPreloadScanner::tokenize(UChar c)
65 {
66     // We are just interested in @import rules, no need for real tokenization here
67     // Searching for other types of resources is probably low payoff.
68     switch (m_state) {
69     case Initial:
70         if (isHTMLSpace(c))
71             break;
72         if (c == '@')
73             m_state = RuleStart;
74         else if (c == '/')
75             m_state = MaybeComment;
76         else
77             m_state = DoneParsingImportRules;
78         break;
79     case MaybeComment:
80         if (c == '*')
81             m_state = Comment;
82         else
83             m_state = Initial;
84         break;
85     case Comment:
86         if (c == '*')
87             m_state = MaybeCommentEnd;
88         break;
89     case MaybeCommentEnd:
90         if (c == '*')
91             break;
92         if (c == '/')
93             m_state = Initial;
94         else
95             m_state = Comment;
96         break;
97     case RuleStart:
98         if (isASCIIAlpha(c)) {
99             m_rule.clear();
100             m_ruleValue.clear();
101             m_rule.append(c);
102             m_state = Rule;
103         } else
104             m_state = Initial;
105         break;
106     case Rule:
107         if (isHTMLSpace(c))
108             m_state = AfterRule;
109         else if (c == ';')
110             m_state = Initial;
111         else
112             m_rule.append(c);
113         break;
114     case AfterRule:
115         if (isHTMLSpace(c))
116             break;
117         if (c == ';')
118             m_state = Initial;
119         else if (c == '{')
120             m_state = DoneParsingImportRules;
121         else {
122             m_state = RuleValue;
123             m_ruleValue.append(c);
124         }
125         break;
126     case RuleValue:
127         if (isHTMLSpace(c))
128             m_state = AfterRuleValue;
129         else if (c == ';')
130             emitRule();
131         else
132             m_ruleValue.append(c);
133         break;
134     case AfterRuleValue:
135         if (isHTMLSpace(c))
136             break;
137         if (c == ';')
138             emitRule();
139         else if (c == '{')
140             m_state = DoneParsingImportRules;
141         else {
142             // FIXME: media rules
143             m_state = Initial;
144         }
145         break;
146     case DoneParsingImportRules:
147         ASSERT_NOT_REACHED();
148         break;
149     }
150 }
151
152 static String parseCSSStringOrURL(const UChar* characters, size_t length)
153 {
154     size_t offset = 0;
155     size_t reducedLength = length;
156
157     while (reducedLength && isHTMLSpace(characters[offset])) {
158         ++offset;
159         --reducedLength;
160     }
161     while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1]))
162         --reducedLength;
163
164     if (reducedLength >= 5
165             && (characters[offset] == 'u' || characters[offset] == 'U')
166             && (characters[offset + 1] == 'r' || characters[offset + 1] == 'R')
167             && (characters[offset + 2] == 'l' || characters[offset + 2] == 'L')
168             && characters[offset + 3] == '('
169             && characters[offset + reducedLength - 1] == ')') {
170         offset += 4;
171         reducedLength -= 5;
172     }
173
174     while (reducedLength && isHTMLSpace(characters[offset])) {
175         ++offset;
176         --reducedLength;
177     }
178     while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1]))
179         --reducedLength;
180
181     if (reducedLength < 2 || characters[offset] != characters[offset + reducedLength - 1] || !(characters[offset] == '\'' || characters[offset] == '"'))
182         return String();
183     offset++;
184     reducedLength -= 2;
185
186     while (reducedLength && isHTMLSpace(characters[offset])) {
187         ++offset;
188         --reducedLength;
189     }
190     while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1]))
191         --reducedLength;
192
193     return String(characters + offset, reducedLength);
194 }
195
196 void CSSPreloadScanner::emitRule()
197 {
198     StringView rule(m_rule.data(), m_rule.size());
199     if (equalLettersIgnoringASCIICase(rule, "import")) {
200         String url = parseCSSStringOrURL(m_ruleValue.data(), m_ruleValue.size());
201         if (!url.isEmpty()) {
202             URL baseElementURL; // FIXME: This should be passed in from the HTMLPreloadScanner via scan(): without it we will get relative URLs wrong.
203             // FIXME: Should this be including the charset in the preload request?
204             m_requests->append(std::make_unique<PreloadRequest>("css", url, baseElementURL, CachedResource::Type::CSSStyleSheet, String(), PreloadRequest::ModuleScript::No));
205         }
206         m_state = Initial;
207     } else if (equalLettersIgnoringASCIICase(rule, "charset"))
208         m_state = Initial;
209     else
210         m_state = DoneParsingImportRules;
211     m_rule.clear();
212     m_ruleValue.clear();
213 }
214
215 }