CSSPreloadScanner.cpp revision cad810f21b803229eb11403f9209855525a25d57
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(); ++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 (c == '@') 68 m_state = RuleStart; 69 else if (c == '/') 70 m_state = MaybeComment; 71 break; 72 case MaybeComment: 73 if (c == '*') 74 m_state = Comment; 75 else 76 m_state = Initial; 77 break; 78 case Comment: 79 if (c == '*') 80 m_state = MaybeCommentEnd; 81 break; 82 case MaybeCommentEnd: 83 if (c == '/') 84 m_state = Initial; 85 else if (c == '*') 86 ; 87 else 88 m_state = Comment; 89 break; 90 case RuleStart: 91 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { 92 m_rule.clear(); 93 m_ruleValue.clear(); 94 m_rule.append(c); 95 m_state = Rule; 96 } else 97 m_state = Initial; 98 break; 99 case Rule: 100 if (isHTMLSpace(c)) 101 m_state = AfterRule; 102 else if (c == ';') 103 m_state = Initial; 104 else 105 m_rule.append(c); 106 break; 107 case AfterRule: 108 if (isHTMLSpace(c)) 109 ; 110 else if (c == ';') 111 m_state = Initial; 112 else { 113 m_state = RuleValue; 114 m_ruleValue.append(c); 115 } 116 break; 117 case RuleValue: 118 if (isHTMLSpace(c)) 119 m_state = AfterRuleValue; 120 else if (c == ';') { 121 emitRule(); 122 m_state = Initial; 123 } else 124 m_ruleValue.append(c); 125 break; 126 case AfterRuleValue: 127 if (isHTMLSpace(c)) 128 ; 129 else if (c == ';') { 130 emitRule(); 131 m_state = Initial; 132 } else { 133 // FIXME: media rules 134 m_state = Initial; 135 } 136 break; 137 } 138} 139 140static String parseCSSStringOrURL(const UChar* characters, size_t length) 141{ 142 size_t offset = 0; 143 size_t reducedLength = length; 144 145 while (reducedLength && isHTMLSpace(characters[offset])) { 146 ++offset; 147 --reducedLength; 148 } 149 while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1])) 150 --reducedLength; 151 152 if (reducedLength >= 5 153 && (characters[offset] == 'u' || characters[offset] == 'U') 154 && (characters[offset + 1] == 'r' || characters[offset + 1] == 'R') 155 && (characters[offset + 2] == 'l' || characters[offset + 2] == 'L') 156 && characters[offset + 3] == '(' 157 && characters[offset + reducedLength - 1] == ')') { 158 offset += 4; 159 reducedLength -= 5; 160 } 161 162 while (reducedLength && isHTMLSpace(characters[offset])) { 163 ++offset; 164 --reducedLength; 165 } 166 while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1])) 167 --reducedLength; 168 169 if (reducedLength < 2 || characters[offset] != characters[offset + reducedLength - 1] || !(characters[offset] == '\'' || characters[offset] == '"')) 170 return String(); 171 offset++; 172 reducedLength -= 2; 173 174 while (reducedLength && isHTMLSpace(characters[offset])) { 175 ++offset; 176 --reducedLength; 177 } 178 while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1])) 179 --reducedLength; 180 181 return String(characters + offset, reducedLength); 182} 183 184void CSSPreloadScanner::emitRule() 185{ 186 if (equalIgnoringCase("import", m_rule.data(), m_rule.size())) { 187 String value = parseCSSStringOrURL(m_ruleValue.data(), m_ruleValue.size()); 188 if (!value.isEmpty()) 189 m_document->cachedResourceLoader()->preload(CachedResource::CSSStyleSheet, value, String(), m_scanningBody); 190 } 191 m_rule.clear(); 192 m_ruleValue.clear(); 193} 194 195} 196