Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 "core/html/parser/CSSPreloadScanner.h"
30
31 #include "core/FetchInitiatorTypeNames.h"
32 #include "core/html/parser/HTMLParserIdioms.h"
33 #include "platform/text/SegmentedString.h"
34
35 namespace blink {
36
37 CSSPreloadScanner::CSSPreloadScanner()
38     : m_state(Initial)
39     , m_requests(0)
40 {
41 }
42
43 CSSPreloadScanner::~CSSPreloadScanner()
44 {
45 }
46
47 void CSSPreloadScanner::reset()
48 {
49     m_state = Initial;
50     m_rule.clear();
51     m_ruleValue.clear();
52 }
53
54 template<typename Char>
55 void CSSPreloadScanner::scanCommon(const Char* begin, const Char* end, const SegmentedString& source, PreloadRequestStream& requests)
56 {
57     m_requests = &requests;
58     for (const Char* it = begin; it != end && m_state != DoneParsingImportRules; ++it)
59         tokenize(*it, source);
60     m_requests = 0;
61 }
62
63 void CSSPreloadScanner::scan(const HTMLToken::DataVector& data, const SegmentedString& source, PreloadRequestStream& requests)
64 {
65     scanCommon(data.data(), data.data() + data.size(), source, requests);
66 }
67
68 void CSSPreloadScanner::scan(const String& tagName,  const SegmentedString& source, PreloadRequestStream& requests)
69 {
70     if (tagName.is8Bit()) {
71         const LChar* begin = tagName.characters8();
72         scanCommon(begin, begin + tagName.length(), source, requests);
73         return;
74     }
75     const UChar* begin = tagName.characters16();
76     scanCommon(begin, begin + tagName.length(), source, requests);
77 }
78
79 inline void CSSPreloadScanner::tokenize(UChar c, const SegmentedString& source)
80 {
81     // We are just interested in @import rules, no need for real tokenization here
82     // Searching for other types of resources is probably low payoff.
83     switch (m_state) {
84     case Initial:
85         if (isHTMLSpace<UChar>(c))
86             break;
87         if (c == '@')
88             m_state = RuleStart;
89         else if (c == '/')
90             m_state = MaybeComment;
91         else
92             m_state = DoneParsingImportRules;
93         break;
94     case MaybeComment:
95         if (c == '*')
96             m_state = Comment;
97         else
98             m_state = Initial;
99         break;
100     case Comment:
101         if (c == '*')
102             m_state = MaybeCommentEnd;
103         break;
104     case MaybeCommentEnd:
105         if (c == '*')
106             break;
107         if (c == '/')
108             m_state = Initial;
109         else
110             m_state = Comment;
111         break;
112     case RuleStart:
113         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
114             m_rule.clear();
115             m_ruleValue.clear();
116             m_rule.append(c);
117             m_state = Rule;
118         } else
119             m_state = Initial;
120         break;
121     case Rule:
122         if (isHTMLSpace<UChar>(c))
123             m_state = AfterRule;
124         else if (c == ';')
125             m_state = Initial;
126         else
127             m_rule.append(c);
128         break;
129     case AfterRule:
130         if (isHTMLSpace<UChar>(c))
131             break;
132         if (c == ';')
133             m_state = Initial;
134         else if (c == '{')
135             m_state = DoneParsingImportRules;
136         else {
137             m_state = RuleValue;
138             m_ruleValue.append(c);
139         }
140         break;
141     case RuleValue:
142         if (isHTMLSpace<UChar>(c))
143             m_state = AfterRuleValue;
144         else if (c == ';')
145             emitRule(source);
146         else
147             m_ruleValue.append(c);
148         break;
149     case AfterRuleValue:
150         if (isHTMLSpace<UChar>(c))
151             break;
152         if (c == ';')
153             emitRule(source);
154         else if (c == '{')
155             m_state = DoneParsingImportRules;
156         else {
157             // FIXME: media rules
158             m_state = Initial;
159         }
160         break;
161     case DoneParsingImportRules:
162         ASSERT_NOT_REACHED();
163         break;
164     }
165 }
166
167 static String parseCSSStringOrURL(const String& string)
168 {
169     size_t offset = 0;
170     size_t reducedLength = string.length();
171
172     while (reducedLength && isHTMLSpace<UChar>(string[offset])) {
173         ++offset;
174         --reducedLength;
175     }
176     while (reducedLength && isHTMLSpace<UChar>(string[offset + reducedLength - 1]))
177         --reducedLength;
178
179     if (reducedLength >= 5
180         && (string[offset] == 'u' || string[offset] == 'U')
181         && (string[offset + 1] == 'r' || string[offset + 1] == 'R')
182         && (string[offset + 2] == 'l' || string[offset + 2] == 'L')
183         && string[offset + 3] == '('
184         && string[offset + reducedLength - 1] == ')') {
185         offset += 4;
186         reducedLength -= 5;
187     }
188
189     while (reducedLength && isHTMLSpace<UChar>(string[offset])) {
190         ++offset;
191         --reducedLength;
192     }
193     while (reducedLength && isHTMLSpace<UChar>(string[offset + reducedLength - 1]))
194         --reducedLength;
195
196     if (reducedLength < 2 || string[offset] != string[offset + reducedLength - 1] || !(string[offset] == '\'' || string[offset] == '"'))
197         return String();
198     offset++;
199     reducedLength -= 2;
200
201     while (reducedLength && isHTMLSpace<UChar>(string[offset])) {
202         ++offset;
203         --reducedLength;
204     }
205     while (reducedLength && isHTMLSpace<UChar>(string[offset + reducedLength - 1]))
206         --reducedLength;
207
208     return string.substring(offset, reducedLength);
209 }
210
211 void CSSPreloadScanner::emitRule(const SegmentedString& source)
212 {
213     if (equalIgnoringCase(m_rule, "import")) {
214         String url = parseCSSStringOrURL(m_ruleValue.toString());
215         if (!url.isEmpty()) {
216             KURL baseElementURL; // FIXME: This should be passed in from the HTMLPreloadScaner via scan()!
217             TextPosition position = TextPosition(source.currentLine(), source.currentColumn());
218             OwnPtr<PreloadRequest> request = PreloadRequest::create(FetchInitiatorTypeNames::css, position, url, baseElementURL, Resource::CSSStyleSheet);
219             // FIXME: Should this be including the charset in the preload request?
220             m_requests->append(request.release());
221         }
222         m_state = Initial;
223     } else if (equalIgnoringCase(m_rule, "charset"))
224         m_state = Initial;
225     else
226         m_state = DoneParsingImportRules;
227     m_rule.clear();
228     m_ruleValue.clear();
229 }
230
231 }