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 13namespace blink { 14 15PassRefPtrWillBeRawPtr<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 24PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaCondition(MediaQueryTokenIterator token, MediaQueryTokenIterator endToken) 25{ 26 return MediaQueryParser(MediaConditionParser).parseImpl(token, endToken); 27} 28 29const MediaQueryParser::State MediaQueryParser::ReadRestrictor = &MediaQueryParser::readRestrictor; 30const MediaQueryParser::State MediaQueryParser::ReadMediaType = &MediaQueryParser::readMediaType; 31const MediaQueryParser::State MediaQueryParser::ReadAnd = &MediaQueryParser::readAnd; 32const MediaQueryParser::State MediaQueryParser::ReadFeatureStart = &MediaQueryParser::readFeatureStart; 33const MediaQueryParser::State MediaQueryParser::ReadFeature = &MediaQueryParser::readFeature; 34const MediaQueryParser::State MediaQueryParser::ReadFeatureColon = &MediaQueryParser::readFeatureColon; 35const MediaQueryParser::State MediaQueryParser::ReadFeatureValue = &MediaQueryParser::readFeatureValue; 36const MediaQueryParser::State MediaQueryParser::ReadFeatureEnd = &MediaQueryParser::readFeatureEnd; 37const MediaQueryParser::State MediaQueryParser::SkipUntilComma = &MediaQueryParser::skipUntilComma; 38const MediaQueryParser::State MediaQueryParser::SkipUntilBlockEnd = &MediaQueryParser::skipUntilBlockEnd; 39const MediaQueryParser::State MediaQueryParser::Done = &MediaQueryParser::done; 40 41MediaQueryParser::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 51MediaQueryParser::~MediaQueryParser() { }; 52 53void 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 60void MediaQueryParser::readRestrictor(MediaQueryTokenType type, const MediaQueryToken& token) 61{ 62 readMediaType(type, token); 63} 64 65void 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 87void 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 101void MediaQueryParser::readFeatureStart(MediaQueryTokenType type, const MediaQueryToken& token) 102{ 103 if (type == LeftParenthesisToken) 104 m_state = ReadFeature; 105 else 106 m_state = SkipUntilComma; 107} 108 109void 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 119void 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 129void 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 139void 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 154void 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 163void MediaQueryParser::skipUntilBlockEnd(MediaQueryTokenType type, const MediaQueryToken& token) 164{ 165 if (token.blockType() == MediaQueryToken::BlockEnd && !m_blockWatcher.blockLevel()) 166 m_state = SkipUntilComma; 167} 168 169void MediaQueryParser::done(MediaQueryTokenType type, const MediaQueryToken& token) { } 170 171void 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 178void 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 191PassRefPtrWillBeRawPtr<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 204MediaQueryData::MediaQueryData() 205 : m_restrictor(MediaQuery::None) 206 , m_mediaType(MediaTypeNames::all) 207 , m_expressions(adoptPtrWillBeNoop(new ExpressionHeapVector)) 208 , m_mediaTypeSet(false) 209{ 210} 211 212void 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 222PassOwnPtrWillBeRawPtr<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 229bool 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 238void 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 260void MediaQueryData::setMediaType(const String& mediaType) 261{ 262 m_mediaType = mediaType; 263 m_mediaTypeSet = true; 264} 265 266} // namespace blink 267