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/CSSTokenizer.h"
11 #include "wtf/Vector.h"
15 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaQuerySet(const String& queryString)
17 // FIXME: Replace the CSSTokenizer with a generic CSSTokenizer, once there is one,
18 // or better yet, replace the MediaQueryParser with a generic thread-safe CSS parser.
19 Vector<CSSParserToken> tokens;
20 CSSTokenizer::tokenize(queryString, tokens);
21 return MediaQueryParser(MediaQuerySetParser).parseImpl(tokens.begin(), tokens.end());
24 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaCondition(CSSParserTokenIterator token, CSSParserTokenIterator endToken)
26 return MediaQueryParser(MediaConditionParser).parseImpl(token, endToken);
29 const MediaQueryParser::State MediaQueryParser::ReadRestrictor = &MediaQueryParser::readRestrictor;
30 const MediaQueryParser::State MediaQueryParser::ReadMediaNot = &MediaQueryParser::readMediaNot;
31 const MediaQueryParser::State MediaQueryParser::ReadMediaType = &MediaQueryParser::readMediaType;
32 const MediaQueryParser::State MediaQueryParser::ReadAnd = &MediaQueryParser::readAnd;
33 const MediaQueryParser::State MediaQueryParser::ReadFeatureStart = &MediaQueryParser::readFeatureStart;
34 const MediaQueryParser::State MediaQueryParser::ReadFeature = &MediaQueryParser::readFeature;
35 const MediaQueryParser::State MediaQueryParser::ReadFeatureColon = &MediaQueryParser::readFeatureColon;
36 const MediaQueryParser::State MediaQueryParser::ReadFeatureValue = &MediaQueryParser::readFeatureValue;
37 const MediaQueryParser::State MediaQueryParser::ReadFeatureEnd = &MediaQueryParser::readFeatureEnd;
38 const MediaQueryParser::State MediaQueryParser::SkipUntilComma = &MediaQueryParser::skipUntilComma;
39 const MediaQueryParser::State MediaQueryParser::SkipUntilBlockEnd = &MediaQueryParser::skipUntilBlockEnd;
40 const MediaQueryParser::State MediaQueryParser::Done = &MediaQueryParser::done;
42 MediaQueryParser::MediaQueryParser(ParserType parserType)
43 : m_parserType(parserType)
44 , m_querySet(MediaQuerySet::create())
46 if (parserType == MediaQuerySetParser)
47 m_state = &MediaQueryParser::readRestrictor;
48 else // MediaConditionParser
49 m_state = &MediaQueryParser::readMediaNot;
52 MediaQueryParser::~MediaQueryParser() { };
54 void MediaQueryParser::setStateAndRestrict(State state, MediaQuery::Restrictor restrictor)
56 m_mediaQueryData.setRestrictor(restrictor);
60 // State machine member functions start here
61 void MediaQueryParser::readRestrictor(CSSParserTokenType type, const CSSParserToken& token)
63 readMediaType(type, token);
66 void MediaQueryParser::readMediaNot(CSSParserTokenType type, const CSSParserToken& token)
68 if (type == IdentToken && equalIgnoringCase(token.value(), "not"))
69 setStateAndRestrict(ReadFeatureStart, MediaQuery::Not);
71 readFeatureStart(type, token);
74 void MediaQueryParser::readMediaType(CSSParserTokenType type, const CSSParserToken& token)
76 if (type == LeftParenthesisToken) {
77 m_state = ReadFeature;
78 } else if (type == IdentToken) {
79 if (m_state == ReadRestrictor && equalIgnoringCase(token.value(), "not")) {
80 setStateAndRestrict(ReadMediaType, MediaQuery::Not);
81 } else if (m_state == ReadRestrictor && equalIgnoringCase(token.value(), "only")) {
82 setStateAndRestrict(ReadMediaType, MediaQuery::Only);
84 m_mediaQueryData.setMediaType(token.value());
87 } else if (type == EOFToken && (!m_querySet->queryVector().size() || m_state != ReadRestrictor)) {
90 m_state = SkipUntilComma;
91 if (type == CommaToken)
92 skipUntilComma(type, token);
96 void MediaQueryParser::readAnd(CSSParserTokenType type, const CSSParserToken& token)
98 if (type == IdentToken && equalIgnoringCase(token.value(), "and")) {
99 m_state = ReadFeatureStart;
100 } else if (type == CommaToken && m_parserType != MediaConditionParser) {
101 m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery());
102 m_state = ReadRestrictor;
103 } else if (type == EOFToken) {
106 m_state = SkipUntilComma;
110 void MediaQueryParser::readFeatureStart(CSSParserTokenType type, const CSSParserToken& token)
112 if (type == LeftParenthesisToken)
113 m_state = ReadFeature;
115 m_state = SkipUntilComma;
118 void MediaQueryParser::readFeature(CSSParserTokenType type, const CSSParserToken& token)
120 if (type == IdentToken) {
121 m_mediaQueryData.setMediaFeature(token.value());
122 m_state = ReadFeatureColon;
124 m_state = SkipUntilComma;
128 void MediaQueryParser::readFeatureColon(CSSParserTokenType type, const CSSParserToken& token)
130 if (type == ColonToken)
131 m_state = ReadFeatureValue;
132 else if (type == RightParenthesisToken || type == EOFToken)
133 readFeatureEnd(type, token);
135 m_state = SkipUntilBlockEnd;
138 void MediaQueryParser::readFeatureValue(CSSParserTokenType type, const CSSParserToken& token)
140 if (type == DimensionToken && token.unitType() == CSSPrimitiveValue::CSS_UNKNOWN) {
141 m_state = SkipUntilComma;
143 m_mediaQueryData.addParserValue(type, token);
144 m_state = ReadFeatureEnd;
148 void MediaQueryParser::readFeatureEnd(CSSParserTokenType type, const CSSParserToken& token)
150 if (type == RightParenthesisToken || type == EOFToken) {
151 if (m_mediaQueryData.addExpression())
154 m_state = SkipUntilComma;
155 } else if (type == DelimiterToken && token.delimiter() == '/') {
156 m_mediaQueryData.addParserValue(type, token);
157 m_state = ReadFeatureValue;
159 m_state = SkipUntilBlockEnd;
163 void MediaQueryParser::skipUntilComma(CSSParserTokenType type, const CSSParserToken& token)
165 if ((type == CommaToken && !m_blockWatcher.blockLevel()) || type == EOFToken) {
166 m_state = ReadRestrictor;
167 m_mediaQueryData.clear();
168 m_querySet->addMediaQuery(MediaQuery::createNotAll());
172 void MediaQueryParser::skipUntilBlockEnd(CSSParserTokenType type, const CSSParserToken& token)
174 if (token.blockType() == CSSParserToken::BlockEnd && !m_blockWatcher.blockLevel())
175 m_state = SkipUntilComma;
178 void MediaQueryParser::done(CSSParserTokenType type, const CSSParserToken& token) { }
180 void MediaQueryParser::handleBlocks(const CSSParserToken& token)
182 if (token.blockType() == CSSParserToken::BlockStart
183 && (token.type() != LeftParenthesisToken || m_blockWatcher.blockLevel()))
184 m_state = SkipUntilBlockEnd;
187 void MediaQueryParser::processToken(const CSSParserToken& token)
189 CSSParserTokenType type = token.type();
192 m_blockWatcher.handleToken(token);
194 // Call the function that handles current state
195 if (type != WhitespaceToken && type != CommentToken)
196 ((this)->*(m_state))(type, token);
199 // The state machine loop
200 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseImpl(CSSParserTokenIterator token, CSSParserTokenIterator endToken)
202 for (; token != endToken; ++token)
203 processToken(*token);
205 if (m_state != ReadAnd && m_state != ReadRestrictor && m_state != Done && m_state != ReadMediaNot)
206 m_querySet->addMediaQuery(MediaQuery::createNotAll());
207 else if (m_mediaQueryData.currentMediaQueryChanged())
208 m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery());
213 MediaQueryData::MediaQueryData()
214 : m_restrictor(MediaQuery::None)
215 , m_mediaType(MediaTypeNames::all)
216 , m_expressions(adoptPtrWillBeNoop(new ExpressionHeapVector))
217 , m_mediaTypeSet(false)
221 void MediaQueryData::clear()
223 m_restrictor = MediaQuery::None;
224 m_mediaType = MediaTypeNames::all;
225 m_mediaTypeSet = false;
226 m_mediaFeature = String();
227 m_valueList.destroyAndClear();
228 m_expressions = adoptPtrWillBeNoop(new ExpressionHeapVector);
231 PassOwnPtrWillBeRawPtr<MediaQuery> MediaQueryData::takeMediaQuery()
233 OwnPtrWillBeRawPtr<MediaQuery> mediaQuery = adoptPtrWillBeNoop(new MediaQuery(m_restrictor, m_mediaType, m_expressions.release()));
235 return mediaQuery.release();
238 bool MediaQueryData::addExpression()
240 OwnPtrWillBeRawPtr<MediaQueryExp> expression = MediaQueryExp::createIfValid(m_mediaFeature, &m_valueList);
241 bool isValid = !!expression;
242 m_expressions->append(expression.release());
243 m_valueList.destroyAndClear();
247 void MediaQueryData::addParserValue(CSSParserTokenType type, const CSSParserToken& token)
249 CSSParserValue value;
250 if (type == NumberToken || type == PercentageToken || type == DimensionToken) {
251 value.setFromNumber(token.numericValue(), token.unitType());
252 value.isInt = (token.numericValueType() == IntegerValueType);
253 } else if (type == DelimiterToken) {
254 value.unit = CSSParserValue::Operator;
255 value.iValue = token.delimiter();
256 value.id = CSSValueInvalid;
259 CSSParserString tokenValue;
260 tokenValue.init(token.value());
261 value.unit = CSSPrimitiveValue::CSS_IDENT;
262 value.string = tokenValue;
263 value.id = cssValueKeywordID(tokenValue);
266 m_valueList.addValue(value);
269 void MediaQueryData::setMediaType(const String& mediaType)
271 m_mediaType = mediaType;
272 m_mediaTypeSet = true;