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 "MediaQueryExp.h" 32 33#include "CSSAspectRatioValue.h" 34#include "CSSParser.h" 35#include "CSSPrimitiveValue.h" 36#include "CSSValueList.h" 37#include <wtf/text/StringBuilder.h> 38 39namespace WebCore { 40 41static inline bool featureWithCSSValueID(const AtomicString& mediaFeature, const CSSParserValue* value) 42{ 43 if (!value->id) 44 return false; 45 46 return mediaFeature == MediaFeatureNames::orientationMediaFeature 47#if ENABLE(VIEW_MODE_CSS_MEDIA) 48 || mediaFeature == MediaFeatureNames::view_modeMediaFeature 49#endif // ENABLE(VIEW_MODE_CSS_MEDIA) 50 || mediaFeature == MediaFeatureNames::pointerMediaFeature; 51} 52 53static inline bool featureWithValidPositiveLenghtOrNumber(const AtomicString& mediaFeature, const CSSParserValue* value) 54{ 55 if (!(((value->unit >= CSSPrimitiveValue::CSS_EMS && value->unit <= CSSPrimitiveValue::CSS_PC) || value->unit == CSSPrimitiveValue::CSS_REMS) || value->unit == CSSPrimitiveValue::CSS_NUMBER) || value->fValue < 0) 56 return false; 57 58 return mediaFeature == MediaFeatureNames::heightMediaFeature 59 || mediaFeature == MediaFeatureNames::max_heightMediaFeature 60 || mediaFeature == MediaFeatureNames::min_heightMediaFeature 61 || mediaFeature == MediaFeatureNames::widthMediaFeature 62 || mediaFeature == MediaFeatureNames::max_widthMediaFeature 63 || mediaFeature == MediaFeatureNames::min_widthMediaFeature 64 || mediaFeature == MediaFeatureNames::device_heightMediaFeature 65 || mediaFeature == MediaFeatureNames::max_device_heightMediaFeature 66 || mediaFeature == MediaFeatureNames::min_device_heightMediaFeature 67 || mediaFeature == MediaFeatureNames::device_widthMediaFeature 68 || mediaFeature == MediaFeatureNames::max_device_widthMediaFeature 69 || mediaFeature == MediaFeatureNames::min_device_widthMediaFeature; 70} 71 72static inline bool featureWithValidDensity(const AtomicString& mediaFeature, const CSSParserValue* value) 73{ 74 if ((value->unit != CSSPrimitiveValue::CSS_DPPX && value->unit != CSSPrimitiveValue::CSS_DPI && value->unit != CSSPrimitiveValue::CSS_DPCM) || value->fValue <= 0) 75 return false; 76 77 return mediaFeature == MediaFeatureNames::resolutionMediaFeature 78 || mediaFeature == MediaFeatureNames::max_resolutionMediaFeature 79 || mediaFeature == MediaFeatureNames::min_resolutionMediaFeature; 80} 81 82static inline bool featureWithPositiveInteger(const AtomicString& mediaFeature, const CSSParserValue* value) 83{ 84 if (!value->isInt || value->fValue < 0) 85 return false; 86 87 return mediaFeature == MediaFeatureNames::colorMediaFeature 88 || mediaFeature == MediaFeatureNames::max_colorMediaFeature 89 || mediaFeature == MediaFeatureNames::min_colorMediaFeature 90 || mediaFeature == MediaFeatureNames::color_indexMediaFeature 91 || mediaFeature == MediaFeatureNames::max_color_indexMediaFeature 92 || mediaFeature == MediaFeatureNames::min_color_indexMediaFeature 93 || mediaFeature == MediaFeatureNames::min_monochromeMediaFeature 94 || mediaFeature == MediaFeatureNames::max_monochromeMediaFeature; 95} 96 97static inline bool featureWithPositiveNumber(const AtomicString& mediaFeature, const CSSParserValue* value) 98{ 99 if (value->unit != CSSPrimitiveValue::CSS_NUMBER || value->fValue < 0) 100 return false; 101 102 return mediaFeature == MediaFeatureNames::transform_2dMediaFeature 103 || mediaFeature == MediaFeatureNames::transform_3dMediaFeature 104 || mediaFeature == MediaFeatureNames::transitionMediaFeature 105 || mediaFeature == MediaFeatureNames::animationMediaFeature 106 || mediaFeature == MediaFeatureNames::device_pixel_ratioMediaFeature 107 || mediaFeature == MediaFeatureNames::max_device_pixel_ratioMediaFeature 108 || mediaFeature == MediaFeatureNames::min_device_pixel_ratioMediaFeature; 109} 110 111static inline bool featureWithZeroOrOne(const AtomicString& mediaFeature, const CSSParserValue* value) 112{ 113 if (!value->isInt || !(value->fValue == 1 || !value->fValue)) 114 return false; 115 116 return mediaFeature == MediaFeatureNames::gridMediaFeature 117 || mediaFeature == MediaFeatureNames::hoverMediaFeature; 118} 119 120static inline bool featureWithAspectRatio(const AtomicString& mediaFeature) 121{ 122 return mediaFeature == MediaFeatureNames::aspect_ratioMediaFeature 123 || mediaFeature == MediaFeatureNames::device_aspect_ratioMediaFeature 124 || mediaFeature == MediaFeatureNames::min_aspect_ratioMediaFeature 125 || mediaFeature == MediaFeatureNames::max_aspect_ratioMediaFeature 126 || mediaFeature == MediaFeatureNames::min_device_aspect_ratioMediaFeature 127 || mediaFeature == MediaFeatureNames::max_device_aspect_ratioMediaFeature; 128} 129 130static inline bool featureWithoutValue(const AtomicString& mediaFeature) 131{ 132 // Media features that are prefixed by min/max cannot be used without a value. 133 return mediaFeature == MediaFeatureNames::monochromeMediaFeature 134 || mediaFeature == MediaFeatureNames::colorMediaFeature 135 || mediaFeature == MediaFeatureNames::color_indexMediaFeature 136 || mediaFeature == MediaFeatureNames::gridMediaFeature 137 || mediaFeature == MediaFeatureNames::heightMediaFeature 138 || mediaFeature == MediaFeatureNames::widthMediaFeature 139 || mediaFeature == MediaFeatureNames::device_heightMediaFeature 140 || mediaFeature == MediaFeatureNames::device_widthMediaFeature 141 || mediaFeature == MediaFeatureNames::orientationMediaFeature 142 || mediaFeature == MediaFeatureNames::aspect_ratioMediaFeature 143 || mediaFeature == MediaFeatureNames::device_aspect_ratioMediaFeature 144 || mediaFeature == MediaFeatureNames::hoverMediaFeature 145 || mediaFeature == MediaFeatureNames::transform_2dMediaFeature 146 || mediaFeature == MediaFeatureNames::transform_3dMediaFeature 147 || mediaFeature == MediaFeatureNames::transitionMediaFeature 148 || mediaFeature == MediaFeatureNames::animationMediaFeature 149#if ENABLE(VIEW_MODE_CSS_MEDIA) 150 || mediaFeature == MediaFeatureNames::view_modeMediaFeature 151#endif // ENABLE(VIEW_MODE_CSS_MEDIA) 152 || mediaFeature == MediaFeatureNames::pointerMediaFeature 153 || mediaFeature == MediaFeatureNames::device_pixel_ratioMediaFeature 154 || mediaFeature == MediaFeatureNames::resolutionMediaFeature; 155} 156 157inline MediaQueryExp::MediaQueryExp(const AtomicString& mediaFeature, CSSParserValueList* valueList) 158 : m_mediaFeature(mediaFeature) 159 , m_value(0) 160 , m_isValid(false) 161{ 162 // Initialize media query expression that must have 1 or more values. 163 if (valueList) { 164 if (valueList->size() == 1) { 165 CSSParserValue* value = valueList->current(); 166 167 // Media features that use CSSValueIDs. 168 if (featureWithCSSValueID(mediaFeature, value)) 169 m_value = CSSPrimitiveValue::createIdentifier(value->id); 170 171 // Media features that must have non-negative <density>, ie. dppx, dpi or dpcm. 172 else if (featureWithValidDensity(mediaFeature, value)) 173 m_value = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 174 175 // Media features that must have non-negative <lenght> or number value. 176 else if (featureWithValidPositiveLenghtOrNumber(mediaFeature, value)) 177 m_value = CSSPrimitiveValue::create(value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit); 178 179 // Media features that must have non-negative integer value. 180 else if (featureWithPositiveInteger(mediaFeature, value)) 181 m_value = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_NUMBER); 182 183 // Media features that must have non-negative number value. 184 else if (featureWithPositiveNumber(mediaFeature, value)) 185 m_value = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_NUMBER); 186 187 // Media features that must have (0|1) value. 188 else if (featureWithZeroOrOne(mediaFeature, value)) 189 m_value = CSSPrimitiveValue::create(value->fValue, CSSPrimitiveValue::CSS_NUMBER); 190 191 m_isValid = m_value; 192 } else if (valueList->size() == 3 && featureWithAspectRatio(mediaFeature)) { 193 // Create list of values. 194 // Currently accepts only <integer>/<integer>. 195 // Applicable to device-aspect-ratio and aspec-ratio. 196 bool isValid = true; 197 float numeratorValue = 0; 198 float denominatorValue = 0; 199 200 // The aspect-ratio must be <integer> (whitespace)? / (whitespace)? <integer>. 201 for (unsigned i = 0; i < 3; ++i, valueList->next()) { 202 const CSSParserValue* value = valueList->current(); 203 if (i != 1 && value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue > 0 && value->isInt) { 204 if (!i) 205 numeratorValue = value->fValue; 206 else 207 denominatorValue = value->fValue; 208 } else if (i == 1 && value->unit == CSSParserValue::Operator && value->iValue == '/') 209 continue; 210 else { 211 isValid = false; 212 break; 213 } 214 } 215 216 if (isValid) 217 m_value = CSSAspectRatioValue::create(numeratorValue, denominatorValue); 218 219 m_isValid = m_value; 220 } 221 } else if (featureWithoutValue(mediaFeature)) 222 m_isValid = true; 223} 224 225PassOwnPtr<MediaQueryExp> MediaQueryExp::create(const AtomicString& mediaFeature, CSSParserValueList* values) 226{ 227 return adoptPtr(new MediaQueryExp(mediaFeature, values)); 228} 229 230MediaQueryExp::~MediaQueryExp() 231{ 232} 233 234String MediaQueryExp::serialize() const 235{ 236 if (!m_serializationCache.isNull()) 237 return m_serializationCache; 238 239 StringBuilder result; 240 result.append('('); 241 result.append(m_mediaFeature.lower()); 242 if (m_value) { 243 result.appendLiteral(": "); 244 result.append(m_value->cssText()); 245 } 246 result.append(')'); 247 248 const_cast<MediaQueryExp*>(this)->m_serializationCache = result.toString(); 249 return m_serializationCache; 250} 251 252} // namespace 253