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.
6 #include "core/css/parser/MediaQueryParser.h"
8 #include "core/MediaTypeNames.h"
9 #include "core/css/parser/CSSPropertyParser.h"
10 #include "core/css/parser/MediaQueryTokenizer.h"
11 #include "wtf/Vector.h"
15 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaQuerySet(const String& queryString)
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());
24 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaCondition(MediaQueryTokenIterator token, MediaQueryTokenIterator endToken)
26 return MediaQueryParser(MediaConditionParser).parseImpl(token, endToken);
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;
41 MediaQueryParser::MediaQueryParser(ParserType parserType)
42 : m_parserType(parserType)
43 , m_querySet(MediaQuerySet::create())
45 if (parserType == MediaQuerySetParser)
46 m_state = &MediaQueryParser::readRestrictor;
47 else // MediaConditionParser
48 m_state = &MediaQueryParser::readFeatureStart;
51 MediaQueryParser::~MediaQueryParser() { };
53 void MediaQueryParser::setStateAndRestrict(State state, MediaQuery::Restrictor restrictor)
55 m_mediaQueryData.setRestrictor(restrictor);
59 // State machine member functions start here
60 void MediaQueryParser::readRestrictor(MediaQueryTokenType type, const MediaQueryToken& token)
62 readMediaType(type, token);
65 void MediaQueryParser::readMediaType(MediaQueryTokenType type, const MediaQueryToken& token)
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);
75 m_mediaQueryData.setMediaType(token.value());
78 } else if (type == EOFToken && (!m_querySet->queryVector().size() || m_state != ReadRestrictor)) {
81 m_state = SkipUntilComma;
82 if (type == CommaToken)
83 skipUntilComma(type, token);
87 void MediaQueryParser::readAnd(MediaQueryTokenType type, const MediaQueryToken& token)
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) {
97 m_state = SkipUntilComma;
101 void MediaQueryParser::readFeatureStart(MediaQueryTokenType type, const MediaQueryToken& token)
103 if (type == LeftParenthesisToken)
104 m_state = ReadFeature;
106 m_state = SkipUntilComma;
109 void MediaQueryParser::readFeature(MediaQueryTokenType type, const MediaQueryToken& token)
111 if (type == IdentToken) {
112 m_mediaQueryData.setMediaFeature(token.value());
113 m_state = ReadFeatureColon;
115 m_state = SkipUntilComma;
119 void MediaQueryParser::readFeatureColon(MediaQueryTokenType type, const MediaQueryToken& token)
121 if (type == ColonToken)
122 m_state = ReadFeatureValue;
123 else if (type == RightParenthesisToken || type == EOFToken)
124 readFeatureEnd(type, token);
126 m_state = SkipUntilBlockEnd;
129 void MediaQueryParser::readFeatureValue(MediaQueryTokenType type, const MediaQueryToken& token)
131 if (type == DimensionToken && token.unitType() == CSSPrimitiveValue::CSS_UNKNOWN) {
132 m_state = SkipUntilComma;
134 m_mediaQueryData.addParserValue(type, token);
135 m_state = ReadFeatureEnd;
139 void MediaQueryParser::readFeatureEnd(MediaQueryTokenType type, const MediaQueryToken& token)
141 if (type == RightParenthesisToken || type == EOFToken) {
142 if (m_mediaQueryData.addExpression())
145 m_state = SkipUntilComma;
146 } else if (type == DelimiterToken && token.delimiter() == '/') {
147 m_mediaQueryData.addParserValue(type, token);
148 m_state = ReadFeatureValue;
150 m_state = SkipUntilBlockEnd;
154 void MediaQueryParser::skipUntilComma(MediaQueryTokenType type, const MediaQueryToken& token)
156 if ((type == CommaToken && !m_blockWatcher.blockLevel()) || type == EOFToken) {
157 m_state = ReadRestrictor;
158 m_mediaQueryData.clear();
159 m_querySet->addMediaQuery(MediaQuery::createNotAll());
163 void MediaQueryParser::skipUntilBlockEnd(MediaQueryTokenType type, const MediaQueryToken& token)
165 if (token.blockType() == MediaQueryToken::BlockEnd && !m_blockWatcher.blockLevel())
166 m_state = SkipUntilComma;
169 void MediaQueryParser::done(MediaQueryTokenType type, const MediaQueryToken& token) { }
171 void MediaQueryParser::handleBlocks(const MediaQueryToken& token)
173 if (token.blockType() == MediaQueryToken::BlockStart
174 && (token.type() != LeftParenthesisToken || m_blockWatcher.blockLevel()))
175 m_state = SkipUntilBlockEnd;
178 void MediaQueryParser::processToken(const MediaQueryToken& token)
180 MediaQueryTokenType type = token.type();
183 m_blockWatcher.handleToken(token);
185 // Call the function that handles current state
186 if (type != WhitespaceToken && type != CommentToken)
187 ((this)->*(m_state))(type, token);
190 // The state machine loop
191 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseImpl(MediaQueryTokenIterator token, MediaQueryTokenIterator endToken)
193 for (; token != endToken; ++token)
194 processToken(*token);
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());
204 MediaQueryData::MediaQueryData()
205 : m_restrictor(MediaQuery::None)
206 , m_mediaType(MediaTypeNames::all)
207 , m_expressions(adoptPtrWillBeNoop(new ExpressionHeapVector))
208 , m_mediaTypeSet(false)
212 void MediaQueryData::clear()
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);
222 PassOwnPtrWillBeRawPtr<MediaQuery> MediaQueryData::takeMediaQuery()
224 OwnPtrWillBeRawPtr<MediaQuery> mediaQuery = adoptPtrWillBeNoop(new MediaQuery(m_restrictor, m_mediaType, m_expressions.release()));
226 return mediaQuery.release();
229 bool MediaQueryData::addExpression()
231 OwnPtrWillBeRawPtr<MediaQueryExp> expression = MediaQueryExp::createIfValid(m_mediaFeature, &m_valueList);
232 bool isValid = !!expression;
233 m_expressions->append(expression.release());
234 m_valueList.destroyAndClear();
238 void MediaQueryData::addParserValue(MediaQueryTokenType type, const MediaQueryToken& token)
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;
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);
257 m_valueList.addValue(value);
260 void MediaQueryData::setMediaType(const String& mediaType)
262 m_mediaType = mediaType;
263 m_mediaTypeSet = true;