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