/* * Copyright (C) 2006 Kimmo Kinnunen . * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2013, 2015 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 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 "config.h" #include "MediaQueryExpression.h" #include "CSSAspectRatioValue.h" #include "CSSParser.h" #include "CSSParserIdioms.h" #include "CSSParserToken.h" #include "CSSPrimitiveValue.h" #include "CSSValueList.h" #include "MediaFeatureNames.h" #include namespace WebCore { static inline bool featureWithValidIdent(const AtomicString& mediaFeature) { return mediaFeature == MediaFeatureNames::orientation #if ENABLE(VIEW_MODE_CSS_MEDIA) || mediaFeature == MediaFeatureNames::viewMode #endif || mediaFeature == MediaFeatureNames::colorGamut || mediaFeature == MediaFeatureNames::anyHover || mediaFeature == MediaFeatureNames::anyPointer || mediaFeature == MediaFeatureNames::hover || mediaFeature == MediaFeatureNames::invertedColors || mediaFeature == MediaFeatureNames::pointer || mediaFeature == MediaFeatureNames::prefersReducedMotion; } static inline bool featureWithValidDensity(const String& mediaFeature, const CSSParserToken& token) { if (!CSSPrimitiveValue::isResolution(static_cast(token.unitType())) || token.numericValue() <= 0) return false; return mediaFeature == MediaFeatureNames::resolution || mediaFeature == MediaFeatureNames::minResolution || mediaFeature == MediaFeatureNames::maxResolution; } static inline bool featureWithValidPositiveLength(const String& mediaFeature, const CSSParserToken& token) { if (!(CSSPrimitiveValue::isLength(token.unitType()) || (token.type() == NumberToken && !token.numericValue())) || token.numericValue() < 0) return false; return mediaFeature == MediaFeatureNames::height || mediaFeature == MediaFeatureNames::maxHeight || mediaFeature == MediaFeatureNames::minHeight || mediaFeature == MediaFeatureNames::width || mediaFeature == MediaFeatureNames::maxWidth || mediaFeature == MediaFeatureNames::minWidth || mediaFeature == MediaFeatureNames::deviceHeight || mediaFeature == MediaFeatureNames::maxDeviceHeight || mediaFeature == MediaFeatureNames::minDeviceHeight || mediaFeature == MediaFeatureNames::deviceWidth || mediaFeature == MediaFeatureNames::minDeviceWidth || mediaFeature == MediaFeatureNames::maxDeviceWidth; } static inline bool featureWithPositiveInteger(const String& mediaFeature, const CSSParserToken& token) { if (token.numericValueType() != IntegerValueType || token.numericValue() < 0) return false; return mediaFeature == MediaFeatureNames::color || mediaFeature == MediaFeatureNames:: maxColor || mediaFeature == MediaFeatureNames:: minColor || mediaFeature == MediaFeatureNames::colorIndex || mediaFeature == MediaFeatureNames::maxColorIndex || mediaFeature == MediaFeatureNames::minColorIndex || mediaFeature == MediaFeatureNames::monochrome || mediaFeature == MediaFeatureNames::maxMonochrome || mediaFeature == MediaFeatureNames::minMonochrome; } static inline bool featureWithPositiveNumber(const String& mediaFeature, const CSSParserToken& token) { if (token.type() != NumberToken || token.numericValue() < 0) return false; return mediaFeature == MediaFeatureNames::transform3d || mediaFeature == MediaFeatureNames::devicePixelRatio || mediaFeature == MediaFeatureNames::maxDevicePixelRatio || mediaFeature == MediaFeatureNames::minDevicePixelRatio || mediaFeature == MediaFeatureNames::transition || mediaFeature == MediaFeatureNames::animation || mediaFeature == MediaFeatureNames::transform2d; } static inline bool featureWithZeroOrOne(const String& mediaFeature, const CSSParserToken& token) { if (token.numericValueType() != IntegerValueType || !(token.numericValue() == 1 || !token.numericValue())) return false; return mediaFeature == MediaFeatureNames::grid; } static inline bool isAspectRatioFeature(const AtomicString& mediaFeature) { return mediaFeature == MediaFeatureNames::aspectRatio || mediaFeature == MediaFeatureNames::deviceAspectRatio || mediaFeature == MediaFeatureNames::minAspectRatio || mediaFeature == MediaFeatureNames::maxAspectRatio || mediaFeature == MediaFeatureNames::minDeviceAspectRatio || mediaFeature == MediaFeatureNames::maxDeviceAspectRatio; } static inline bool isFeatureValidWithoutValue(const AtomicString& mediaFeature) { // Media features that are prefixed by min/max cannot be used without a value. return mediaFeature == MediaFeatureNames::anyHover || mediaFeature == MediaFeatureNames::anyPointer || mediaFeature == MediaFeatureNames::monochrome || mediaFeature == MediaFeatureNames::color || mediaFeature == MediaFeatureNames::colorIndex || mediaFeature == MediaFeatureNames::grid || mediaFeature == MediaFeatureNames::height || mediaFeature == MediaFeatureNames::width || mediaFeature == MediaFeatureNames::deviceHeight || mediaFeature == MediaFeatureNames::deviceWidth || mediaFeature == MediaFeatureNames::orientation || mediaFeature == MediaFeatureNames::aspectRatio || mediaFeature == MediaFeatureNames::deviceAspectRatio || mediaFeature == MediaFeatureNames::hover || mediaFeature == MediaFeatureNames::transform2d || mediaFeature == MediaFeatureNames::transform3d || mediaFeature == MediaFeatureNames::transition || mediaFeature == MediaFeatureNames::animation || mediaFeature == MediaFeatureNames::invertedColors #if ENABLE(VIEW_MODE_CSS_MEDIA) || mediaFeature == MediaFeatureNames::viewMode #endif || mediaFeature == MediaFeatureNames::pointer || mediaFeature == MediaFeatureNames::prefersReducedMotion || mediaFeature == MediaFeatureNames::devicePixelRatio || mediaFeature == MediaFeatureNames::resolution || mediaFeature == MediaFeatureNames::videoPlayableInline; } MediaQueryExpression::MediaQueryExpression(const String& feature, const Vector& tokenList) : m_mediaFeature(feature.convertToASCIILowercase()) , m_isValid(false) { // Create value for media query expression that must have 1 or more values. if (!tokenList.size() && isFeatureValidWithoutValue(m_mediaFeature)) { // Valid, creates a MediaQueryExp with an 'invalid' MediaQueryExpValue m_isValid = true; } else if (tokenList.size() == 1) { CSSParserToken token = tokenList.first(); if (token.type() == IdentToken) { CSSValueID ident = token.id(); if (!featureWithValidIdent(m_mediaFeature)) return; m_value = CSSPrimitiveValue::createIdentifier(ident); m_isValid = true; } else if (token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken) { // Check for numeric token types since it is only safe for these types to call numericValue. if (featureWithValidDensity(m_mediaFeature, token) || featureWithValidPositiveLength(m_mediaFeature, token)) { // Media features that must have non-negative , ie. dppx, dpi or dpcm, // or Media features that must have non-negative or number value. m_value = CSSPrimitiveValue::create(token.numericValue(), (CSSPrimitiveValue::UnitType) token.unitType()); m_isValid = true; } else if (featureWithPositiveInteger(m_mediaFeature, token) || featureWithPositiveNumber(m_mediaFeature, token) || featureWithZeroOrOne(m_mediaFeature, token)) { // 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. m_value = CSSPrimitiveValue::create(token.numericValue(), CSSPrimitiveValue::UnitType::CSS_NUMBER); m_isValid = true; } } } else if (tokenList.size() == 3 && isAspectRatioFeature(m_mediaFeature)) { // FIXME: is supposed to allow whitespace around the '/' // Applicable to device-aspect-ratio and aspect-ratio. const CSSParserToken& numerator = tokenList[0]; const CSSParserToken& delimiter = tokenList[1]; const CSSParserToken& denominator = tokenList[2]; if (delimiter.type() != DelimiterToken || delimiter.delimiter() != '/') return; if (numerator.type() != NumberToken || numerator.numericValue() <= 0 || numerator.numericValueType() != IntegerValueType) return; if (denominator.type() != NumberToken || denominator.numericValue() <= 0 || denominator.numericValueType() != IntegerValueType) return; m_value = CSSAspectRatioValue::create(numerator.numericValue(), denominator.numericValue()); m_isValid = true; } } String MediaQueryExpression::serialize() const { if (!m_serializationCache.isNull()) return m_serializationCache; StringBuilder result; result.append('('); result.append(m_mediaFeature.convertToASCIILowercase()); if (m_value) { result.appendLiteral(": "); result.append(m_value->cssText()); } result.append(')'); m_serializationCache = result.toString(); return m_serializationCache; } } // namespace