Upstream version 9.38.198.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/MediaQueryTokenizer.h"
11 #include "wtf/Vector.h"
12
13 namespace blink {
14
15 PassRefPtrWillBeRawPtr<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
24 PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseMediaCondition(MediaQueryTokenIterator token, MediaQueryTokenIterator endToken)
25 {
26     return MediaQueryParser(MediaConditionParser).parseImpl(token, endToken);
27 }
28
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;
40
41 MediaQueryParser::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
51 MediaQueryParser::~MediaQueryParser() { };
52
53 void 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
60 void MediaQueryParser::readRestrictor(MediaQueryTokenType type, const MediaQueryToken& token)
61 {
62     readMediaType(type, token);
63 }
64
65 void 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
87 void 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
101 void MediaQueryParser::readFeatureStart(MediaQueryTokenType type, const MediaQueryToken& token)
102 {
103     if (type == LeftParenthesisToken)
104         m_state = ReadFeature;
105     else
106         m_state = SkipUntilComma;
107 }
108
109 void 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
119 void 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
129 void 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
139 void 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
154 void 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
163 void MediaQueryParser::skipUntilBlockEnd(MediaQueryTokenType type, const MediaQueryToken& token)
164 {
165     if (token.blockType() == MediaQueryToken::BlockEnd && !m_blockWatcher.blockLevel())
166         m_state = SkipUntilComma;
167 }
168
169 void MediaQueryParser::done(MediaQueryTokenType type, const MediaQueryToken& token) { }
170
171 void 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
178 void 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
191 PassRefPtrWillBeRawPtr<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
204 MediaQueryData::MediaQueryData()
205     : m_restrictor(MediaQuery::None)
206     , m_mediaType(MediaTypeNames::all)
207     , m_expressions(adoptPtrWillBeNoop(new ExpressionHeapVector))
208     , m_mediaTypeSet(false)
209 {
210 }
211
212 void 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
222 PassOwnPtrWillBeRawPtr<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
229 bool 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
238 void 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
260 void MediaQueryData::setMediaType(const String& mediaType)
261 {
262     m_mediaType = mediaType;
263     m_mediaTypeSet = true;
264 }
265
266 } // namespace blink