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.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
31 #include "core/css/MediaQueryExp.h"
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"
44 using namespace MediaFeatureNames;
46 static inline bool featureWithCSSValueID(const String& mediaFeature, const CSSParserValue* value)
51 return mediaFeature == orientationMediaFeature
52 || mediaFeature == pointerMediaFeature
53 || mediaFeature == anyPointerMediaFeature
54 || (mediaFeature == hoverMediaFeature && RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled())
55 || mediaFeature == anyHoverMediaFeature
56 || mediaFeature == scanMediaFeature;
59 static inline bool featureWithValidIdent(const String& mediaFeature, CSSValueID ident)
61 if (mediaFeature == orientationMediaFeature)
62 return ident == CSSValuePortrait || ident == CSSValueLandscape;
64 if (mediaFeature == pointerMediaFeature || mediaFeature == anyPointerMediaFeature)
65 return ident == CSSValueNone || ident == CSSValueCoarse || ident == CSSValueFine;
67 if ((mediaFeature == hoverMediaFeature && RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled())
68 || mediaFeature == anyHoverMediaFeature)
69 return ident == CSSValueNone || ident == CSSValueOnDemand || ident == CSSValueHover;
71 if (mediaFeature == scanMediaFeature)
72 return ident == CSSValueInterlace || ident == CSSValueProgressive;
78 static bool positiveLengthUnit(const int 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:
96 static inline bool featureWithValidPositiveLength(const String& mediaFeature, const CSSParserValue* value)
98 if (!(positiveLengthUnit(value->unit) || (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue == 0)) || value->fValue < 0)
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;
116 static inline bool featureWithValidDensity(const String& mediaFeature, const CSSParserValue* value)
118 if ((value->unit != CSSPrimitiveValue::CSS_DPPX && value->unit != CSSPrimitiveValue::CSS_DPI && value->unit != CSSPrimitiveValue::CSS_DPCM) || value->fValue <= 0)
121 return mediaFeature == resolutionMediaFeature
122 || mediaFeature == minResolutionMediaFeature
123 || mediaFeature == maxResolutionMediaFeature;
126 static inline bool featureWithPositiveInteger(const String& mediaFeature, const CSSParserValue* value)
128 if (!value->isInt || value->fValue < 0)
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;
142 static inline bool featureWithPositiveNumber(const String& mediaFeature, const CSSParserValue* value)
144 if (value->unit != CSSPrimitiveValue::CSS_NUMBER || value->fValue < 0)
147 return mediaFeature == transform3dMediaFeature
148 || mediaFeature == devicePixelRatioMediaFeature
149 || mediaFeature == maxDevicePixelRatioMediaFeature
150 || mediaFeature == minDevicePixelRatioMediaFeature;
153 static inline bool featureWithZeroOrOne(const String& mediaFeature, const CSSParserValue* value)
155 if (!value->isInt || !(value->fValue == 1 || !value->fValue))
158 return mediaFeature == gridMediaFeature
159 || (mediaFeature == hoverMediaFeature && !RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled());
162 static inline bool featureWithAspectRatio(const String& mediaFeature)
164 return mediaFeature == aspectRatioMediaFeature
165 || mediaFeature == deviceAspectRatioMediaFeature
166 || mediaFeature == minAspectRatioMediaFeature
167 || mediaFeature == maxAspectRatioMediaFeature
168 || mediaFeature == minDeviceAspectRatioMediaFeature
169 || mediaFeature == maxDeviceAspectRatioMediaFeature;
172 static inline bool featureWithoutValue(const String& mediaFeature)
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;
196 bool MediaQueryExp::isViewportDependent() const
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;
212 MediaQueryExp::MediaQueryExp(const MediaQueryExp& other)
213 : m_mediaFeature(other.mediaFeature())
214 , m_expValue(other.expValue())
218 MediaQueryExp::MediaQueryExp(const String& mediaFeature, const MediaQueryExpValue& expValue)
219 : m_mediaFeature(mediaFeature)
220 , m_expValue(expValue)
224 PassOwnPtrWillBeRawPtr<MediaQueryExp> MediaQueryExp::createIfValid(const String& mediaFeature, CSSParserValueList* valueList)
226 ASSERT(!mediaFeature.isNull());
228 MediaQueryExpValue expValue;
229 bool isValid = false;
230 String lowerMediaFeature = attemptStaticStringCreation(mediaFeature.lower());
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();
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;
261 isValid = (expValue.isID || expValue.isValue);
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.
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) {
275 numeratorValue = value->fValue;
277 denominatorValue = value->fValue;
278 } else if (i == 1 && value->unit == CSSParserValue::Operator && value->iValue == '/') {
287 expValue.numerator = (unsigned)numeratorValue;
288 expValue.denominator = (unsigned)denominatorValue;
289 expValue.isRatio = true;
292 } else if (featureWithoutValue(lowerMediaFeature)) {
299 return adoptPtrWillBeNoop(new MediaQueryExp(lowerMediaFeature, expValue));
302 MediaQueryExp::~MediaQueryExp()
306 bool MediaQueryExp::operator==(const MediaQueryExp& other) const
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)));
313 String MediaQueryExp::serialize() const
315 StringBuilder result;
317 result.append(m_mediaFeature.lower());
318 if (m_expValue.isValid()) {
319 result.appendLiteral(": ");
320 result.append(m_expValue.cssText());
324 return result.toString();
327 static inline String printNumber(double number)
329 return Decimal::fromDouble(number).toString();
332 String MediaQueryExpValue::cssText() const
334 StringBuilder output;
336 output.append(printNumber(value));
337 output.append(CSSPrimitiveValue::unitTypeToString(unit));
338 } else if (isRatio) {
339 output.append(printNumber(numerator));
341 output.append(printNumber(denominator));
343 output.append(getValueName(id));
346 return output.toString();