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