Home | History | Annotate | Download | only in parser
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "core/css/parser/MediaQueryParser.h"
      7 
      8 #include "core/MediaTypeNames.h"
      9 #include "core/css/parser/CSSPropertyParser.h"
     10 #include "core/css/parser/MediaQueryTokenizer.h"
     11 #include "wtf/Vector.h"
     12 
     13 namespace blink {
     14 
     15 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaQuerySet(const String& queryString)
     16 {
     17     // FIXME: Replace the MediaQueryTokenizer with a generic CSSTokenizer, once there is one,
     18     // or better yet, replace the MediaQueryParser with a generic thread-safe CSS parser.
     19     Vector<MediaQueryToken> tokens;
     20     MediaQueryTokenizer::tokenize(queryString, tokens);
     21     return MediaQueryParser(MediaQuerySetParser).parseImpl(tokens.begin(), tokens.end());
     22 }
     23 
     24 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaCondition(MediaQueryTokenIterator token, MediaQueryTokenIterator endToken)
     25 {
     26     return MediaQueryParser(MediaConditionParser).parseImpl(token, endToken);
     27 }
     28 
     29 const MediaQueryParser::State MediaQueryParser::ReadRestrictor = &MediaQueryParser::readRestrictor;
     30 const MediaQueryParser::State MediaQueryParser::ReadMediaType = &MediaQueryParser::readMediaType;
     31 const MediaQueryParser::State MediaQueryParser::ReadAnd = &MediaQueryParser::readAnd;
     32 const MediaQueryParser::State MediaQueryParser::ReadFeatureStart = &MediaQueryParser::readFeatureStart;
     33 const MediaQueryParser::State MediaQueryParser::ReadFeature = &MediaQueryParser::readFeature;
     34 const MediaQueryParser::State MediaQueryParser::ReadFeatureColon = &MediaQueryParser::readFeatureColon;
     35 const MediaQueryParser::State MediaQueryParser::ReadFeatureValue = &MediaQueryParser::readFeatureValue;
     36 const MediaQueryParser::State MediaQueryParser::ReadFeatureEnd = &MediaQueryParser::readFeatureEnd;
     37 const MediaQueryParser::State MediaQueryParser::SkipUntilComma = &MediaQueryParser::skipUntilComma;
     38 const MediaQueryParser::State MediaQueryParser::SkipUntilBlockEnd = &MediaQueryParser::skipUntilBlockEnd;
     39 const MediaQueryParser::State MediaQueryParser::Done = &MediaQueryParser::done;
     40 
     41 MediaQueryParser::MediaQueryParser(ParserType parserType)
     42     : m_parserType(parserType)
     43     , m_querySet(MediaQuerySet::create())
     44 {
     45     if (parserType == MediaQuerySetParser)
     46         m_state = &MediaQueryParser::readRestrictor;
     47     else // MediaConditionParser
     48         m_state = &MediaQueryParser::readFeatureStart;
     49 }
     50 
     51 MediaQueryParser::~MediaQueryParser() { };
     52 
     53 void MediaQueryParser::setStateAndRestrict(State state, MediaQuery::Restrictor restrictor)
     54 {
     55     m_mediaQueryData.setRestrictor(restrictor);
     56     m_state = state;
     57 }
     58 
     59 // State machine member functions start here
     60 void MediaQueryParser::readRestrictor(MediaQueryTokenType type, const MediaQueryToken& token)
     61 {
     62     readMediaType(type, token);
     63 }
     64 
     65 void MediaQueryParser::readMediaType(MediaQueryTokenType type, const MediaQueryToken& token)
     66 {
     67     if (type == LeftParenthesisToken) {
     68         m_state = ReadFeature;
     69     } else if (type == IdentToken) {
     70         if (m_state == ReadRestrictor && equalIgnoringCase(token.value(), "not")) {
     71             setStateAndRestrict(ReadMediaType, MediaQuery::Not);
     72         } else if (m_state == ReadRestrictor && equalIgnoringCase(token.value(), "only")) {
     73             setStateAndRestrict(ReadMediaType, MediaQuery::Only);
     74         } else {
     75             m_mediaQueryData.setMediaType(token.value());
     76             m_state = ReadAnd;
     77         }
     78     } else if (type == EOFToken && (!m_querySet->queryVector().size() || m_state != ReadRestrictor)) {
     79         m_state = Done;
     80     } else {
     81         m_state = SkipUntilComma;
     82         if (type == CommaToken)
     83             skipUntilComma(type, token);
     84     }
     85 }
     86 
     87 void MediaQueryParser::readAnd(MediaQueryTokenType type, const MediaQueryToken& token)
     88 {
     89     if (type == IdentToken && equalIgnoringCase(token.value(), "and")) {
     90         m_state = ReadFeatureStart;
     91     } else if (type == CommaToken && m_parserType != MediaConditionParser) {
     92         m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery());
     93         m_state = ReadRestrictor;
     94     } else if (type == EOFToken) {
     95         m_state = Done;
     96     } else {
     97         m_state = SkipUntilComma;
     98     }
     99 }
    100 
    101 void MediaQueryParser::readFeatureStart(MediaQueryTokenType type, const MediaQueryToken& token)
    102 {
    103     if (type == LeftParenthesisToken)
    104         m_state = ReadFeature;
    105     else
    106         m_state = SkipUntilComma;
    107 }
    108 
    109 void MediaQueryParser::readFeature(MediaQueryTokenType type, const MediaQueryToken& token)
    110 {
    111     if (type == IdentToken) {
    112         m_mediaQueryData.setMediaFeature(token.value());
    113         m_state = ReadFeatureColon;
    114     } else {
    115         m_state = SkipUntilComma;
    116     }
    117 }
    118 
    119 void MediaQueryParser::readFeatureColon(MediaQueryTokenType type, const MediaQueryToken& token)
    120 {
    121     if (type == ColonToken)
    122         m_state = ReadFeatureValue;
    123     else if (type == RightParenthesisToken || type == EOFToken)
    124         readFeatureEnd(type, token);
    125     else
    126         m_state = SkipUntilBlockEnd;
    127 }
    128 
    129 void MediaQueryParser::readFeatureValue(MediaQueryTokenType type, const MediaQueryToken& token)
    130 {
    131     if (type == DimensionToken && token.unitType() == CSSPrimitiveValue::CSS_UNKNOWN) {
    132         m_state = SkipUntilComma;
    133     } else {
    134         m_mediaQueryData.addParserValue(type, token);
    135         m_state = ReadFeatureEnd;
    136     }
    137 }
    138 
    139 void MediaQueryParser::readFeatureEnd(MediaQueryTokenType type, const MediaQueryToken& token)
    140 {
    141     if (type == RightParenthesisToken || type == EOFToken) {
    142         if (m_mediaQueryData.addExpression())
    143             m_state = ReadAnd;
    144         else
    145             m_state = SkipUntilComma;
    146     } else if (type == DelimiterToken && token.delimiter() == '/') {
    147         m_mediaQueryData.addParserValue(type, token);
    148         m_state = ReadFeatureValue;
    149     } else {
    150         m_state = SkipUntilBlockEnd;
    151     }
    152 }
    153 
    154 void MediaQueryParser::skipUntilComma(MediaQueryTokenType type, const MediaQueryToken& token)
    155 {
    156     if ((type == CommaToken && !m_blockWatcher.blockLevel()) || type == EOFToken) {
    157         m_state = ReadRestrictor;
    158         m_mediaQueryData.clear();
    159         m_querySet->addMediaQuery(MediaQuery::createNotAll());
    160     }
    161 }
    162 
    163 void MediaQueryParser::skipUntilBlockEnd(MediaQueryTokenType type, const MediaQueryToken& token)
    164 {
    165     if (token.blockType() == MediaQueryToken::BlockEnd && !m_blockWatcher.blockLevel())
    166         m_state = SkipUntilComma;
    167 }
    168 
    169 void MediaQueryParser::done(MediaQueryTokenType type, const MediaQueryToken& token) { }
    170 
    171 void MediaQueryParser::handleBlocks(const MediaQueryToken& token)
    172 {
    173     if (token.blockType() == MediaQueryToken::BlockStart
    174         && (token.type() != LeftParenthesisToken || m_blockWatcher.blockLevel()))
    175             m_state = SkipUntilBlockEnd;
    176 }
    177 
    178 void MediaQueryParser::processToken(const MediaQueryToken& token)
    179 {
    180     MediaQueryTokenType type = token.type();
    181 
    182     handleBlocks(token);
    183     m_blockWatcher.handleToken(token);
    184 
    185     // Call the function that handles current state
    186     if (type != WhitespaceToken && type != CommentToken)
    187         ((this)->*(m_state))(type, token);
    188 }
    189 
    190 // The state machine loop
    191 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseImpl(MediaQueryTokenIterator token, MediaQueryTokenIterator endToken)
    192 {
    193     for (; token != endToken; ++token)
    194         processToken(*token);
    195 
    196     if (m_state != ReadAnd && m_state != ReadRestrictor && m_state != Done && (m_parserType != MediaConditionParser || m_state != ReadFeatureStart))
    197         m_querySet->addMediaQuery(MediaQuery::createNotAll());
    198     else if (m_mediaQueryData.currentMediaQueryChanged())
    199         m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery());
    200 
    201     return m_querySet;
    202 }
    203 
    204 MediaQueryData::MediaQueryData()
    205     : m_restrictor(MediaQuery::None)
    206     , m_mediaType(MediaTypeNames::all)
    207     , m_expressions(adoptPtrWillBeNoop(new ExpressionHeapVector))
    208     , m_mediaTypeSet(false)
    209 {
    210 }
    211 
    212 void MediaQueryData::clear()
    213 {
    214     m_restrictor = MediaQuery::None;
    215     m_mediaType = MediaTypeNames::all;
    216     m_mediaTypeSet = false;
    217     m_mediaFeature = String();
    218     m_valueList.destroyAndClear();
    219     m_expressions = adoptPtrWillBeNoop(new ExpressionHeapVector);
    220 }
    221 
    222 PassOwnPtrWillBeRawPtr<MediaQuery> MediaQueryData::takeMediaQuery()
    223 {
    224     OwnPtrWillBeRawPtr<MediaQuery> mediaQuery = adoptPtrWillBeNoop(new MediaQuery(m_restrictor, m_mediaType, m_expressions.release()));
    225     clear();
    226     return mediaQuery.release();
    227 }
    228 
    229 bool MediaQueryData::addExpression()
    230 {
    231     OwnPtrWillBeRawPtr<MediaQueryExp> expression = MediaQueryExp::createIfValid(m_mediaFeature, &m_valueList);
    232     bool isValid = !!expression;
    233     m_expressions->append(expression.release());
    234     m_valueList.destroyAndClear();
    235     return isValid;
    236 }
    237 
    238 void MediaQueryData::addParserValue(MediaQueryTokenType type, const MediaQueryToken& token)
    239 {
    240     CSSParserValue value;
    241     if (type == NumberToken || type == PercentageToken || type == DimensionToken) {
    242         value.setFromNumber(token.numericValue(), token.unitType());
    243         value.isInt = (token.numericValueType() == IntegerValueType);
    244     } else if (type == DelimiterToken) {
    245         value.unit = CSSParserValue::Operator;
    246         value.iValue = token.delimiter();
    247         value.id = CSSValueInvalid;
    248         value.isInt = false;
    249     } else {
    250         CSSParserFunction* function = new CSSParserFunction;
    251         function->name.init(token.value());
    252         value.setFromFunction(function);
    253         CSSParserString tokenValue;
    254         tokenValue.init(token.value());
    255         value.id = cssValueKeywordID(tokenValue);
    256     }
    257     m_valueList.addValue(value);
    258 }
    259 
    260 void MediaQueryData::setMediaType(const String& mediaType)
    261 {
    262     m_mediaType = mediaType;
    263     m_mediaTypeSet = true;
    264 }
    265 
    266 } // namespace blink
    267