blob: e78a42f789973bfba3961a4650342eadcdc61967 [file] [log] [blame]
/*
* CSS Media Query Evaluator
*
* Copyright (C) 2006 Kimmo Kinnunen <kimmo.t.kinnunen@nokia.com>.
* Copyright (C) 2013 Apple Inc. All rights reserved.
* Copyright (C) 2013 Intel Corporation. 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 "third_party/blink/renderer/core/css/media_query_evaluator.h"
#include "third_party/blink/public/common/manifest/web_display_mode.h"
#include "third_party/blink/public/platform/pointer_properties.h"
#include "third_party/blink/public/platform/shape_properties.h"
#include "third_party/blink/public/platform/web_color_scheme.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/css_resolution_units.h"
#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
#include "third_party/blink/renderer/core/css/media_feature_names.h"
#include "third_party/blink/renderer/core/css/media_features.h"
#include "third_party/blink/renderer/core/css/media_list.h"
#include "third_party/blink/renderer/core/css/media_query.h"
#include "third_party/blink/renderer/core/css/media_values_dynamic.h"
#include "third_party/blink/renderer/core/css/media_values_initial_viewport.h"
#include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/use_counter.h"
#include "third_party/blink/renderer/core/media_type_names.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/graphics/color_space_gamut.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
namespace blink {
using namespace media_feature_names;
enum MediaFeaturePrefix { kMinPrefix, kMaxPrefix, kNoPrefix };
using EvalFunc = bool (*)(const MediaQueryExpValue&,
MediaFeaturePrefix,
const MediaValues&);
using FunctionMap = HashMap<StringImpl*, EvalFunc>;
static FunctionMap* g_function_map;
MediaQueryEvaluator::MediaQueryEvaluator(const char* accepted_media_type)
: media_type_(accepted_media_type) {}
MediaQueryEvaluator::MediaQueryEvaluator(LocalFrame* frame)
: media_values_(MediaValues::CreateDynamicIfFrameExists(frame)) {}
MediaQueryEvaluator::MediaQueryEvaluator(const MediaValues& media_values)
: media_values_(media_values.Copy()) {}
MediaQueryEvaluator::MediaQueryEvaluator(
MediaValuesInitialViewport* media_values)
: media_values_(media_values) {
DCHECK(media_values);
}
MediaQueryEvaluator::~MediaQueryEvaluator() = default;
void MediaQueryEvaluator::Trace(blink::Visitor* visitor) {
visitor->Trace(media_values_);
}
const String MediaQueryEvaluator::MediaType() const {
// If a static mediaType was given by the constructor, we use it here.
if (!media_type_.IsEmpty())
return media_type_;
// Otherwise, we get one from mediaValues (which may be dynamic or cached).
if (media_values_)
return media_values_->MediaType();
return g_null_atom;
}
bool MediaQueryEvaluator::MediaTypeMatch(
const String& media_type_to_match) const {
return media_type_to_match.IsEmpty() ||
DeprecatedEqualIgnoringCase(media_type_to_match,
media_type_names::kAll) ||
DeprecatedEqualIgnoringCase(media_type_to_match, MediaType());
}
static bool ApplyRestrictor(MediaQuery::RestrictorType r, bool value) {
return r == MediaQuery::kNot ? !value : value;
}
bool MediaQueryEvaluator::Eval(
const MediaQuery& query,
MediaQueryResultList* viewport_dependent_media_query_results,
MediaQueryResultList* device_dependent_media_query_results) const {
if (!MediaTypeMatch(query.MediaType()))
return ApplyRestrictor(query.Restrictor(), false);
const ExpressionHeapVector& expressions = query.Expressions();
// Iterate through expressions, stop if any of them eval to false (AND
// semantics).
wtf_size_t i = 0;
for (; i < expressions.size(); ++i) {
bool expr_result = Eval(expressions.at(i));
if (viewport_dependent_media_query_results &&
expressions.at(i).IsViewportDependent()) {
viewport_dependent_media_query_results->push_back(
MediaQueryResult(expressions.at(i), expr_result));
}
if (device_dependent_media_query_results &&
expressions.at(i).IsDeviceDependent()) {
device_dependent_media_query_results->push_back(
MediaQueryResult(expressions.at(i), expr_result));
}
if (!expr_result)
break;
}
// Assume true if we are at the end of the list, otherwise assume false.
return ApplyRestrictor(query.Restrictor(), expressions.size() == i);
}
bool MediaQueryEvaluator::Eval(
const MediaQuerySet& query_set,
MediaQueryResultList* viewport_dependent_media_query_results,
MediaQueryResultList* device_dependent_media_query_results) const {
const Vector<std::unique_ptr<MediaQuery>>& queries = query_set.QueryVector();
if (!queries.size())
return true; // Empty query list evaluates to true.
// Iterate over queries, stop if any of them eval to true (OR semantics).
bool result = false;
for (wtf_size_t i = 0; i < queries.size() && !result; ++i)
result = Eval(*queries[i], viewport_dependent_media_query_results,
device_dependent_media_query_results);
return result;
}
template <typename T>
bool CompareValue(T a, T b, MediaFeaturePrefix op) {
switch (op) {
case kMinPrefix:
return a >= b;
case kMaxPrefix:
return a <= b;
case kNoPrefix:
return a == b;
}
return false;
}
bool CompareDoubleValue(double a, double b, MediaFeaturePrefix op) {
const double precision = LayoutUnit::Epsilon();
switch (op) {
case kMinPrefix:
return a >= (b - precision);
case kMaxPrefix:
return a <= (b + precision);
case kNoPrefix:
return std::abs(a - b) <= precision;
}
return false;
}
static bool CompareAspectRatioValue(const MediaQueryExpValue& value,
int width,
int height,
MediaFeaturePrefix op) {
if (value.is_ratio) {
return CompareValue(static_cast<double>(width) * value.denominator,
static_cast<double>(height) * value.numerator, op);
}
return false;
}
static bool NumberValue(const MediaQueryExpValue& value, float& result) {
if (value.is_value && value.unit == CSSPrimitiveValue::UnitType::kNumber) {
result = value.value;
return true;
}
return false;
}
static bool ColorMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
float number;
int bits_per_component = media_values.ColorBitsPerComponent();
if (value.IsValid())
return NumberValue(value, number) &&
CompareValue(bits_per_component, static_cast<int>(number), op);
return bits_per_component != 0;
}
static bool ColorIndexMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues&) {
// FIXME: We currently assume that we do not support indexed displays, as it
// is unknown how to retrieve the information if the display mode is indexed.
// This matches Firefox.
if (!value.IsValid())
return false;
// Acording to spec, if the device does not use a color lookup table, the
// value is zero.
float number;
return NumberValue(value, number) &&
CompareValue(0, static_cast<int>(number), op);
}
static bool MonochromeMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
float number;
int bits_per_component = media_values.MonochromeBitsPerComponent();
if (value.IsValid()) {
return NumberValue(value, number) &&
CompareValue(bits_per_component, static_cast<int>(number), op);
}
return bits_per_component != 0;
}
static bool DisplayModeMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
// isValid() is false if there is no parameter. Without parameter we should
// return true to indicate that displayModeMediaFeature is enabled in the
// browser.
if (!value.IsValid())
return true;
if (!value.is_id)
return false;
WebDisplayMode mode = media_values.DisplayMode();
switch (value.id) {
case CSSValueFullscreen:
return mode == kWebDisplayModeFullscreen;
case CSSValueStandalone:
return mode == kWebDisplayModeStandalone;
case CSSValueMinimalUi:
return mode == kWebDisplayModeMinimalUi;
case CSSValueBrowser:
return mode == kWebDisplayModeBrowser;
default:
NOTREACHED();
return false;
}
}
static bool OrientationMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
int width = media_values.ViewportWidth();
int height = media_values.ViewportHeight();
if (value.is_id) {
if (width > height) // Square viewport is portrait.
return CSSValueLandscape == value.id;
return CSSValuePortrait == value.id;
}
// Expression (orientation) evaluates to true if width and height >= 0.
return height >= 0 && width >= 0;
}
static bool AspectRatioMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
if (value.IsValid())
return CompareAspectRatioValue(value, media_values.ViewportWidth(),
media_values.ViewportHeight(), op);
// ({,min-,max-}aspect-ratio)
// assume if we have a device, its aspect ratio is non-zero.
return true;
}
static bool DeviceAspectRatioMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
if (value.IsValid())
return CompareAspectRatioValue(value, media_values.DeviceWidth(),
media_values.DeviceHeight(), op);
// ({,min-,max-}device-aspect-ratio)
// assume if we have a device, its aspect ratio is non-zero.
return true;
}
static bool EvalResolution(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
// According to MQ4, only 'screen', 'print' and 'speech' may match.
// FIXME: What should speech match?
// https://www.w3.org/Style/CSS/Tracker/issues/348
float actual_resolution = 0;
// This checks the actual media type applied to the document, and we know
// this method only got called if this media type matches the one defined
// in the query. Thus, if if the document's media type is "print", the
// media type of the query will either be "print" or "all".
if (DeprecatedEqualIgnoringCase(media_values.MediaType(),
media_type_names::kScreen)) {
actual_resolution = clampTo<float>(media_values.DevicePixelRatio());
} else if (DeprecatedEqualIgnoringCase(media_values.MediaType(),
media_type_names::kPrint)) {
// The resolution of images while printing should not depend on the DPI
// of the screen. Until we support proper ways of querying this info
// we use 300px which is considered minimum for current printers.
actual_resolution = 300 / kCssPixelsPerInch;
}
if (!value.IsValid())
return !!actual_resolution;
if (!value.is_value)
return false;
if (value.unit == CSSPrimitiveValue::UnitType::kNumber)
return CompareValue(actual_resolution, clampTo<float>(value.value), op);
if (!CSSPrimitiveValue::IsResolution(value.unit))
return false;
double canonical_factor =
CSSPrimitiveValue::ConversionToCanonicalUnitsScaleFactor(value.unit);
double dppx_factor = CSSPrimitiveValue::ConversionToCanonicalUnitsScaleFactor(
CSSPrimitiveValue::UnitType::kDotsPerPixel);
float value_in_dppx =
clampTo<float>(value.value * (canonical_factor / dppx_factor));
if (value.unit == CSSPrimitiveValue::UnitType::kDotsPerCentimeter) {
// To match DPCM to DPPX values, we limit to 2 decimal points.
// The https://drafts.csswg.org/css-values/#absolute-lengths recommends
// "that the pixel unit refer to the whole number of device pixels that best
// approximates the reference pixel". With that in mind, allowing 2 decimal
// point precision seems appropriate.
return CompareValue(floorf(0.5 + 100 * actual_resolution) / 100,
floorf(0.5 + 100 * value_in_dppx) / 100, op);
}
return CompareValue(actual_resolution, value_in_dppx, op);
}
static bool DevicePixelRatioMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
UseCounter::Count(media_values.GetDocument(),
WebFeature::kPrefixedDevicePixelRatioMediaFeature);
return (!value.IsValid() ||
value.unit == CSSPrimitiveValue::UnitType::kNumber) &&
EvalResolution(value, op, media_values);
}
static bool ResolutionMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
return (!value.IsValid() || CSSPrimitiveValue::IsResolution(value.unit)) &&
EvalResolution(value, op, media_values);
}
static bool GridMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues&) {
// if output device is bitmap, grid: 0 == true
// assume we have bitmap device
float number;
if (value.IsValid() && NumberValue(value, number))
return CompareValue(static_cast<int>(number), 0, op);
return false;
}
static bool ComputeLength(const MediaQueryExpValue& value,
const MediaValues& media_values,
double& result) {
if (!value.is_value)
return false;
if (value.unit == CSSPrimitiveValue::UnitType::kNumber) {
result = clampTo<int>(value.value);
return !media_values.StrictMode() || !result;
}
if (CSSPrimitiveValue::IsLength(value.unit))
return media_values.ComputeLength(value.value, value.unit, result);
return false;
}
static bool ComputeLengthAndCompare(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values,
double compare_to_value) {
double length;
return ComputeLength(value, media_values, length) &&
CompareDoubleValue(compare_to_value, length, op);
}
static bool DeviceHeightMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
if (value.IsValid())
return ComputeLengthAndCompare(value, op, media_values,
media_values.DeviceHeight());
// ({,min-,max-}device-height)
// assume if we have a device, assume non-zero
return true;
}
static bool DeviceWidthMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
if (value.IsValid())
return ComputeLengthAndCompare(value, op, media_values,
media_values.DeviceWidth());
// ({,min-,max-}device-width)
// assume if we have a device, assume non-zero
return true;
}
static bool HeightMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
double height = media_values.ViewportHeight();
if (value.IsValid())
return ComputeLengthAndCompare(value, op, media_values, height);
return height;
}
static bool WidthMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
double width = media_values.ViewportWidth();
if (value.IsValid())
return ComputeLengthAndCompare(value, op, media_values, width);
return width;
}
// Rest of the functions are trampolines which set the prefix according to the
// media feature expression used.
static bool MinColorMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return ColorMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxColorMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return ColorMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinColorIndexMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return ColorIndexMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxColorIndexMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return ColorIndexMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinMonochromeMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return MonochromeMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxMonochromeMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return MonochromeMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinAspectRatioMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return AspectRatioMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxAspectRatioMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return AspectRatioMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinDeviceAspectRatioMediaFeatureEval(
const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return DeviceAspectRatioMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxDeviceAspectRatioMediaFeatureEval(
const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return DeviceAspectRatioMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinDevicePixelRatioMediaFeatureEval(
const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
UseCounter::Count(media_values.GetDocument(),
WebFeature::kPrefixedMinDevicePixelRatioMediaFeature);
return DevicePixelRatioMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxDevicePixelRatioMediaFeatureEval(
const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
UseCounter::Count(media_values.GetDocument(),
WebFeature::kPrefixedMaxDevicePixelRatioMediaFeature);
return DevicePixelRatioMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinHeightMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return HeightMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxHeightMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return HeightMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinWidthMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return WidthMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxWidthMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return WidthMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinDeviceHeightMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return DeviceHeightMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxDeviceHeightMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return DeviceHeightMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinDeviceWidthMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return DeviceWidthMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxDeviceWidthMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return DeviceWidthMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool MinResolutionMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return ResolutionMediaFeatureEval(value, kMinPrefix, media_values);
}
static bool MaxResolutionMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
return ResolutionMediaFeatureEval(value, kMaxPrefix, media_values);
}
static bool Transform3dMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
UseCounter::Count(media_values.GetDocument(),
WebFeature::kPrefixedTransform3dMediaFeature);
bool return_value_if_no_parameter;
int have3d_rendering;
bool three_d_enabled = media_values.ThreeDEnabled();
return_value_if_no_parameter = three_d_enabled;
have3d_rendering = three_d_enabled ? 1 : 0;
if (value.IsValid()) {
float number;
return NumberValue(value, number) &&
CompareValue(have3d_rendering, static_cast<int>(number), op);
}
return return_value_if_no_parameter;
}
static bool ImmersiveMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix op,
const MediaValues& media_values) {
bool return_value_if_no_parameter;
int is_immersive_numeric_value;
bool immersive = media_values.InImmersiveMode();
return_value_if_no_parameter = immersive;
is_immersive_numeric_value = immersive ? 1 : 0;
if (value.IsValid()) {
float number;
return NumberValue(value, number) &&
CompareValue(is_immersive_numeric_value, static_cast<int>(number),
op);
}
return return_value_if_no_parameter;
}
static bool HoverMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
HoverType hover = media_values.PrimaryHoverType();
if (!value.IsValid())
return hover != kHoverTypeNone;
if (!value.is_id)
return false;
return (hover == kHoverTypeNone && value.id == CSSValueNone) ||
(hover == kHoverTypeHover && value.id == CSSValueHover);
}
static bool AnyHoverMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
int available_hover_types = media_values.AvailableHoverTypes();
if (!value.IsValid())
return available_hover_types & ~kHoverTypeNone;
if (!value.is_id)
return false;
switch (value.id) {
case CSSValueNone:
return available_hover_types & kHoverTypeNone;
case CSSValueHover:
return available_hover_types & kHoverTypeHover;
default:
NOTREACHED();
return false;
}
}
static bool PointerMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
PointerType pointer = media_values.PrimaryPointerType();
if (!value.IsValid())
return pointer != kPointerTypeNone;
if (!value.is_id)
return false;
return (pointer == kPointerTypeNone && value.id == CSSValueNone) ||
(pointer == kPointerTypeCoarse && value.id == CSSValueCoarse) ||
(pointer == kPointerTypeFine && value.id == CSSValueFine);
}
static bool PrefersReducedMotionMediaFeatureEval(
const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
// If the value is not valid, this was passed without an argument. In that
// case, it implicitly resolves to 'reduce'.
if (!value.IsValid())
return media_values.PrefersReducedMotion();
if (!value.is_id)
return false;
return (value.id == CSSValueNoPreference) ^
media_values.PrefersReducedMotion();
}
static bool ShapeMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
if (!value.IsValid())
return true;
if (!value.is_id)
return false;
DisplayShape shape = media_values.GetDisplayShape();
switch (value.id) {
case CSSValueRect:
return shape == kDisplayShapeRect;
case CSSValueRound:
return shape == kDisplayShapeRound;
default:
NOTREACHED();
return false;
}
}
static bool AnyPointerMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
int available_pointers = media_values.AvailablePointerTypes();
if (!value.IsValid())
return available_pointers & ~kPointerTypeNone;
if (!value.is_id)
return false;
switch (value.id) {
case CSSValueCoarse:
return available_pointers & kPointerTypeCoarse;
case CSSValueFine:
return available_pointers & kPointerTypeFine;
case CSSValueNone:
return available_pointers & kPointerTypeNone;
default:
NOTREACHED();
return false;
}
}
static bool ScanMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
// Scan only applies to 'tv' media.
if (!DeprecatedEqualIgnoringCase(media_values.MediaType(),
media_type_names::kTv))
return false;
if (!value.IsValid())
return true;
if (!value.is_id)
return false;
// If a platform interface supplies progressive/interlace info for TVs in the
// future, it needs to be handled here. For now, assume a modern TV with
// progressive display.
return (value.id == CSSValueProgressive);
}
static bool ColorGamutMediaFeatureEval(const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
// isValid() is false if there is no parameter. Without parameter we should
// return true to indicate that colorGamutMediaFeature is enabled in the
// browser.
if (!value.IsValid())
return true;
if (!value.is_id)
return false;
DCHECK(value.id == CSSValueSRGB || value.id == CSSValueP3 ||
value.id == CSSValueRec2020);
ColorSpaceGamut gamut = media_values.ColorGamut();
switch (gamut) {
case ColorSpaceGamut::kUnknown:
case ColorSpaceGamut::kLessThanNTSC:
case ColorSpaceGamut::NTSC:
case ColorSpaceGamut::SRGB:
return value.id == CSSValueSRGB;
case ColorSpaceGamut::kAlmostP3:
case ColorSpaceGamut::P3:
case ColorSpaceGamut::kAdobeRGB:
case ColorSpaceGamut::kWide:
return value.id == CSSValueSRGB || value.id == CSSValueP3;
case ColorSpaceGamut::BT2020:
case ColorSpaceGamut::kProPhoto:
case ColorSpaceGamut::kUltraWide:
return value.id == CSSValueSRGB || value.id == CSSValueP3 ||
value.id == CSSValueRec2020;
case ColorSpaceGamut::kEnd:
NOTREACHED();
return false;
}
// This is for some compilers that do not understand that it can't be reached.
NOTREACHED();
return false;
}
static bool PrefersColorSchemeMediaFeatureEval(
const MediaQueryExpValue& value,
MediaFeaturePrefix,
const MediaValues& media_values) {
WebColorScheme preferred_scheme = media_values.PreferredColorScheme();
if (!value.IsValid())
return preferred_scheme != WebColorScheme::kNoPreference;
if (!value.is_id)
return false;
return (preferred_scheme == WebColorScheme::kNoPreference &&
value.id == CSSValueNoPreference) ||
(preferred_scheme == WebColorScheme::kDark &&
value.id == CSSValueDark) ||
(preferred_scheme == WebColorScheme::kLight &&
value.id == CSSValueLight);
}
void MediaQueryEvaluator::Init() {
// Create the table.
g_function_map = new FunctionMap;
#define ADD_TO_FUNCTIONMAP(constantPrefix, methodPrefix) \
g_function_map->Set(constantPrefix##MediaFeature.Impl(), \
methodPrefix##MediaFeatureEval);
CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(ADD_TO_FUNCTIONMAP);
#undef ADD_TO_FUNCTIONMAP
}
bool MediaQueryEvaluator::Eval(const MediaQueryExp& expr) const {
if (!media_values_ || !media_values_->HasValues())
return true;
DCHECK(g_function_map);
// Call the media feature evaluation function. Assume no prefix and let
// trampoline functions override the prefix if prefix is used.
EvalFunc func = g_function_map->at(expr.MediaFeature().Impl());
if (func)
return func(expr.ExpValue(), kNoPrefix, *media_values_);
return false;
}
} // namespace blink