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