/* * CSS Media Query * * Copyright (C) 2006 Kimmo Kinnunen . * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sky/engine/core/css/MediaQueryExp.h" #include "gen/sky/platform/RuntimeEnabledFeatures.h" #include "sky/engine/core/css/CSSAspectRatioValue.h" #include "sky/engine/core/css/CSSPrimitiveValue.h" #include "sky/engine/core/css/parser/CSSParserValues.h" #include "sky/engine/core/html/parser/HTMLParserIdioms.h" #include "sky/engine/platform/Decimal.h" #include "sky/engine/wtf/text/StringBuffer.h" #include "sky/engine/wtf/text/StringBuilder.h" namespace blink { using namespace MediaFeatureNames; static inline bool featureWithCSSValueID(const String& mediaFeature, const CSSParserValue* value) { if (!value->id) return false; return mediaFeature == orientationMediaFeature || mediaFeature == pointerMediaFeature || mediaFeature == anyPointerMediaFeature || (mediaFeature == hoverMediaFeature && RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled()) || mediaFeature == anyHoverMediaFeature || mediaFeature == scanMediaFeature; } static inline bool featureWithValidIdent(const String& mediaFeature, CSSValueID ident) { if (mediaFeature == orientationMediaFeature) return ident == CSSValuePortrait || ident == CSSValueLandscape; if (mediaFeature == pointerMediaFeature || mediaFeature == anyPointerMediaFeature) return ident == CSSValueNone || ident == CSSValueCoarse || ident == CSSValueFine; if ((mediaFeature == hoverMediaFeature && RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled()) || mediaFeature == anyHoverMediaFeature) return ident == CSSValueNone || ident == CSSValueOnDemand || ident == CSSValueHover; if (mediaFeature == scanMediaFeature) return ident == CSSValueInterlace || ident == CSSValueProgressive; ASSERT_NOT_REACHED(); return false; } static bool positiveLengthUnit(const int unit) { switch (unit) { case CSSPrimitiveValue::CSS_EMS: case CSSPrimitiveValue::CSS_EXS: case CSSPrimitiveValue::CSS_PX: case CSSPrimitiveValue::CSS_CM: case CSSPrimitiveValue::CSS_MM: case CSSPrimitiveValue::CSS_IN: case CSSPrimitiveValue::CSS_PT: case CSSPrimitiveValue::CSS_PC: case CSSPrimitiveValue::CSS_CHS: return true; } return false; } static inline bool featureWithValidPositiveLength(const String& mediaFeature, const CSSParserValue* value) { if (!(positiveLengthUnit(value->unit) || (value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue == 0)) || value->fValue < 0) return false; return mediaFeature == heightMediaFeature || mediaFeature == maxHeightMediaFeature || mediaFeature == minHeightMediaFeature || mediaFeature == widthMediaFeature || mediaFeature == maxWidthMediaFeature || mediaFeature == minWidthMediaFeature || mediaFeature == deviceHeightMediaFeature || mediaFeature == maxDeviceHeightMediaFeature || mediaFeature == minDeviceHeightMediaFeature || mediaFeature == deviceWidthMediaFeature || mediaFeature == minDeviceWidthMediaFeature || mediaFeature == maxDeviceWidthMediaFeature; } static inline bool featureWithValidDensity(const String& mediaFeature, const CSSParserValue* value) { if ((value->unit != CSSPrimitiveValue::CSS_DPPX && value->unit != CSSPrimitiveValue::CSS_DPI && value->unit != CSSPrimitiveValue::CSS_DPCM) || value->fValue <= 0) return false; return mediaFeature == resolutionMediaFeature || mediaFeature == minResolutionMediaFeature || mediaFeature == maxResolutionMediaFeature; } static inline bool featureWithPositiveInteger(const String& mediaFeature, const CSSParserValue* value) { if (!value->isInt || value->fValue < 0) return false; return mediaFeature == colorMediaFeature || mediaFeature == maxColorMediaFeature || mediaFeature == minColorMediaFeature || mediaFeature == colorIndexMediaFeature || mediaFeature == maxColorIndexMediaFeature || mediaFeature == minColorIndexMediaFeature || mediaFeature == monochromeMediaFeature || mediaFeature == maxMonochromeMediaFeature || mediaFeature == minMonochromeMediaFeature; } static inline bool featureWithPositiveNumber(const String& mediaFeature, const CSSParserValue* value) { if (value->unit != CSSPrimitiveValue::CSS_NUMBER || value->fValue < 0) return false; return mediaFeature == transform3dMediaFeature || mediaFeature == devicePixelRatioMediaFeature || mediaFeature == maxDevicePixelRatioMediaFeature || mediaFeature == minDevicePixelRatioMediaFeature; } static inline bool featureWithZeroOrOne(const String& mediaFeature, const CSSParserValue* value) { if (!value->isInt || !(value->fValue == 1 || !value->fValue)) return false; return mediaFeature == hoverMediaFeature && !RuntimeEnabledFeatures::hoverMediaQueryKeywordsEnabled(); } static inline bool featureWithAspectRatio(const String& mediaFeature) { return mediaFeature == aspectRatioMediaFeature || mediaFeature == deviceAspectRatioMediaFeature || mediaFeature == minAspectRatioMediaFeature || mediaFeature == maxAspectRatioMediaFeature || mediaFeature == minDeviceAspectRatioMediaFeature || mediaFeature == maxDeviceAspectRatioMediaFeature; } static inline bool featureWithoutValue(const String& mediaFeature) { // Media features that are prefixed by min/max cannot be used without a value. return mediaFeature == monochromeMediaFeature || mediaFeature == colorMediaFeature || mediaFeature == colorIndexMediaFeature || mediaFeature == heightMediaFeature || mediaFeature == widthMediaFeature || mediaFeature == deviceHeightMediaFeature || mediaFeature == deviceWidthMediaFeature || mediaFeature == orientationMediaFeature || mediaFeature == aspectRatioMediaFeature || mediaFeature == deviceAspectRatioMediaFeature || mediaFeature == hoverMediaFeature || mediaFeature == anyHoverMediaFeature || mediaFeature == transform3dMediaFeature || mediaFeature == pointerMediaFeature || mediaFeature == anyPointerMediaFeature || mediaFeature == devicePixelRatioMediaFeature || mediaFeature == resolutionMediaFeature || mediaFeature == scanMediaFeature; } bool MediaQueryExp::isViewportDependent() const { return m_mediaFeature == widthMediaFeature || m_mediaFeature == heightMediaFeature || m_mediaFeature == minWidthMediaFeature || m_mediaFeature == minHeightMediaFeature || m_mediaFeature == maxWidthMediaFeature || m_mediaFeature == maxHeightMediaFeature || m_mediaFeature == orientationMediaFeature || m_mediaFeature == aspectRatioMediaFeature || m_mediaFeature == minAspectRatioMediaFeature || m_mediaFeature == devicePixelRatioMediaFeature || m_mediaFeature == resolutionMediaFeature || m_mediaFeature == maxAspectRatioMediaFeature; } MediaQueryExp::MediaQueryExp(const MediaQueryExp& other) : m_mediaFeature(other.mediaFeature()) , m_expValue(other.expValue()) { } MediaQueryExp::MediaQueryExp(const String& mediaFeature, const MediaQueryExpValue& expValue) : m_mediaFeature(mediaFeature) , m_expValue(expValue) { } PassOwnPtr MediaQueryExp::createIfValid(const String& mediaFeature, CSSParserValueList* valueList) { ASSERT(!mediaFeature.isNull()); MediaQueryExpValue expValue; bool isValid = false; String lowerMediaFeature = attemptStaticStringCreation(mediaFeature.lower()); // Create value for media query expression that must have 1 or more values. if (valueList && valueList->size() > 0) { if (valueList->size() == 1) { CSSParserValue* value = valueList->current(); ASSERT(value); if (featureWithCSSValueID(lowerMediaFeature, value) && featureWithValidIdent(lowerMediaFeature, value->id)) { // Media features that use CSSValueIDs. expValue.id = value->id; expValue.unit = CSSPrimitiveValue::CSS_VALUE_ID; expValue.isID = true; } else if (featureWithValidDensity(lowerMediaFeature, value) || featureWithValidPositiveLength(lowerMediaFeature, value)) { // Media features that must have non-negative , ie. dppx, dpi or dpcm, // or Media features that must have non-negative or number value. expValue.value = value->fValue; expValue.unit = (CSSPrimitiveValue::UnitType)value->unit; expValue.isValue = true; } else if (featureWithPositiveInteger(lowerMediaFeature, value) || featureWithPositiveNumber(lowerMediaFeature, value) || featureWithZeroOrOne(lowerMediaFeature, value)) { // Media features that must have non-negative integer value, // or media features that must have non-negative number value, // or media features that must have (0|1) value. expValue.value = value->fValue; expValue.unit = CSSPrimitiveValue::CSS_NUMBER; expValue.isValue = true; } isValid = (expValue.isID || expValue.isValue); } else if (valueList->size() == 3 && featureWithAspectRatio(lowerMediaFeature)) { // Create list of values. // Currently accepts only /. // Applicable to device-aspect-ratio and aspec-ratio. isValid = true; float numeratorValue = 0; float denominatorValue = 0; // The aspect-ratio must be (whitespace)? / (whitespace)? . for (unsigned i = 0; i < 3; ++i, valueList->next()) { const CSSParserValue* value = valueList->current(); if (i != 1 && value->unit == CSSPrimitiveValue::CSS_NUMBER && value->fValue > 0 && value->isInt) { if (!i) numeratorValue = value->fValue; else denominatorValue = value->fValue; } else if (i == 1 && value->unit == CSSParserValue::Operator && value->iValue == '/') { continue; } else { isValid = false; break; } } if (isValid) { expValue.numerator = (unsigned)numeratorValue; expValue.denominator = (unsigned)denominatorValue; expValue.isRatio = true; } } } else if (featureWithoutValue(lowerMediaFeature)) { isValid = true; } if (!isValid) return nullptr; return adoptPtr(new MediaQueryExp(lowerMediaFeature, expValue)); } MediaQueryExp::~MediaQueryExp() { } bool MediaQueryExp::operator==(const MediaQueryExp& other) const { return (other.m_mediaFeature == m_mediaFeature) && ((!other.m_expValue.isValid() && !m_expValue.isValid()) || (other.m_expValue.isValid() && m_expValue.isValid() && other.m_expValue.equals(m_expValue))); } String MediaQueryExp::serialize() const { StringBuilder result; result.append('('); result.append(m_mediaFeature.lower()); if (m_expValue.isValid()) { result.appendLiteral(": "); result.append(m_expValue.cssText()); } result.append(')'); return result.toString(); } static inline String printNumber(double number) { return Decimal::fromDouble(number).toString(); } String MediaQueryExpValue::cssText() const { StringBuilder output; if (isValue) { output.append(printNumber(value)); output.append(CSSPrimitiveValue::unitTypeToString(unit)); } else if (isRatio) { output.append(printNumber(numerator)); output.append('/'); output.append(printNumber(denominator)); } else if (isID) { output.append(getValueName(id)); } return output.toString(); } } // namespace