MediaQueryExpression.cpp [plain text]
#include "config.h"
#include "MediaQueryExpression.h"
#include "CSSAspectRatioValue.h"
#include "CSSPrimitiveValue.h"
#include "CSSPropertyParserHelpers.h"
#include "MediaFeatureNames.h"
#include "MediaQueryParserContext.h"
#include <wtf/text/TextStream.h>
#include <wtf/text/StringBuilder.h>
namespace WebCore {
static inline bool featureWithValidIdent(const AtomicString& mediaFeature, const CSSPrimitiveValue& value, const MediaQueryParserContext& context)
{
if (value.primitiveType() != CSSPrimitiveValue::UnitType::CSS_IDENT)
return false;
return mediaFeature == MediaFeatureNames::orientation
|| mediaFeature == MediaFeatureNames::colorGamut
|| mediaFeature == MediaFeatureNames::anyHover
|| mediaFeature == MediaFeatureNames::anyPointer
|| mediaFeature == MediaFeatureNames::hover
|| mediaFeature == MediaFeatureNames::invertedColors
|| mediaFeature == MediaFeatureNames::pointer
#if ENABLE(APPLICATION_MANIFEST)
|| mediaFeature == MediaFeatureNames::displayMode
#endif
|| mediaFeature == MediaFeatureNames::prefersReducedMotion
|| (mediaFeature == MediaFeatureNames::prefersDarkInterface && (context.useSystemAppearance || isUASheetBehavior(context.mode)));
}
static inline bool featureWithValidDensity(const String& mediaFeature, const CSSPrimitiveValue& value)
{
if (!value.isResolution() || value.doubleValue() <= 0)
return false;
return mediaFeature == MediaFeatureNames::resolution
|| mediaFeature == MediaFeatureNames::minResolution
|| mediaFeature == MediaFeatureNames::maxResolution;
}
static inline bool featureWithValidPositiveLength(const String& mediaFeature, const CSSPrimitiveValue& value)
{
if (!(value.isLength() || (value.isNumber() && !value.doubleValue())) || value.doubleValue() < 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 featureExpectingPositiveInteger(const String& mediaFeature)
{
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 featureWithPositiveInteger(const String& mediaFeature, const CSSPrimitiveValue& value)
{
if (!value.isNumber())
return false;
return featureExpectingPositiveInteger(mediaFeature);
}
static inline bool featureWithPositiveNumber(const String& mediaFeature, const CSSPrimitiveValue& value)
{
if (!value.isNumber())
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 CSSPrimitiveValue& value)
{
if (!value.isNumber() || !(value.doubleValue() == 1 || !value.doubleValue()))
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, const MediaQueryParserContext& context)
{
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
|| mediaFeature == MediaFeatureNames::pointer
|| mediaFeature == MediaFeatureNames::prefersReducedMotion
|| (mediaFeature == MediaFeatureNames::prefersDarkInterface && (context.useSystemAppearance || isUASheetBehavior(context.mode)))
|| mediaFeature == MediaFeatureNames::devicePixelRatio
|| mediaFeature == MediaFeatureNames::resolution
#if ENABLE(APPLICATION_MANIFEST)
|| mediaFeature == MediaFeatureNames::displayMode
#endif
|| mediaFeature == MediaFeatureNames::videoPlayableInline;
}
inline RefPtr<CSSPrimitiveValue> consumeFirstValue(const String& mediaFeature, CSSParserTokenRange& range)
{
if (auto value = CSSPropertyParserHelpers::consumeInteger(range, 0))
return value;
if (!featureExpectingPositiveInteger(mediaFeature) && !isAspectRatioFeature(mediaFeature)) {
if (auto value = CSSPropertyParserHelpers::consumeNumber(range, ValueRangeNonNegative))
return value;
}
if (auto value = CSSPropertyParserHelpers::consumeLength(range, HTMLStandardMode, ValueRangeNonNegative))
return value;
if (auto value = CSSPropertyParserHelpers::consumeResolution(range))
return value;
if (auto value = CSSPropertyParserHelpers::consumeIdent(range))
return value;
return nullptr;
}
MediaQueryExpression::MediaQueryExpression(const String& feature, CSSParserTokenRange& range, MediaQueryParserContext& context)
: m_mediaFeature(feature.convertToASCIILowercase())
, m_isValid(false)
{
RefPtr<CSSPrimitiveValue> firstValue = consumeFirstValue(m_mediaFeature, range);
if (!firstValue) {
if (isFeatureValidWithoutValue(m_mediaFeature, context)) {
m_isValid = true;
}
return;
}
if (isAspectRatioFeature(m_mediaFeature)) {
if (!firstValue->isNumber() || !firstValue->doubleValue())
return;
if (!CSSPropertyParserHelpers::consumeSlashIncludingWhitespace(range))
return;
RefPtr<CSSPrimitiveValue> denominatorValue = CSSPropertyParserHelpers::consumePositiveInteger(range);
if (!denominatorValue)
return;
unsigned numerator = clampTo<unsigned>(firstValue->doubleValue());
unsigned denominator = clampTo<unsigned>(denominatorValue->doubleValue());
m_value = CSSAspectRatioValue::create(numerator, denominator);
m_isValid = true;
return;
}
if (featureWithPositiveInteger(m_mediaFeature, *firstValue) || featureWithPositiveNumber(m_mediaFeature, *firstValue)
|| featureWithZeroOrOne(m_mediaFeature, *firstValue) || featureWithValidDensity(m_mediaFeature, *firstValue)
|| featureWithValidPositiveLength(m_mediaFeature, *firstValue) || featureWithValidIdent(m_mediaFeature, *firstValue, context)) {
m_value = firstValue;
m_isValid = true;
return;
}
}
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;
}
TextStream& operator<<(TextStream& ts, const MediaQueryExpression& expression)
{
ts << expression.serialize();
return ts;
}
}