Unreviewed, rolling out r134442.
[WebKit-https.git] / Source / WebCore / html / parser / CSSPreloadScanner.cpp
1 /*
2  * Copyright (C) 2008, 2010 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 "CachedCSSStyleSheet.h"
32 #include "CachedResourceLoader.h"
33 #include "Document.h"
34 #include "HTMLParserIdioms.h"
35 #include "HTMLToken.h"
36
37 namespace WebCore {
38
39 CSSPreloadScanner::CSSPreloadScanner(Document* document)
40     : m_state(Initial)
41     , m_scanningBody(false)
42     , m_document(document)
43 {
44 }
45
46 void CSSPreloadScanner::reset()
47 {
48     m_state = Initial;
49     m_rule.clear();
50     m_ruleValue.clear();
51 }
52
53 void CSSPreloadScanner::scan(const HTMLToken& token, bool scanningBody)
54 {
55     m_scanningBody = scanningBody;
56
57     const HTMLToken::DataVector& characters = token.characters();
58     for (HTMLToken::DataVector::const_iterator iter = characters.begin(); iter != characters.end() && m_state != DoneParsingImportRules; ++iter)
59         tokenize(*iter);
60 }
61
62 inline void CSSPreloadScanner::tokenize(UChar c)
63 {
64     // We are just interested in @import rules, no need for real tokenization here
65     // Searching for other types of resources is probably low payoff.
66     switch (m_state) {
67     case Initial:
68         if (isHTMLSpace(c))
69             break;
70         if (c == '@')
71             m_state = RuleStart;
72         else if (c == '/')
73             m_state = MaybeComment;
74         else
75             m_state = DoneParsingImportRules;
76         break;
77     case MaybeComment:
78         if (c == '*')
79             m_state = Comment;
80         else
81             m_state = Initial;
82         break;
83     case Comment:
84         if (c == '*')
85             m_state = MaybeCommentEnd;
86         break;
87     case MaybeCommentEnd:
88         if (c == '*')
89             break;
90         if (c == '/')
91             m_state = Initial;
92         else
93             m_state = Comment;
94         break;
95     case RuleStart:
96         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
97             m_rule.clear();
98             m_ruleValue.clear();
99             m_rule.append(c);
100             m_state = Rule;
101         } else
102             m_state = Initial;
103         break;
104     case Rule:
105         if (isHTMLSpace(c))
106             m_state = AfterRule;
107         else if (c == ';')
108             m_state = Initial;
109         else
110             m_rule.append(c);
111         break;
112     case AfterRule:
113         if (isHTMLSpace(c))
114             break;
115         if (c == ';')
116             m_state = Initial;
117         else if (c == '{')
118             m_state = DoneParsingImportRules;
119         else {
120             m_state = RuleValue;
121             m_ruleValue.append(c);
122         }
123         break;
124     case RuleValue:
125         if (isHTMLSpace(c))
126             m_state = AfterRuleValue;
127         else if (c == ';')
128             emitRule();
129         else
130             m_ruleValue.append(c);
131         break;
132     case AfterRuleValue:
133         if (isHTMLSpace(c))
134             break;
135         if (c == ';')
136             emitRule();
137         else if (c == '{')
138             m_state = DoneParsingImportRules;
139         else {
140             // FIXME: media rules
141             m_state = Initial;
142         }
143         break;
144     case DoneParsingImportRules:
145         ASSERT_NOT_REACHED();
146         break;
147     }
148 }
149
150 static String parseCSSStringOrURL(const UChar* characters, size_t length)
151 {
152     size_t offset = 0;
153     size_t reducedLength = length;
154
155     while (reducedLength && isHTMLSpace(characters[offset])) {
156         ++offset;
157         --reducedLength;
158     }
159     while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1]))
160         --reducedLength;
161
162     if (reducedLength >= 5
163             && (characters[offset] == 'u' || characters[offset] == 'U')
164             && (characters[offset + 1] == 'r' || characters[offset + 1] == 'R')
165             && (characters[offset + 2] == 'l' || characters[offset + 2] == 'L')
166             && characters[offset + 3] == '('
167             && characters[offset + reducedLength - 1] == ')') {
168         offset += 4;
169         reducedLength -= 5;
170     }
171
172     while (reducedLength && isHTMLSpace(characters[offset])) {
173         ++offset;
174         --reducedLength;
175     }
176     while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1]))
177         --reducedLength;
178
179     if (reducedLength < 2 || characters[offset] != characters[offset + reducedLength - 1] || !(characters[offset] == '\'' || characters[offset] == '"'))
180         return String();
181     offset++;
182     reducedLength -= 2;
183
184     while (reducedLength && isHTMLSpace(characters[offset])) {
185         ++offset;
186         --reducedLength;
187     }
188     while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1]))
189         --reducedLength;
190
191     return String(characters + offset, reducedLength);
192 }
193
194 void CSSPreloadScanner::emitRule()
195 {
196     if (equalIgnoringCase("import", m_rule.characters(), m_rule.length())) {
197         String value = parseCSSStringOrURL(m_ruleValue.characters(), m_ruleValue.length());
198         if (!value.isEmpty()) {
199             ResourceRequest request(m_document->completeURL(value));
200             m_document->cachedResourceLoader()->preload(CachedResource::CSSStyleSheet, request, String(), m_scanningBody);
201         }
202         m_state = Initial;
203     } else if (equalIgnoringCase("charset", m_rule.characters(), m_rule.length()))
204         m_state = Initial;
205     else
206         m_state = DoneParsingImportRules;
207     m_rule.clear();
208     m_ruleValue.clear();
209 }
210
211 }