1 /* 2 * CSS Media Query 3 * 4 * Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen (at) 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 "CSSValueKeywords.h" 34 #include "core/css/CSSAspectRatioValue.h" 35 #include "core/css/CSSParser.h" 36 #include "core/css/CSSPrimitiveValue.h" 37 #include "wtf/text/StringBuilder.h" 38 39 namespace WebCore { 40 41 static inline bool featureWithCSSValueID(const AtomicString& mediaFeature, const CSSParserValue* value) 42 { 43 if (!value->id) 44 return false; 45 46 return mediaFeature == MediaFeatureNames::orientationMediaFeature 47 || mediaFeature == MediaFeatureNames::viewModeMediaFeature 48 || mediaFeature == MediaFeatureNames::pointerMediaFeature 49 || mediaFeature == MediaFeatureNames::scanMediaFeature; 50 } 51 52 static inline bool featureWithValidIdent(const AtomicString& mediaFeature, CSSValueID ident) 53 { 54 if (mediaFeature == MediaFeatureNames::orientationMediaFeature) 55 return ident == CSSValuePortrait || ident == CSSValueLandscape; 56 57 if (mediaFeature == MediaFeatureNames::viewModeMediaFeature) { 58 switch (ident) { 59 case CSSValueWindowed: 60 case CSSValueFloating: 61 case CSSValueFullscreen: 62 case CSSValueMaximized: 63 case CSSValueMinimized: 64 return true; 65 default: 66 return false; 67 } 68 } 69 70 if (mediaFeature == MediaFeatureNames::pointerMediaFeature) 71 return ident == CSSValueNone || ident == CSSValueCoarse || ident == CSSValueFine; 72 73 if (mediaFeature == MediaFeatureNames::scanMediaFeature) 74 return ident == CSSValueInterlace || ident == CSSValueProgressive; 75 76 ASSERT_NOT_REACHED(); 77 return false; 78 } 79 80 static inline bool featureWithValidPositiveLenghtOrNumber(const AtomicString& mediaFeature, const CSSParserValue* value) 81 { 82 if (!(((value->unit >= CSSPrimitiveValue::CSS_EMS && value->unit <= CSSPrimitiveValue::CSS_PC) || value->unit == CSSPrimitiveValue::CSS_REMS) || value->unit == CSSPrimitiveValue::CSS_NUMBER) || value->fValue < 0) 83 return false; 84 85 return mediaFeature == MediaFeatureNames::heightMediaFeature 86 || mediaFeature == MediaFeatureNames::maxHeightMediaFeature 87 || mediaFeature == MediaFeatureNames::minHeightMediaFeature 88 || mediaFeature == MediaFeatureNames::widthMediaFeature 89 || mediaFeature == MediaFeatureNames::maxWidthMediaFeature 90 || mediaFeature == MediaFeatureNames::minWidthMediaFeature 91 || mediaFeature == MediaFeatureNames::deviceHeightMediaFeature 92 || mediaFeature == MediaFeatureNames::maxDeviceHeightMediaFeature 93 || mediaFeature == MediaFeatureNames::minDeviceHeightMediaFeature 94 || mediaFeature == MediaFeatureNames::deviceWidthMediaFeature 95 || mediaFeature == MediaFeatureNames::maxDeviceWidthMediaFeature 96 || mediaFeature == MediaFeatureNames::minDeviceWidthMediaFeature; 97 } 98 99 static inline bool featureWithValidDensity(const AtomicString& mediaFeature, const CSSParserValue* value) 100 { 101 if ((value->unit != CSSPrimitiveValue::CSS_DPPX && value->unit != CSSPrimitiveValue::CSS_DPI && value->unit != CSSPrimitiveValue::CSS_DPCM) || value->fValue <= 0) 102 return false; 103 104 return mediaFeature == MediaFeatureNames::resolutionMediaFeature 105 || mediaFeature == MediaFeatureNames::maxResolutionMediaFeature 106 || mediaFeature == MediaFeatureNames::minResolutionMediaFeature; 107 } 108 109 static inline bool featureWithPositiveInteger(const AtomicString& mediaFeature, const CSSParserValue* value) 110 { 111 if (!value->isInt || value->fValue < 0) 112 return false; 113 114 return mediaFeature == MediaFeatureNames::colorMediaFeature 115 || mediaFeature == MediaFeatureNames::maxColorMediaFeature 116 || mediaFeature == MediaFeatureNames::minColorMediaFeature 117 || mediaFeature == MediaFeatureNames::colorIndexMediaFeature 118 || mediaFeature == MediaFeatureNames::maxColorIndexMediaFeature 119 || mediaFeature == MediaFeatureNames::minColorIndexMediaFeature 120 || mediaFeature == MediaFeatureNames::monochromeMediaFeature 121 || mediaFeature == MediaFeatureNames::minMonochromeMediaFeature 122 || mediaFeature == MediaFeatureNames::maxMonochromeMediaFeature; 123 } 124 125 static inline bool featureWithPositiveNumber(const AtomicString& mediaFeature, const CSSParserValue* value) 126 { 127 if (value->unit != CSSPrimitiveValue::CSS_NUMBER || value->fValue < 0) 128 return false; 129 130 return mediaFeature == MediaFeatureNames::transform2dMediaFeature 131 || mediaFeature == MediaFeatureNames::transform3dMediaFeature 132 || mediaFeature == MediaFeatureNames::deprecatedTransitionMediaFeature 133 || mediaFeature == MediaFeatureNames::animationMediaFeature 134 || mediaFeature == MediaFeatureNames::devicePixelRatioMediaFeature 135 || mediaFeature == MediaFeatureNames::maxDevicePixelRatioMediaFeature 136 || mediaFeature == MediaFeatureNames::minDevicePixelRatioMediaFeature; 137 } 138 139 static inline bool featureWithZeroOrOne(const AtomicString& mediaFeature, const CSSParserValue* value) 140 { 141 if (!value->isInt || !(value->fValue == 1 || !value->fValue)) 142 return false; 143 144 return mediaFeature == MediaFeatureNames::gridMediaFeature 145 || mediaFeature == MediaFeatureNames::hoverMediaFeature; 146 } 147 148 static inline bool featureWithAspectRatio(const AtomicString& mediaFeature) 149 { 150 return mediaFeature == MediaFeatureNames::aspectRatioMediaFeature 151 || mediaFeature == MediaFeatureNames::deviceAspectRatioMediaFeature 152 || mediaFeature == MediaFeatureNames::minAspectRatioMediaFeature 153 || mediaFeature == MediaFeatureNames::maxAspectRatioMediaFeature 154 || mediaFeature == MediaFeatureNames::minDeviceAspectRatioMediaFeature 155 || mediaFeature == MediaFeatureNames::maxDeviceAspectRatioMediaFeature; 156 } 157 158 static inline bool featureWithoutValue(const AtomicString& mediaFeature) 159 { 160 // Media features that are prefixed by min/max cannot be used without a value. 161 return mediaFeature == MediaFeatureNames::monochromeMediaFeature 162 || mediaFeature == MediaFeatureNames::colorMediaFeature 163 || mediaFeature == MediaFeatureNames::colorIndexMediaFeature 164 || mediaFeature == MediaFeatureNames::gridMediaFeature 165 || mediaFeature == MediaFeatureNames::heightMediaFeature 166 || mediaFeature == MediaFeatureNames::widthMediaFeature 167 || mediaFeature == MediaFeatureNames::deviceHeightMediaFeature 168 || mediaFeature == MediaFeatureNames::deviceWidthMediaFeature 169 || mediaFeature == MediaFeatureNames::orientationMediaFeature 170 || mediaFeature == MediaFeatureNames::aspectRatioMediaFeature 171 || mediaFeature == MediaFeatureNames::deviceAspectRatioMediaFeature 172 || mediaFeature == MediaFeatureNames::hoverMediaFeature 173 || mediaFeature == MediaFeatureNames::transform2dMediaFeature 174 || mediaFeature == MediaFeatureNames::transform3dMediaFeature 175 || mediaFeature == MediaFeatureNames::deprecatedTransitionMediaFeature 176 || mediaFeature == MediaFeatureNames::animationMediaFeature 177 || mediaFeature == MediaFeatureNames::viewModeMediaFeature 178 || mediaFeature == MediaFeatureNames::pointerMediaFeature 179 || mediaFeature == MediaFeatureNames::devicePixelRatioMediaFeature 180 || mediaFeature == MediaFeatureNames::resolutionMediaFeature 181 || mediaFeature == MediaFeatureNames::scanMediaFeature; 182 } 183 184 bool MediaQueryExp::isViewportDependent() const 185 { 186 return m_mediaFeature == MediaFeatureNames::widthMediaFeature 187 || m_mediaFeature == MediaFeatureNames::heightMediaFeature 188 || m_mediaFeature == MediaFeatureNames::minWidthMediaFeature 189 || m_mediaFeature == MediaFeatureNames::minHeightMediaFeature 190 || m_mediaFeature == MediaFeatureNames::maxWidthMediaFeature 191 || m_mediaFeature == MediaFeatureNames::maxHeightMediaFeature 192 || m_mediaFeature == MediaFeatureNames::orientationMediaFeature 193 || m_mediaFeature == MediaFeatureNames::aspectRatioMediaFeature 194 || m_mediaFeature == MediaFeatureNames::minAspectRatioMediaFeature 195 || m_mediaFeature == MediaFeatureNames::devicePixelRatioMediaFeature 196 || m_mediaFeature == MediaFeatureNames::resolutionMediaFeature 197 || m_mediaFeature == MediaFeatureNames::maxAspectRatioMediaFeature; 198 } 199 200 MediaQueryExp::MediaQueryExp(const AtomicString& mediaFeature, PassRefPtr<CSSValue> value) 201 : m_mediaFeature(mediaFeature) 202 , m_value(value) 203 { 204 } 205 206 PassOwnPtr<MediaQueryExp> MediaQueryExp::create(const AtomicString& mediaFeature, CSSParserValueList* valueList) 207 { 208 RefPtr<CSSValue> cssValue; 209 bool isValid = false; 210 211 // Create value for media query expression that must have 1 or more values. 212 if (valueList) { 213 if (valueList->size() == 1) { 214 CSSParserValue* value = valueList->current(); 215 216 if (featureWithCSSValueID(mediaFeature, value)) { 217 // Media features that use CSSValueIDs. 218 cssValue = CSSPrimitiveValue::createIdentifier(value->id); 219 if (!featureWithValidIdent(mediaFeature, toCSSPrimitiveValue(cssValue.get())->getValueID())) 220 cssValue.clear(); 221 } else if (featureWithValidDensity(mediaFeature, value)) { 222 // Media features that must have non-negative <density>, ie. dppx, dpi or dpcm. 223 cssValue = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 224 } else if (featureWithValidPositiveLenghtOrNumber(mediaFeature, value)) { 225 // Media features that must have non-negative <lenght> or number value. 226 cssValue = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 227 } else if (featureWithPositiveInteger(mediaFeature, value)) { 228 // Media features that must have non-negative integer value. 229 cssValue = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_NUMBER); 230 } else if (featureWithPositiveNumber(mediaFeature, value)) { 231 // Media features that must have non-negative number value. 232 cssValue = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_NUMBER); 233 } else if (featureWithZeroOrOne(mediaFeature, value)) { 234 // Media features that must have (0|1) value. 235 cssValue = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_NUMBER); 236 } 237 238 isValid = cssValue; 239 240 } else if (valueList->size() == 3 && featureWithAspectRatio(mediaFeature)) { 241 // Create list of values. 242 // Currently accepts only <integer>/<integer>. 243 // Applicable to device-aspect-ratio and aspec-ratio. 244 isValid = true; 245 float numeratorValue = 0; 246 float denominatorValue = 0; 247 // The aspect-ratio must be <integer> (whitespace)? / (whitespace)? <integer>. 248 for (unsigned i = 0; i < 3; ++i, valueList->next()) { 249 const CSSParserValue* value = valueList->current(); 250 if (i != 1 && value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue > 0 && value->isInt) { 251 if (!i) 252 numeratorValue = value->fValue; 253 else 254 denominatorValue = value->fValue; 255 } else if (i == 1 && value->unit == CSSParserValue::Operator && value->iValue == '/') { 256 continue; 257 } else { 258 isValid = false; 259 break; 260 } 261 } 262 263 if (isValid) 264 cssValue = CSSAspectRatioValue::create(numeratorValue, denominatorValue); 265 } 266 } else if (featureWithoutValue(mediaFeature)) { 267 isValid = true; 268 } 269 270 if (!isValid) 271 return nullptr; 272 273 return adoptPtr(new MediaQueryExp(mediaFeature, cssValue)); 274 } 275 276 MediaQueryExp::~MediaQueryExp() 277 { 278 } 279 280 String MediaQueryExp::serialize() const 281 { 282 StringBuilder result; 283 result.append("("); 284 result.append(m_mediaFeature.lower()); 285 if (m_value) { 286 result.append(": "); 287 result.append(m_value->cssText()); 288 } 289 result.append(")"); 290 291 return result.toString(); 292 } 293 294 } // namespace 295