mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
346 lines
13 KiB
C++
346 lines
13 KiB
C++
/*
|
|
* CSS Media Query
|
|
*
|
|
* Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen@nokia.com>.
|
|
* 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> 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 <density>, ie. dppx, dpi or dpcm,
|
|
// or Media features that must have non-negative <length> 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 <integer>/<integer>.
|
|
// Applicable to device-aspect-ratio and aspec-ratio.
|
|
isValid = true;
|
|
float numeratorValue = 0;
|
|
float denominatorValue = 0;
|
|
// The aspect-ratio must be <integer> (whitespace)? / (whitespace)? <integer>.
|
|
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
|