Home | History | Annotate | Download | only in css
      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