Home | History | Annotate | Download | only in parser
      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.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