Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / parser / MediaQueryParser.cpp
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/CSSTokenizer.h"
11 #include "wtf/Vector.h"
12
13 namespace blink {
14
15 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaQuerySet(const String& queryString)
16 {
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());
22 }
23
24 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaCondition(CSSParserTokenIterator token, CSSParserTokenIterator endToken)
25 {
26     return MediaQueryParser(MediaConditionParser).parseImpl(token, endToken);
27 }
28
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;
41
42 MediaQueryParser::MediaQueryParser(ParserType parserType)
43     : m_parserType(parserType)
44     , m_querySet(MediaQuerySet::create())
45 {
46     if (parserType == MediaQuerySetParser)
47         m_state = &MediaQueryParser::readRestrictor;
48     else // MediaConditionParser
49         m_state = &MediaQueryParser::readMediaNot;
50 }
51
52 MediaQueryParser::~MediaQueryParser() { };
53
54 void MediaQueryParser::setStateAndRestrict(State state, MediaQuery::Restrictor restrictor)
55 {
56     m_mediaQueryData.setRestrictor(restrictor);
57     m_state = state;
58 }
59
60 // State machine member functions start here
61 void MediaQueryParser::readRestrictor(CSSParserTokenType type, const CSSParserToken& token)
62 {
63     readMediaType(type, token);
64 }
65
66 void MediaQueryParser::readMediaNot(CSSParserTokenType type, const CSSParserToken& token)
67 {
68     if (type == IdentToken && equalIgnoringCase(token.value(), "not"))
69         setStateAndRestrict(ReadFeatureStart, MediaQuery::Not);
70     else
71         readFeatureStart(type, token);
72 }
73
74 void MediaQueryParser::readMediaType(CSSParserTokenType type, const CSSParserToken& token)
75 {
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);
83         } else {
84             m_mediaQueryData.setMediaType(token.value());
85             m_state = ReadAnd;
86         }
87     } else if (type == EOFToken && (!m_querySet->queryVector().size() || m_state != ReadRestrictor)) {
88         m_state = Done;
89     } else {
90         m_state = SkipUntilComma;
91         if (type == CommaToken)
92             skipUntilComma(type, token);
93     }
94 }
95
96 void MediaQueryParser::readAnd(CSSParserTokenType type, const CSSParserToken& token)
97 {
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) {
104         m_state = Done;
105     } else {
106         m_state = SkipUntilComma;
107     }
108 }
109
110 void MediaQueryParser::readFeatureStart(CSSParserTokenType type, const CSSParserToken& token)
111 {
112     if (type == LeftParenthesisToken)
113         m_state = ReadFeature;
114     else
115         m_state = SkipUntilComma;
116 }
117
118 void MediaQueryParser::readFeature(CSSParserTokenType type, const CSSParserToken& token)
119 {
120     if (type == IdentToken) {
121         m_mediaQueryData.setMediaFeature(token.value());
122         m_state = ReadFeatureColon;
123     } else {
124         m_state = SkipUntilComma;
125     }
126 }
127
128 void MediaQueryParser::readFeatureColon(CSSParserTokenType type, const CSSParserToken& token)
129 {
130     if (type == ColonToken)
131         m_state = ReadFeatureValue;
132     else if (type == RightParenthesisToken || type == EOFToken)
133         readFeatureEnd(type, token);
134     else
135         m_state = SkipUntilBlockEnd;
136 }
137
138 void MediaQueryParser::readFeatureValue(CSSParserTokenType type, const CSSParserToken& token)
139 {
140     if (type == DimensionToken && token.unitType() == CSSPrimitiveValue::CSS_UNKNOWN) {
141         m_state = SkipUntilComma;
142     } else {
143         m_mediaQueryData.addParserValue(type, token);
144         m_state = ReadFeatureEnd;
145     }
146 }
147
148 void MediaQueryParser::readFeatureEnd(CSSParserTokenType type, const CSSParserToken& token)
149 {
150     if (type == RightParenthesisToken || type == EOFToken) {
151         if (m_mediaQueryData.addExpression())
152             m_state = ReadAnd;
153         else
154             m_state = SkipUntilComma;
155     } else if (type == DelimiterToken && token.delimiter() == '/') {
156         m_mediaQueryData.addParserValue(type, token);
157         m_state = ReadFeatureValue;
158     } else {
159         m_state = SkipUntilBlockEnd;
160     }
161 }
162
163 void MediaQueryParser::skipUntilComma(CSSParserTokenType type, const CSSParserToken& token)
164 {
165     if ((type == CommaToken && !m_blockWatcher.blockLevel()) || type == EOFToken) {
166         m_state = ReadRestrictor;
167         m_mediaQueryData.clear();
168         m_querySet->addMediaQuery(MediaQuery::createNotAll());
169     }
170 }
171
172 void MediaQueryParser::skipUntilBlockEnd(CSSParserTokenType type, const CSSParserToken& token)
173 {
174     if (token.blockType() == CSSParserToken::BlockEnd && !m_blockWatcher.blockLevel())
175         m_state = SkipUntilComma;
176 }
177
178 void MediaQueryParser::done(CSSParserTokenType type, const CSSParserToken& token) { }
179
180 void MediaQueryParser::handleBlocks(const CSSParserToken& token)
181 {
182     if (token.blockType() == CSSParserToken::BlockStart
183         && (token.type() != LeftParenthesisToken || m_blockWatcher.blockLevel()))
184             m_state = SkipUntilBlockEnd;
185 }
186
187 void MediaQueryParser::processToken(const CSSParserToken& token)
188 {
189     CSSParserTokenType type = token.type();
190
191     handleBlocks(token);
192     m_blockWatcher.handleToken(token);
193
194     // Call the function that handles current state
195     if (type != WhitespaceToken && type != CommentToken)
196         ((this)->*(m_state))(type, token);
197 }
198
199 // The state machine loop
200 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseImpl(CSSParserTokenIterator token, CSSParserTokenIterator endToken)
201 {
202     for (; token != endToken; ++token)
203         processToken(*token);
204
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());
209
210     return m_querySet;
211 }
212
213 MediaQueryData::MediaQueryData()
214     : m_restrictor(MediaQuery::None)
215     , m_mediaType(MediaTypeNames::all)
216     , m_expressions(adoptPtrWillBeNoop(new ExpressionHeapVector))
217     , m_mediaTypeSet(false)
218 {
219 }
220
221 void MediaQueryData::clear()
222 {
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);
229 }
230
231 PassOwnPtrWillBeRawPtr<MediaQuery> MediaQueryData::takeMediaQuery()
232 {
233     OwnPtrWillBeRawPtr<MediaQuery> mediaQuery = adoptPtrWillBeNoop(new MediaQuery(m_restrictor, m_mediaType, m_expressions.release()));
234     clear();
235     return mediaQuery.release();
236 }
237
238 bool MediaQueryData::addExpression()
239 {
240     OwnPtrWillBeRawPtr<MediaQueryExp> expression = MediaQueryExp::createIfValid(m_mediaFeature, &m_valueList);
241     bool isValid = !!expression;
242     m_expressions->append(expression.release());
243     m_valueList.destroyAndClear();
244     return isValid;
245 }
246
247 void MediaQueryData::addParserValue(CSSParserTokenType type, const CSSParserToken& token)
248 {
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;
257         value.isInt = false;
258     } else {
259         CSSParserString tokenValue;
260         tokenValue.init(token.value());
261         value.unit = CSSPrimitiveValue::CSS_IDENT;
262         value.string = tokenValue;
263         value.id = cssValueKeywordID(tokenValue);
264         value.isInt = false;
265     }
266     m_valueList.addValue(value);
267 }
268
269 void MediaQueryData::setMediaType(const String& mediaType)
270 {
271     m_mediaType = mediaType;
272     m_mediaTypeSet = true;
273 }
274
275 } // namespace blink