Upstream version 7.36.149.0
[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/CSSParserValues.h"
35 #include "core/css/CSSPrimitiveValue.h"
36 #include "core/html/parser/HTMLParserIdioms.h"
37 #include "wtf/DecimalNumber.h"
38 #include "wtf/text/StringBuffer.h"
39 #include "wtf/text/StringBuilder.h"
40
41 namespace WebCore {
42
43 using namespace MediaFeatureNames;
44
45 static inline bool featureWithCSSValueID(const String& mediaFeature, const CSSParserValue* value)
46 {
47     if (!value->id)
48         return false;
49
50     return mediaFeature == orientationMediaFeature
51         || mediaFeature == pointerMediaFeature
52         || mediaFeature == scanMediaFeature;
53 }
54
55 static inline bool featureWithValidIdent(const String& mediaFeature, CSSValueID ident)
56 {
57     if (mediaFeature == orientationMediaFeature)
58         return ident == CSSValuePortrait || ident == CSSValueLandscape;
59
60     if (mediaFeature == pointerMediaFeature)
61         return ident == CSSValueNone || ident == CSSValueCoarse || ident == CSSValueFine;
62
63     if (mediaFeature == scanMediaFeature)
64         return ident == CSSValueInterlace || ident == CSSValueProgressive;
65
66     ASSERT_NOT_REACHED();
67     return false;
68 }
69
70 static inline bool featureWithValidPositiveLength(const String& mediaFeature, const CSSParserValue* value)
71 {
72     if (!(((value->unit >= CSSPrimitiveValue::CSS_EMS && value->unit <= CSSPrimitiveValue::CSS_PC) || value->unit == CSSPrimitiveValue::CSS_REMS) || (value->unit == CSSPrimitiveValue::CSS_NUMBER && !(value->fValue))) || value->fValue < 0)
73         return false;
74
75
76     return mediaFeature == heightMediaFeature
77         || mediaFeature == maxHeightMediaFeature
78         || mediaFeature == minHeightMediaFeature
79         || mediaFeature == widthMediaFeature
80         || mediaFeature == maxWidthMediaFeature
81         || mediaFeature == minWidthMediaFeature
82         || mediaFeature == deviceHeightMediaFeature
83         || mediaFeature == maxDeviceHeightMediaFeature
84         || mediaFeature == minDeviceHeightMediaFeature
85         || mediaFeature == deviceWidthMediaFeature
86         || mediaFeature == minDeviceWidthMediaFeature
87         || mediaFeature == maxDeviceWidthMediaFeature;
88 }
89
90 static inline bool featureWithValidDensity(const String& mediaFeature, const CSSParserValue* value)
91 {
92     if ((value->unit != CSSPrimitiveValue::CSS_DPPX && value->unit != CSSPrimitiveValue::CSS_DPI && value->unit != CSSPrimitiveValue::CSS_DPCM) || value->fValue <= 0)
93         return false;
94
95     return mediaFeature == resolutionMediaFeature
96         || mediaFeature == minResolutionMediaFeature
97         || mediaFeature == maxResolutionMediaFeature;
98 }
99
100 static inline bool featureWithPositiveInteger(const String& mediaFeature, const CSSParserValue* value)
101 {
102     if (!value->isInt || value->fValue < 0)
103         return false;
104
105     return mediaFeature == colorMediaFeature
106         || mediaFeature == maxColorMediaFeature
107         || mediaFeature == minColorMediaFeature
108         || mediaFeature == colorIndexMediaFeature
109         || mediaFeature == maxColorIndexMediaFeature
110         || mediaFeature == minColorIndexMediaFeature
111         || mediaFeature == monochromeMediaFeature
112         || mediaFeature == maxMonochromeMediaFeature
113         || mediaFeature == minMonochromeMediaFeature;
114 }
115
116 static inline bool featureWithPositiveNumber(const String& mediaFeature, const CSSParserValue* value)
117 {
118     if (value->unit != CSSPrimitiveValue::CSS_NUMBER || value->fValue < 0)
119         return false;
120
121     return mediaFeature == transform3dMediaFeature
122         || mediaFeature == devicePixelRatioMediaFeature
123         || mediaFeature == maxDevicePixelRatioMediaFeature
124         || mediaFeature == minDevicePixelRatioMediaFeature;
125 }
126
127 static inline bool featureWithZeroOrOne(const String& mediaFeature, const CSSParserValue* value)
128 {
129     if (!value->isInt || !(value->fValue == 1 || !value->fValue))
130         return false;
131
132     return mediaFeature == gridMediaFeature
133         || mediaFeature == hoverMediaFeature;
134 }
135
136 static inline bool featureWithAspectRatio(const String& mediaFeature)
137 {
138     return mediaFeature == aspectRatioMediaFeature
139         || mediaFeature == deviceAspectRatioMediaFeature
140         || mediaFeature == minAspectRatioMediaFeature
141         || mediaFeature == maxAspectRatioMediaFeature
142         || mediaFeature == minDeviceAspectRatioMediaFeature
143         || mediaFeature == maxDeviceAspectRatioMediaFeature;
144 }
145
146 static inline bool featureWithoutValue(const String& mediaFeature)
147 {
148     // Media features that are prefixed by min/max cannot be used without a value.
149     return mediaFeature == monochromeMediaFeature
150         || mediaFeature == colorMediaFeature
151         || mediaFeature == colorIndexMediaFeature
152         || mediaFeature == gridMediaFeature
153         || mediaFeature == heightMediaFeature
154         || mediaFeature == widthMediaFeature
155         || mediaFeature == deviceHeightMediaFeature
156         || mediaFeature == deviceWidthMediaFeature
157         || mediaFeature == orientationMediaFeature
158         || mediaFeature == aspectRatioMediaFeature
159         || mediaFeature == deviceAspectRatioMediaFeature
160         || mediaFeature == hoverMediaFeature
161         || mediaFeature == transform3dMediaFeature
162         || mediaFeature == pointerMediaFeature
163         || mediaFeature == devicePixelRatioMediaFeature
164         || mediaFeature == resolutionMediaFeature
165         || mediaFeature == scanMediaFeature;
166 }
167
168 bool MediaQueryExp::isViewportDependent() const
169 {
170     return m_mediaFeature == widthMediaFeature
171         || m_mediaFeature == heightMediaFeature
172         || m_mediaFeature == minWidthMediaFeature
173         || m_mediaFeature == minHeightMediaFeature
174         || m_mediaFeature == maxWidthMediaFeature
175         || m_mediaFeature == maxHeightMediaFeature
176         || m_mediaFeature == orientationMediaFeature
177         || m_mediaFeature == aspectRatioMediaFeature
178         || m_mediaFeature == minAspectRatioMediaFeature
179         || m_mediaFeature == devicePixelRatioMediaFeature
180         || m_mediaFeature == resolutionMediaFeature
181         || m_mediaFeature == maxAspectRatioMediaFeature;
182 }
183
184 MediaQueryExp::MediaQueryExp(const MediaQueryExp& other)
185     : m_mediaFeature(other.mediaFeature())
186     , m_expValue(other.expValue())
187 {
188 }
189
190 MediaQueryExp::MediaQueryExp(const String& mediaFeature, const MediaQueryExpValue& expValue)
191     : m_mediaFeature(mediaFeature)
192     , m_expValue(expValue)
193 {
194 }
195
196 PassOwnPtrWillBeRawPtr<MediaQueryExp> MediaQueryExp::createIfValid(const String& mediaFeature, CSSParserValueList* valueList)
197 {
198     ASSERT(!mediaFeature.isNull());
199
200     MediaQueryExpValue expValue;
201     bool isValid = false;
202     String lowerMediaFeature = attemptStaticStringCreation(mediaFeature.lower());
203
204     // Create value for media query expression that must have 1 or more values.
205     if (valueList && valueList->size() > 0) {
206         if (valueList->size() == 1) {
207             CSSParserValue* value = valueList->current();
208             ASSERT(value);
209
210             if (featureWithCSSValueID(lowerMediaFeature, value) && featureWithValidIdent(lowerMediaFeature, value->id)) {
211                 // Media features that use CSSValueIDs.
212                 expValue.id = value->id;
213                 expValue.unit = CSSPrimitiveValue::CSS_VALUE_ID;
214                 expValue.isID = true;
215             } else if (featureWithValidDensity(lowerMediaFeature, value)
216                 || featureWithValidPositiveLength(lowerMediaFeature, value)) {
217                 // Media features that must have non-negative <density>, ie. dppx, dpi or dpcm,
218                 // or Media features that must have non-negative <length> or number value.
219                 expValue.value = value->fValue;
220                 expValue.unit = (CSSPrimitiveValue::UnitTypes)value->unit;
221                 expValue.isValue = true;
222                 expValue.isInteger = value->isInt;
223             } else if (featureWithPositiveInteger(lowerMediaFeature, value)
224                 || featureWithPositiveNumber(lowerMediaFeature, value)
225                 || featureWithZeroOrOne(lowerMediaFeature, value)) {
226                 // Media features that must have non-negative integer value,
227                 // or media features that must have non-negative number value,
228                 // or media features that must have (0|1) value.
229                 expValue.value = value->fValue;
230                 expValue.unit = CSSPrimitiveValue::CSS_NUMBER;
231                 expValue.isValue = true;
232                 expValue.isInteger = value->isInt;
233             }
234
235             isValid = (expValue.isID || expValue.isValue);
236
237         } else if (valueList->size() == 3 && featureWithAspectRatio(lowerMediaFeature)) {
238             // Create list of values.
239             // Currently accepts only <integer>/<integer>.
240             // Applicable to device-aspect-ratio and aspec-ratio.
241             isValid = true;
242             float numeratorValue = 0;
243             float denominatorValue = 0;
244             // The aspect-ratio must be <integer> (whitespace)? / (whitespace)? <integer>.
245             for (unsigned i = 0; i < 3; ++i, valueList->next()) {
246                 const CSSParserValue* value = valueList->current();
247                 if (i != 1 && value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue > 0 && value->isInt) {
248                     if (!i)
249                         numeratorValue = value->fValue;
250                     else
251                         denominatorValue = value->fValue;
252                 } else if (i == 1 && value->unit == CSSParserValue::Operator && value->iValue == '/') {
253                     continue;
254                 } else {
255                     isValid = false;
256                     break;
257                 }
258             }
259
260             if (isValid) {
261                 expValue.numerator = (unsigned)numeratorValue;
262                 expValue.denominator = (unsigned)denominatorValue;
263                 expValue.isRatio = true;
264             }
265         }
266     } else if (featureWithoutValue(lowerMediaFeature)) {
267         isValid = true;
268     }
269
270     if (!isValid)
271         return nullptr;
272
273     return adoptPtrWillBeNoop(new MediaQueryExp(lowerMediaFeature, expValue));
274 }
275
276 MediaQueryExp::~MediaQueryExp()
277 {
278 }
279
280 bool MediaQueryExp::operator==(const MediaQueryExp& other) const
281 {
282     return (other.m_mediaFeature == m_mediaFeature)
283         && ((!other.m_expValue.isValid() && !m_expValue.isValid())
284             || (other.m_expValue.isValid() && m_expValue.isValid() && other.m_expValue.equals(m_expValue)));
285 }
286
287 String MediaQueryExp::serialize() const
288 {
289     StringBuilder result;
290     result.append("(");
291     result.append(m_mediaFeature.lower());
292     if (m_expValue.isValid()) {
293         result.append(": ");
294         result.append(m_expValue.cssText());
295     }
296     result.append(")");
297
298     return result.toString();
299 }
300
301 static String printNumber(double number)
302 {
303     DecimalNumber decimal(number);
304     StringBuffer<LChar> buffer(decimal.bufferLengthForStringDecimal());
305     decimal.toStringDecimal(buffer.characters(), buffer.length());
306     return String::adopt(buffer);
307 }
308
309 String MediaQueryExpValue::cssText() const
310 {
311     StringBuilder output;
312     if (isValue) {
313         output.append(printNumber(value));
314         output.append(CSSPrimitiveValue::unitTypeToString(unit));
315     } else if (isRatio) {
316         output.append(printNumber(numerator));
317         output.append("/");
318         output.append(printNumber(denominator));
319     } else if (isID) {
320         output.append(getValueName(id));
321     }
322
323     return output.toString();
324 }
325
326 } // namespace