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 37namespace WebCore { 38 39CSSPreloadScanner::CSSPreloadScanner(Document* document) 40 : m_state(Initial) 41 , m_document(document) 42{ 43} 44 45void CSSPreloadScanner::reset() 46{ 47 m_state = Initial; 48 m_rule.clear(); 49 m_ruleValue.clear(); 50} 51 52void 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 61inline 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 149static 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 193void CSSPreloadScanner::emitRule() 194{ 195 if (equalIgnoringCase("import", m_rule.data(), m_rule.size())) { 196 String value = parseCSSStringOrURL(m_ruleValue.data(), m_ruleValue.size()); 197 if (!value.isEmpty()) 198 m_document->cachedResourceLoader()->preload(CachedResource::CSSStyleSheet, value, String(), m_scanningBody); 199 m_state = Initial; 200 } else if (equalIgnoringCase("charset", m_rule.data(), m_rule.size())) 201 m_state = Initial; 202 else 203 m_state = DoneParsingImportRules; 204 m_rule.clear(); 205 m_ruleValue.clear(); 206} 207 208} 209