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