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