5aa3d4fed359208b485f99a009f4b0944f21bcd5
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / MediaQueryExp.cpp
1 /*
2  * CSS Media Query
3  *
4  * Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen@nokia.com>.
5  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6  * Copyright (C) 2013 Apple Inc. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "core/css/MediaQueryExp.h"
32
33 #include "core/css/CSSAspectRatioValue.h"
34 #include "core/css/CSSPrimitiveValue.h"
35 #include "core/css/parser/CSSParserValues.h"
36 #include "core/html/parser/HTMLParserIdioms.h"
37 #include "platform/Decimal.h"
38 #include "platform/RuntimeEnabledFeatures.h"
39 #include "wtf/text/StringBuffer.h"
40 #include "wtf/text/StringBuilder.h"
41
42 namespace blink {
43
44 using namespace MediaFeatureNames;
45
46 static inline bool featureWithCSSValueID(const String& mediaFeature, const CSSParserValue* value)
47 {
48     if (!value->id)
49         return false;
50
51     return mediaFeature == orientationMediaFeature
52         || mediaFeature == pointerMediaFeature
53         || mediaFeature == anyPointerMediaFeature
54         || (mediaFeature == hoverMediaFeature && RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled())
55         || mediaFeature == anyHoverMediaFeature
56         || mediaFeature == scanMediaFeature;
57 }
58
59 static inline bool featureWithValidIdent(const String& mediaFeature, CSSValueID ident)
60 {
61     if (mediaFeature == orientationMediaFeature)
62         return ident == CSSValuePortrait || ident == CSSValueLandscape;
63
64     if (mediaFeature == pointerMediaFeature || mediaFeature == anyPointerMediaFeature)
65         return ident == CSSValueNone || ident == CSSValueCoarse || ident == CSSValueFine;
66
67     if ((mediaFeature == hoverMediaFeature && RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled())
68         || mediaFeature == anyHoverMediaFeature)
69         return ident == CSSValueNone || ident == CSSValueOnDemand || ident == CSSValueHover;
70
71     if (mediaFeature == scanMediaFeature)
72         return ident == CSSValueInterlace || ident == CSSValueProgressive;
73
74     ASSERT_NOT_REACHED();
75     return false;
76 }
77
78 static bool positiveLengthUnit(const int unit)
79 {
80     switch (unit) {
81     case CSSPrimitiveValue::CSS_EMS:
82     case CSSPrimitiveValue::CSS_EXS:
83     case CSSPrimitiveValue::CSS_PX:
84     case CSSPrimitiveValue::CSS_CM:
85     case CSSPrimitiveValue::CSS_MM:
86     case CSSPrimitiveValue::CSS_IN:
87     case CSSPrimitiveValue::CSS_PT:
88     case CSSPrimitiveValue::CSS_PC:
89     case CSSPrimitiveValue::CSS_REMS:
90     case CSSPrimitiveValue::CSS_CHS:
91         return true;
92     }
93     return false;
94 }
95
96 static inline bool featureWithValidPositiveLength(const String& mediaFeature, const CSSParserValue* value)
97 {
98     if (!(positiveLengthUnit(value->unit) || (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue == 0)) || value->fValue < 0)
99         return false;
100
101
102     return mediaFeature == heightMediaFeature
103         || mediaFeature == maxHeightMediaFeature
104         || mediaFeature == minHeightMediaFeature
105         || mediaFeature == widthMediaFeature
106         || mediaFeature == maxWidthMediaFeature
107         || mediaFeature == minWidthMediaFeature
108         || mediaFeature == deviceHeightMediaFeature
109         || mediaFeature == maxDeviceHeightMediaFeature
110         || mediaFeature == minDeviceHeightMediaFeature
111         || mediaFeature == deviceWidthMediaFeature
112         || mediaFeature == minDeviceWidthMediaFeature
113         || mediaFeature == maxDeviceWidthMediaFeature;
114 }
115
116 static inline bool featureWithValidDensity(const String& mediaFeature, const CSSParserValue* value)
117 {
118     if ((value->unit != CSSPrimitiveValue::CSS_DPPX && value->unit != CSSPrimitiveValue::CSS_DPI && value->unit != CSSPrimitiveValue::CSS_DPCM) || value->fValue <= 0)
119         return false;
120
121     return mediaFeature == resolutionMediaFeature
122         || mediaFeature == minResolutionMediaFeature
123         || mediaFeature == maxResolutionMediaFeature;
124 }
125
126 static inline bool featureWithPositiveInteger(const String& mediaFeature, const CSSParserValue* value)
127 {
128     if (!value->isInt || value->fValue < 0)
129         return false;
130
131     return mediaFeature == colorMediaFeature
132         || mediaFeature == maxColorMediaFeature
133         || mediaFeature == minColorMediaFeature
134         || mediaFeature == colorIndexMediaFeature
135         || mediaFeature == maxColorIndexMediaFeature
136         || mediaFeature == minColorIndexMediaFeature
137         || mediaFeature == monochromeMediaFeature
138         || mediaFeature == maxMonochromeMediaFeature
139         || mediaFeature == minMonochromeMediaFeature;
140 }
141
142 static inline bool featureWithPositiveNumber(const String& mediaFeature, const CSSParserValue* value)
143 {
144     if (value->unit != CSSPrimitiveValue::CSS_NUMBER || value->fValue < 0)
145         return false;
146
147     return mediaFeature == transform3dMediaFeature
148         || mediaFeature == devicePixelRatioMediaFeature
149         || mediaFeature == maxDevicePixelRatioMediaFeature
150         || mediaFeature == minDevicePixelRatioMediaFeature;
151 }
152
153 static inline bool featureWithZeroOrOne(const String& mediaFeature, const CSSParserValue* value)
154 {
155     if (!value->isInt || !(value->fValue == 1 || !value->fValue))
156         return false;
157
158     return mediaFeature == gridMediaFeature
159         || (mediaFeature == hoverMediaFeature && !RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled());
160 }
161
162 static inline bool featureWithAspectRatio(const String& mediaFeature)
163 {
164     return mediaFeature == aspectRatioMediaFeature
165         || mediaFeature == deviceAspectRatioMediaFeature
166         || mediaFeature == minAspectRatioMediaFeature
167         || mediaFeature == maxAspectRatioMediaFeature
168         || mediaFeature == minDeviceAspectRatioMediaFeature
169         || mediaFeature == maxDeviceAspectRatioMediaFeature;
170 }
171
172 static inline bool featureWithoutValue(const String& mediaFeature)
173 {
174     // Media features that are prefixed by min/max cannot be used without a value.
175     return mediaFeature == monochromeMediaFeature
176         || mediaFeature == colorMediaFeature
177         || mediaFeature == colorIndexMediaFeature
178         || mediaFeature == gridMediaFeature
179         || mediaFeature == heightMediaFeature
180         || mediaFeature == widthMediaFeature
181         || mediaFeature == deviceHeightMediaFeature
182         || mediaFeature == deviceWidthMediaFeature
183         || mediaFeature == orientationMediaFeature
184         || mediaFeature == aspectRatioMediaFeature
185         || mediaFeature == deviceAspectRatioMediaFeature
186         || mediaFeature == hoverMediaFeature
187         || mediaFeature == anyHoverMediaFeature
188         || mediaFeature == transform3dMediaFeature
189         || mediaFeature == pointerMediaFeature
190         || mediaFeature == anyPointerMediaFeature
191         || mediaFeature == devicePixelRatioMediaFeature
192         || mediaFeature == resolutionMediaFeature
193         || mediaFeature == scanMediaFeature;
194 }
195
196 bool MediaQueryExp::isViewportDependent() const
197 {
198     return m_mediaFeature == widthMediaFeature
199         || m_mediaFeature == heightMediaFeature
200         || m_mediaFeature == minWidthMediaFeature
201         || m_mediaFeature == minHeightMediaFeature
202         || m_mediaFeature == maxWidthMediaFeature
203         || m_mediaFeature == maxHeightMediaFeature
204         || m_mediaFeature == orientationMediaFeature
205         || m_mediaFeature == aspectRatioMediaFeature
206         || m_mediaFeature == minAspectRatioMediaFeature
207         || m_mediaFeature == devicePixelRatioMediaFeature
208         || m_mediaFeature == resolutionMediaFeature
209         || m_mediaFeature == maxAspectRatioMediaFeature;
210 }
211
212 MediaQueryExp::MediaQueryExp(const MediaQueryExp& other)
213     : m_mediaFeature(other.mediaFeature())
214     , m_expValue(other.expValue())
215 {
216 }
217
218 MediaQueryExp::MediaQueryExp(const String& mediaFeature, const MediaQueryExpValue& expValue)
219     : m_mediaFeature(mediaFeature)
220     , m_expValue(expValue)
221 {
222 }
223
224 PassOwnPtrWillBeRawPtr<MediaQueryExp> MediaQueryExp::createIfValid(const String& mediaFeature, CSSParserValueList* valueList)
225 {
226     ASSERT(!mediaFeature.isNull());
227
228     MediaQueryExpValue expValue;
229     bool isValid = false;
230     String lowerMediaFeature = attemptStaticStringCreation(mediaFeature.lower());
231
232     // Create value for media query expression that must have 1 or more values.
233     if (valueList && valueList->size() > 0) {
234         if (valueList->size() == 1) {
235             CSSParserValue* value = valueList->current();
236             ASSERT(value);
237
238             if (featureWithCSSValueID(lowerMediaFeature, value) && featureWithValidIdent(lowerMediaFeature, value->id)) {
239                 // Media features that use CSSValueIDs.
240                 expValue.id = value->id;
241                 expValue.unit = CSSPrimitiveValue::CSS_VALUE_ID;
242                 expValue.isID = true;
243             } else if (featureWithValidDensity(lowerMediaFeature, value)
244                 || featureWithValidPositiveLength(lowerMediaFeature, value)) {
245                 // Media features that must have non-negative <density>, ie. dppx, dpi or dpcm,
246                 // or Media features that must have non-negative <length> or number value.
247                 expValue.value = value->fValue;
248                 expValue.unit = (CSSPrimitiveValue::UnitType)value->unit;
249                 expValue.isValue = true;
250             } else if (featureWithPositiveInteger(lowerMediaFeature, value)
251                 || featureWithPositiveNumber(lowerMediaFeature, value)
252                 || featureWithZeroOrOne(lowerMediaFeature, value)) {
253                 // Media features that must have non-negative integer value,
254                 // or media features that must have non-negative number value,
255                 // or media features that must have (0|1) value.
256                 expValue.value = value->fValue;
257                 expValue.unit = CSSPrimitiveValue::CSS_NUMBER;
258                 expValue.isValue = true;
259             }
260
261             isValid = (expValue.isID || expValue.isValue);
262
263         } else if (valueList->size() == 3 && featureWithAspectRatio(lowerMediaFeature)) {
264             // Create list of values.
265             // Currently accepts only <integer>/<integer>.
266             // Applicable to device-aspect-ratio and aspec-ratio.
267             isValid = true;
268             float numeratorValue = 0;
269             float denominatorValue = 0;
270             // The aspect-ratio must be <integer> (whitespace)? / (whitespace)? <integer>.
271             for (unsigned i = 0; i < 3; ++i, valueList->next()) {
272                 const CSSParserValue* value = valueList->current();
273                 if (i != 1 && value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue > 0 && value->isInt) {
274                     if (!i)
275                         numeratorValue = value->fValue;
276                     else
277                         denominatorValue = value->fValue;
278                 } else if (i == 1 && value->unit == CSSParserValue::Operator && value->iValue == '/') {
279                     continue;
280                 } else {
281                     isValid = false;
282                     break;
283                 }
284             }
285
286             if (isValid) {
287                 expValue.numerator = (unsigned)numeratorValue;
288                 expValue.denominator = (unsigned)denominatorValue;
289                 expValue.isRatio = true;
290             }
291         }
292     } else if (featureWithoutValue(lowerMediaFeature)) {
293         isValid = true;
294     }
295
296     if (!isValid)
297         return nullptr;
298
299     return adoptPtrWillBeNoop(new MediaQueryExp(lowerMediaFeature, expValue));
300 }
301
302 MediaQueryExp::~MediaQueryExp()
303 {
304 }
305
306 bool MediaQueryExp::operator==(const MediaQueryExp& other) const
307 {
308     return (other.m_mediaFeature == m_mediaFeature)
309         && ((!other.m_expValue.isValid() && !m_expValue.isValid())
310             || (other.m_expValue.isValid() && m_expValue.isValid() && other.m_expValue.equals(m_expValue)));
311 }
312
313 String MediaQueryExp::serialize() const
314 {
315     StringBuilder result;
316     result.append('(');
317     result.append(m_mediaFeature.lower());
318     if (m_expValue.isValid()) {
319         result.appendLiteral(": ");
320         result.append(m_expValue.cssText());
321     }
322     result.append(')');
323
324     return result.toString();
325 }
326
327 static inline String printNumber(double number)
328 {
329     return Decimal::fromDouble(number).toString();
330 }
331
332 String MediaQueryExpValue::cssText() const
333 {
334     StringBuilder output;
335     if (isValue) {
336         output.append(printNumber(value));
337         output.append(CSSPrimitiveValue::unitTypeToString(unit));
338     } else if (isRatio) {
339         output.append(printNumber(numerator));
340         output.append('/');
341         output.append(printNumber(denominator));
342     } else if (isID) {
343         output.append(getValueName(id));
344     }
345
346     return output.toString();
347 }
348
349 } // namespace