blob: e6ba4938717db4c5f66b4c3a52017f1b0c6e0d47 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/css/media_query_evaluator.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/css/forced_colors.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_sample_test_utils.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
#include "third_party/blink/public/common/privacy_budget/scoped_identifiability_test_sample_collector.h"
#include "third_party/blink/renderer/core/css/media_list.h"
#include "third_party/blink/renderer/core/css/media_values.h"
#include "third_party/blink/renderer/core/css/media_values_cached.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
#include "third_party/blink/renderer/core/css/parser/media_query_parser.h"
#include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
#include "third_party/blink/renderer/core/dom/document.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/html/html_element.h"
#include "third_party/blink/renderer/core/media_type_names.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/display_color_spaces.h"
namespace blink {
namespace {
MediaQueryExpValue PxValue(double value) {
return MediaQueryExpValue(value, CSSPrimitiveValue::UnitType::kPixels);
}
MediaQueryExpValue RatioValue(unsigned numerator, unsigned denominator) {
return MediaQueryExpValue(numerator, denominator);
}
} // namespace
struct MediaQueryEvaluatorTestCase {
const char* input;
const bool output;
};
MediaQueryEvaluatorTestCase g_screen_test_cases[] = {
{"", true},
{" ", true},
{"screen", true},
{"screen and (color)", true},
{"not screen and (color)", false},
{"screen and (device-aspect-ratio: 16/9)", false},
{"screen and (device-aspect-ratio: 0.5/0.5)", true},
{"screen and (device-aspect-ratio: 1.5)", false},
{"screen and (device-aspect-ratio: 1/1)", true},
{"screen and (device-aspect-ratio: calc(1/1))", true},
{"all and (min-color: 2)", true},
{"all and (min-color: 32)", false},
{"all and (min-color-index: 0)", true},
{"all and (min-color-index: 1)", false},
{"all and (monochrome)", false},
{"all and (min-monochrome: 0)", true},
{"all and (grid: 0)", true},
{"(resolution: 2dppx)", true},
{"(resolution: 1dppx)", false},
{"(resolution: calc(2x))", true},
{"(resolution: calc(1x))", false},
{"(resolution: calc(1x + 1x))", true},
{"(resolution: calc(1x + 0x))", false},
{"(resolution: calc(1x + 96dpi))", true},
{"(resolution: calc(0x + 37.79532dpcm))", false},
{"(resolution: calc(3x - 1x))", true},
{"(resolution: calc(3x - 2x))", false},
{"(resolution: calc(3x - 96dpi))", true},
{"(resolution: calc(2x - 37.79532dpcm))", false},
{"(resolution: calc(1x * 2))", true},
{"(resolution: calc(0.5x * 2))", false},
{"(resolution: calc(4x / 2))", true},
{"(resolution: calc(2x / 2))", false},
{"(orientation: portrait)", true},
{"(orientation: landscape)", false},
{"(orientation: url(portrait))", false},
{"(orientation: #portrait)", false},
{"(orientation: @portrait)", false},
{"(orientation: 'portrait')", false},
{"(orientation: @junk portrait)", false},
{"screen and (orientation: @portrait) and (max-width: 1000px)", false},
{"screen and (orientation: @portrait), (max-width: 1000px)", true},
{"tv and (scan: progressive)", false},
{"(pointer: coarse)", false},
{"(pointer: fine)", true},
{"(hover: hover)", true},
{"(hover: on-demand)", false},
{"(hover: none)", false},
{"(display-mode)", true},
{"(display-mode: fullscreen)", false},
{"(display-mode: standalone)", false},
{"(display-mode: minimal-ui)", false},
{"(display-mode: window-controls-overlay)", false},
{"(display-mode: borderless)", false},
{"(display-mode: browser)", true},
{"(display-mode: min-browser)", false},
{"(display-mode: url(browser))", false},
{"(display-mode: #browser)", false},
{"(display-mode: @browser)", false},
{"(display-mode: 'browser')", false},
{"(display-mode: @junk browser)", false},
{"(display-mode: tabbed)", false},
{"(display-mode: picture-in-picture)", false},
{"(max-device-aspect-ratio: 4294967295/1)", true},
{"(min-device-aspect-ratio: 1/4294967296)", true},
{"(max-device-aspect-ratio: 0.5)", false},
{"(max-device-aspect-ratio: 0.6/0.5)", true},
{"(min-device-aspect-ratio: 1/2)", true},
{"(max-device-aspect-ratio: 1.5)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_display_state_test_cases[] = {
{"(display-state)", true},
{"(display-state: fullscreen)", false},
{"(display-state: minimized)", false},
{"(display-state: maximized)", false},
{"(display-state: normal)", true},
{"(display-state: #normal)", false},
{"(display-state: @normal)", false},
{"(display-state: 'normal')", false},
{"(display-state: @junk normal)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_resizable_test_cases[] = {
{"(resizable)", true},
{"(resizable: true)", true},
{"(resizable: false)", false},
{"(resizable: #true)", false},
{"(resizable: @true)", false},
{"(resizable: 'true')", false},
{"(resizable: \"true\")", false},
{"(resizable: @junk true)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_monochrome_test_cases[] = {
{"(color)", false},
{"(monochrome)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_viewport_test_cases[] = {
{"all and (min-width: 500px)", true},
{"(min-width: 500px)", true},
{"(min-width: 501px)", false},
{"(max-width: 500px)", true},
{"(max-width: 499px)", false},
{"(width: 500px)", true},
{"(width: 501px)", false},
{"(min-height: 500px)", true},
{"(min-height: 501px)", false},
{"(min-height: 500.02px)", false},
{"(max-height: 500px)", true},
{"(max-height: calc(500px))", true},
{"(max-height: 499.98px)", false},
{"(max-height: 499px)", false},
{"(height: 500px)", true},
{"(height: calc(500px))", true},
{"(height: 500.001px)", true},
{"(height: 499.999px)", true},
{"(height: 500.02px)", false},
{"(height: 499.98px)", false},
{"(height: 501px)", false},
{"(height)", true},
{"(width)", true},
{"(width: whatisthis)", false},
{"screen and (min-width: 400px) and (max-width: 700px)", true},
{"(max-aspect-ratio: 4294967296/1)", true},
{"(max-aspect-ratio: calc(4294967296) / calc(1)", true},
{"(min-aspect-ratio: 1/4294967295)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_float_viewport_test_cases[] = {
{"all and (min-width: 600.5px)", true},
{"(min-width: 600px)", true},
{"(min-width: 600.5px)", true},
{"(min-width: 601px)", false},
{"(max-width: 600px)", false},
{"(max-width: 600.5px)", true},
{"(max-width: 601px)", true},
{"(width: 600.5px)", true},
{"(width: 601px)", false},
{"(min-height: 700px)", true},
{"(min-height: 700.125px)", true},
{"(min-height: 701px)", false},
{"(min-height: 700.141px)", false},
{"(max-height: 701px)", true},
{"(max-height: 700.125px)", true},
{"(max-height: 700px)", false},
{"(height: 700.125px)", true},
{"(height: 700.141px)", false},
{"(height: 700.109px)", false},
{"(height: 701px)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_float_non_friendly_viewport_test_cases[] = {
{"(min-width: 821px)", true},
{"(max-width: 821px)", true},
{"(width: 821px)", true},
{"(min-height: 821px)", true},
{"(max-height: 821px)", true},
{"(height: 821px)", true},
{"(width: 100vw)", true},
{"(height: 100vh)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_print_test_cases[] = {
{"print and (min-resolution: 1dppx)", true},
{"print and (min-resolution: calc(100dpi - 4dpi))", true},
{"print and (min-resolution: 118dpcm)", true},
{"print and (min-resolution: 119dpcm)", false},
{nullptr, false} // Do not remove the terminator line.
};
// Tests when the output device is print.
MediaQueryEvaluatorTestCase g_update_with_print_device_test_cases[] = {
{"(update)", false}, {"(update: none)", true},
{"(update: slow)", false}, {"(update: fast)", false},
{"update: fast", false}, {"(update: ?)", false},
{nullptr, false} // Do not remove the terminator line.
};
// Tests when the output device is slow.
MediaQueryEvaluatorTestCase g_update_with_slow_device_test_cases[] = {
{"(update)", true}, {"(update: none)", false},
{"(update: slow)", true}, {"(update: fast)", false},
{"update: fast", false}, {"(update: ?)", false},
{nullptr, false} // Do not remove the terminator line.
};
// Tests when the output device is slow.
MediaQueryEvaluatorTestCase g_update_with_fast_device_test_cases[] = {
{"(update)", true}, {"(update: none)", false},
{"(update: slow)", false}, {"(update: fast)", true},
{"update: fast", false}, {"(update: ?)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_forcedcolors_active_cases[] = {
{"(forced-colors: active)", true},
{"(forced-colors: none)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_forcedcolors_none_cases[] = {
{"(forced-colors: active)", false},
{"(forced-colors: none)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_preferscontrast_nopreference_cases[] = {
{"(prefers-contrast)", false},
{"(prefers-contrast: more)", false},
{"(prefers-contrast: less)", false},
{"(prefers-contrast: no-preference)", true},
{"(prefers-contrast: custom)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_preferscontrast_more_cases[] = {
{"(prefers-contrast)", true},
{"(prefers-contrast: more)", true},
{"(prefers-contrast: less)", false},
{"(prefers-contrast: no-preference)", false},
{"(prefers-contrast: custom)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_preferscontrast_less_cases[] = {
{"(prefers-contrast)", true},
{"(prefers-contrast: more)", false},
{"(prefers-contrast: less)", true},
{"(prefers-contrast: no-preference)", false},
{"(prefers-contrast: custom)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_preferscontrast_custom_cases[] = {
{"(prefers-contrast)", true},
{"(prefers-contrast: more)", false},
{"(prefers-contrast: less)", false},
{"(prefers-contrast: no-preference)", false},
{"(prefers-contrast: custom)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_prefersreducedtransparency_nopreference_cases[] =
{
{"(prefers-reduced-transparency)", false},
{"(prefers-reduced-transparency: reduce)", false},
{"(prefers-reduced-transparency: no-preference)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_prefersreducedtransparency_reduce_cases[] = {
{"(prefers-reduced-transparency)", true},
{"(prefers-reduced-transparency: reduce)", true},
{"(prefers-reduced-transparency: no-preference)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_navigationcontrols_back_button_cases[] = {
{"(navigation-controls: back-button)", true},
{"(navigation-controls: none)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_navigationcontrols_none_cases[] = {
{"(navigation-controls: back-button)", false},
{"(navigation-controls: none)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_single_horizontal_viewport_segment_cases[] = {
{"(horizontal-viewport-segments)", true},
{"(horizontal-viewport-segments: 1)", true},
{"(horizontal-viewport-segments > 1)", false},
{"(horizontal-viewport-segments: 2)", false},
{"(horizontal-viewport-segments: none)", false},
{"(horizontal-viewport-segments: 1px)", false},
{"(horizontal-viewport-segments: 16/9)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_double_horizontal_viewport_segment_cases[] = {
{"(horizontal-viewport-segments)", true},
{"(horizontal-viewport-segments: 1)", false},
{"(horizontal-viewport-segments: 2)", true},
{"(horizontal-viewport-segments: 3)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_single_vertical_viewport_segment_cases[] = {
{"(vertical-viewport-segments)", true},
{"(vertical-viewport-segments: 1)", true},
{"(vertical-viewport-segments: 2)", false},
{"(vertical-viewport-segments: none)", false},
{"(vertical-viewport-segments: 1px)", false},
{"(vertical-viewport-segments: 16/9)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_double_vertical_viewport_segment_cases[] = {
{"(vertical-viewport-segments)", true},
{"(vertical-viewport-segments: 1)", false},
{"(vertical-viewport-segments: 2)", true},
{"(vertical-viewport-segments: 3)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_device_posture_none_cases[] = {
{"(device-posture)", true},
{"(device-posture: continuous)", true},
{"(device-posture: folded)", false},
{"(device-posture: 15)", false},
{"(device-posture: 2px)", false},
{"(device-posture: 16/9)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_device_posture_folded_cases[] = {
{"(device-posture)", true},
{"(device-posture: continuous)", false},
{"(device-posture: folded)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_device_posture_folded_over_cases[] = {
{"(device-posture)", true},
{"(device-posture: continuous)", false},
{"(device-posture: folded)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_dynamic_range_standard_cases[] = {
{"(dynamic-range: standard)", true},
{"(dynamic-range: high)", false},
{"(dynamic-range: invalid)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_dynamic_range_high_cases[] = {
{"(dynamic-range: standard)", true},
{"(dynamic-range: high)", true},
{"(dynamic-range: invalid)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_dynamic_range_feature_disabled_cases[] = {
{"(dynamic-range: standard)", false},
{"(dynamic-range: high)", false},
{"(dynamic-range: invalid)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_video_dynamic_range_standard_cases[] = {
{"(video-dynamic-range: standard)", true},
{"(video-dynamic-range: high)", false},
{"(video-dynamic-range: invalid)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_video_dynamic_range_high_cases[] = {
{"(video-dynamic-range: standard)", true},
{"(video-dynamic-range: high)", true},
{"(video-dynamic-range: invalid)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_video_dynamic_range_feature_disabled_cases[] = {
{"(video-dynamic-range: standard)", false},
{"(video-dynamic-range: high)", false},
{"(video-dynamic-range: invalid)", false},
{nullptr, false} // Do not remove the terminator line.
};
// Tests when the output device is print.
MediaQueryEvaluatorTestCase g_overflow_with_print_device_test_cases[] = {
{"(overflow-inline)", false},
{"(overflow-block)", true},
{"(overflow-inline: none)", true},
{"(overflow-block: none)", false},
{"(overflow-block: paged)", true},
{"(overflow-inline: scroll)", false},
{"(overflow-block: scroll)", false},
{nullptr, false} // Do not remove the terminator line.
};
// Tests when the output device is scrollable.
MediaQueryEvaluatorTestCase g_overflow_with_scrollable_device_test_cases[] = {
{"(overflow-inline)", true},
{"(overflow-block)", true},
{"(overflow-inline: none)", false},
{"(overflow-block: none)", false},
{"(overflow-block: paged)", false},
{"(overflow-inline: scroll)", true},
{"(overflow-block: scroll)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_invertedcolors_none_cases[] = {
{"(inverted-colors)", false},
{"(inverted-colors: inverted)", false},
{"(inverted-colors: none)", true},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_invertedcolors_inverted_cases[] = {
{"(inverted-colors)", true},
{"(inverted-colors: inverted)", true},
{"(inverted-colors: none)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_scripting_none_cases[] = {
{"(scripting)", false},
{"(scripting: none)", true},
{"(scripting: initial-only)", false},
{"(scripting: enabled)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_scripting_initial_only_cases[] = {
{"(scripting)", false},
{"(scripting: none)", false},
{"(scripting: initial-only)", true},
{"(scripting: enabled)", false},
{nullptr, false} // Do not remove the terminator line.
};
MediaQueryEvaluatorTestCase g_scripting_enabled_cases[] = {
{"(scripting)", true},
{"(scripting: none)", false},
{"(scripting: initial-only)", false},
{"(scripting: enabled)", true},
{nullptr, false} // Do not remove the terminator line.
};
void TestMQEvaluator(MediaQueryEvaluatorTestCase* test_cases,
const MediaQueryEvaluator& media_query_evaluator,
CSSParserMode mode) {
MediaQuerySet* query_set = nullptr;
for (unsigned i = 0; test_cases[i].input; ++i) {
if (String(test_cases[i].input).empty()) {
query_set = MediaQuerySet::Create();
} else {
StringView str(test_cases[i].input);
CSSTokenizer tokenizer(StringView(test_cases[i].input));
auto [tokens, offsets] = tokenizer.TokenizeToEOFWithOffsets();
query_set = MediaQueryParser::ParseMediaQuerySetInMode(
CSSParserTokenRange(tokens),
CSSParserTokenOffsets(tokens, std::move(offsets), str), mode,
nullptr);
}
EXPECT_EQ(test_cases[i].output, media_query_evaluator.Eval(*query_set))
<< "Query: " << test_cases[i].input;
}
}
void TestMQEvaluator(MediaQueryEvaluatorTestCase* test_cases,
const MediaQueryEvaluator& media_query_evaluator) {
TestMQEvaluator(test_cases, media_query_evaluator, kHTMLStandardMode);
}
TEST(MediaQueryEvaluatorTest, Cached) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 500;
data.viewport_height = 500;
data.device_width = 500;
data.device_height = 500;
data.device_pixel_ratio = 2.0;
data.color_bits_per_component = 24;
data.monochrome_bits_per_component = 0;
data.primary_pointer_type = mojom::blink::PointerType::kPointerFineType;
data.primary_hover_type = mojom::blink::HoverType::kHoverHoverType;
data.output_device_update_ability_type =
mojom::blink::OutputDeviceUpdateAbilityType::kFastType;
data.three_d_enabled = true;
data.media_type = media_type_names::kScreen;
data.strict_mode = true;
data.display_mode = blink::mojom::DisplayMode::kBrowser;
// Default values.
{
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_screen_test_cases, media_query_evaluator);
TestMQEvaluator(g_viewport_test_cases, media_query_evaluator);
}
// Default display-state values.
{
data.window_show_state = ui::SHOW_STATE_DEFAULT;
ScopedDesktopPWAsAdditionalWindowingControlsForTest scoped_feature(true);
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_display_state_test_cases, media_query_evaluator);
}
// Default resizable values.
{
ScopedDesktopPWAsAdditionalWindowingControlsForTest scoped_feature(true);
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_resizable_test_cases, media_query_evaluator);
}
// Print values.
{
data.media_type = media_type_names::kPrint;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_print_test_cases, media_query_evaluator);
data.media_type = media_type_names::kScreen;
}
// Update values with print device.
{
data.media_type = media_type_names::kPrint;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_update_with_print_device_test_cases,
media_query_evaluator);
data.media_type = media_type_names::kScreen;
}
// Update values with slow device.
{
data.output_device_update_ability_type =
mojom::blink::OutputDeviceUpdateAbilityType::kSlowType;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_update_with_slow_device_test_cases,
media_query_evaluator);
}
// Update values with fast device.
{
data.output_device_update_ability_type =
mojom::blink::OutputDeviceUpdateAbilityType::kFastType;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_update_with_fast_device_test_cases,
media_query_evaluator);
}
// Monochrome values.
{
data.color_bits_per_component = 0;
data.monochrome_bits_per_component = 8;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_monochrome_test_cases, media_query_evaluator);
data.color_bits_per_component = 24;
data.monochrome_bits_per_component = 0;
}
// Overflow values with printing.
{
data.media_type = media_type_names::kPrint;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_overflow_with_print_device_test_cases,
media_query_evaluator);
data.media_type = media_type_names::kScreen;
}
// Overflow values with scrolling.
{
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_overflow_with_scrollable_device_test_cases,
media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, Dynamic) {
test::TaskEnvironment task_environment;
auto page_holder = std::make_unique<DummyPageHolder>(gfx::Size(500, 500));
page_holder->GetFrameView().SetMediaType(media_type_names::kScreen);
MediaQueryEvaluator media_query_evaluator(&page_holder->GetFrame());
TestMQEvaluator(g_viewport_test_cases, media_query_evaluator);
TestMQEvaluator(g_overflow_with_scrollable_device_test_cases,
media_query_evaluator);
TestMQEvaluator(g_update_with_fast_device_test_cases, media_query_evaluator);
page_holder->GetFrame().GetSettings()->SetOutputDeviceUpdateAbilityType(
mojom::blink::OutputDeviceUpdateAbilityType::kSlowType);
TestMQEvaluator(g_update_with_slow_device_test_cases, media_query_evaluator);
page_holder->GetFrameView().SetMediaType(media_type_names::kPrint);
TestMQEvaluator(g_print_test_cases, media_query_evaluator);
TestMQEvaluator(g_overflow_with_print_device_test_cases,
media_query_evaluator);
TestMQEvaluator(g_update_with_print_device_test_cases, media_query_evaluator);
}
TEST(MediaQueryEvaluatorTest, DynamicNoView) {
test::TaskEnvironment task_environment;
auto page_holder = std::make_unique<DummyPageHolder>(gfx::Size(500, 500));
LocalFrame* frame = &page_holder->GetFrame();
page_holder.reset();
ASSERT_EQ(nullptr, frame->View());
MediaQueryEvaluator media_query_evaluator(frame);
MediaQuerySet* query_set = MediaQuerySet::Create("foobar", nullptr);
EXPECT_FALSE(media_query_evaluator.Eval(*query_set));
}
TEST(MediaQueryEvaluatorTest, CachedFloatViewport) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 600.5;
data.viewport_height = 700.125;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_float_viewport_test_cases, media_query_evaluator);
}
TEST(MediaQueryEvaluatorTest, CachedFloatViewportNonFloatFriendly) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 821;
data.viewport_height = 821;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_float_non_friendly_viewport_test_cases,
media_query_evaluator);
}
TEST(MediaQueryEvaluatorTest, CachedForcedColors) {
ScopedForcedColorsForTest scoped_feature(true);
MediaValuesCached::MediaValuesCachedData data;
// Forced colors - none.
{
data.forced_colors = ForcedColors::kNone;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_forcedcolors_none_cases, media_query_evaluator);
}
// Forced colors - active.
{
data.forced_colors = ForcedColors::kActive;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_forcedcolors_active_cases, media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, CachedPrefersContrast) {
ScopedForcedColorsForTest forced_scoped_feature(true);
MediaValuesCached::MediaValuesCachedData data;
data.forced_colors = ForcedColors::kNone;
// Prefers-contrast - no-preference.
{
data.preferred_contrast = mojom::blink::PreferredContrast::kNoPreference;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_preferscontrast_nopreference_cases,
media_query_evaluator);
}
// Prefers-contrast - more.
{
data.preferred_contrast = mojom::blink::PreferredContrast::kMore;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_preferscontrast_more_cases, media_query_evaluator);
}
// Prefers-contrast - less.
{
data.preferred_contrast = mojom::blink::PreferredContrast::kLess;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_preferscontrast_less_cases, media_query_evaluator);
}
// Prefers-contrast - custom.
{
data.preferred_contrast = mojom::blink::PreferredContrast::kCustom;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_preferscontrast_custom_cases, media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, CachedPrefersReducedTransparency) {
MediaValuesCached::MediaValuesCachedData data;
// Prefers-reduced-transparency - no-preference.
{
data.prefers_reduced_transparency = false;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_prefersreducedtransparency_nopreference_cases,
media_query_evaluator);
}
// Prefers-reduced-transparency - reduce.
{
data.prefers_reduced_transparency = true;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_prefersreducedtransparency_reduce_cases,
media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, CachedViewportSegments) {
ScopedViewportSegmentsForTest scoped_feature(true);
MediaValuesCached::MediaValuesCachedData data;
{
data.horizontal_viewport_segments = 1;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_single_horizontal_viewport_segment_cases,
media_query_evaluator);
}
{
data.horizontal_viewport_segments = 2;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_double_horizontal_viewport_segment_cases,
media_query_evaluator);
}
{
data.vertical_viewport_segments = 1;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_single_vertical_viewport_segment_cases,
media_query_evaluator);
}
{
data.vertical_viewport_segments = 2;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_double_vertical_viewport_segment_cases,
media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, CachedDevicePosture) {
ScopedDevicePostureForTest scoped_feature(true);
MediaValuesCached::MediaValuesCachedData data;
{
data.device_posture = mojom::blink::DevicePostureType::kContinuous;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_device_posture_none_cases, media_query_evaluator);
}
{
data.device_posture = mojom::blink::DevicePostureType::kFolded;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_device_posture_folded_cases, media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, CachedDynamicRange) {
MediaValuesCached::MediaValuesCachedData data;
// Test with color spaces supporting standard dynamic range
{
data.device_supports_hdr = gfx::DisplayColorSpaces().SupportsHDR();
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_dynamic_range_standard_cases, media_query_evaluator);
TestMQEvaluator(g_video_dynamic_range_standard_cases,
media_query_evaluator);
// Test again with the feature disabled
ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
false};
TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
media_query_evaluator);
}
{
data.device_supports_hdr =
gfx::DisplayColorSpaces(gfx::ColorSpace::CreateDisplayP3D65())
.SupportsHDR();
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_dynamic_range_standard_cases, media_query_evaluator);
TestMQEvaluator(g_video_dynamic_range_standard_cases,
media_query_evaluator);
// Test again with the feature disabled
ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
false};
TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
media_query_evaluator);
}
// Test with color spaces supporting high dynamic range
{
data.device_supports_hdr =
gfx::DisplayColorSpaces(gfx::ColorSpace::CreateExtendedSRGB())
.SupportsHDR();
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_dynamic_range_high_cases, media_query_evaluator);
TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
// Test again with the feature disabled
ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
false};
TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
media_query_evaluator);
}
{
data.device_supports_hdr =
gfx::DisplayColorSpaces(gfx::ColorSpace::CreateSRGBLinear())
.SupportsHDR();
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_dynamic_range_high_cases, media_query_evaluator);
TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
// Test again with the feature disabled
ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
false};
TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
media_query_evaluator);
}
{
data.device_supports_hdr =
gfx::DisplayColorSpaces(gfx::ColorSpace::CreateHDR10()).SupportsHDR();
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_dynamic_range_high_cases, media_query_evaluator);
TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
// Test again with the feature disabled
ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
false};
TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
media_query_evaluator);
}
{
data.device_supports_hdr =
gfx::DisplayColorSpaces(gfx::ColorSpace::CreateHLG()).SupportsHDR();
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_dynamic_range_high_cases, media_query_evaluator);
TestMQEvaluator(g_video_dynamic_range_high_cases, media_query_evaluator);
// Test again with the feature disabled
ScopedCSSVideoDynamicRangeMediaQueriesForTest const disable_video_feature{
false};
TestMQEvaluator(g_video_dynamic_range_feature_disabled_cases,
media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, CachedInvertedColors) {
MediaValuesCached::MediaValuesCachedData data;
// inverted-colors - none
{
data.inverted_colors = false;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_invertedcolors_none_cases, media_query_evaluator);
}
// inverted-colors - inverted
{
data.inverted_colors = true;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_invertedcolors_inverted_cases, media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, CachedScripting) {
MediaValuesCached::MediaValuesCachedData data;
// scripting - none
{
data.scripting = Scripting::kNone;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_scripting_none_cases, media_query_evaluator);
}
// scripting - initial-only
{
data.scripting = Scripting::kInitialOnly;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_scripting_initial_only_cases, media_query_evaluator);
}
// scripting - enabled
{
data.scripting = Scripting::kEnabled;
MediaValues* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
TestMQEvaluator(g_scripting_enabled_cases, media_query_evaluator);
}
}
TEST(MediaQueryEvaluatorTest, RangedValues) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 500;
data.viewport_height = 250;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
auto eval = [&media_query_evaluator](MediaQueryExp exp) {
const auto* feature = MakeGarbageCollected<MediaQueryFeatureExpNode>(exp);
return media_query_evaluator.Eval(*feature) == KleeneValue::kTrue;
};
// (width < 600px)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(600), MediaQueryOperator::kLt)))));
// (width < 501px)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(501), MediaQueryOperator::kLt)))));
// (width < 500px)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(500), MediaQueryOperator::kLt)))));
// (width > 500px)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(500), MediaQueryOperator::kGt)))));
// (width < 501px)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(501), MediaQueryOperator::kLt)))));
// (width <= 500px)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(500), MediaQueryOperator::kLe)))));
// (400px < width)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kLt),
MediaQueryExpComparison()))));
// (600px < width)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(600), MediaQueryOperator::kLt),
MediaQueryExpComparison()))));
// (400px > width)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kGt),
MediaQueryExpComparison()))));
// (600px > width)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(600), MediaQueryOperator::kGt),
MediaQueryExpComparison()))));
// (400px <= width)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kLe),
MediaQueryExpComparison()))));
// (600px <= width)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(600), MediaQueryOperator::kLe),
MediaQueryExpComparison()))));
// (400px >= width)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kGe),
MediaQueryExpComparison()))));
// (600px >= width)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(600), MediaQueryOperator::kGe),
MediaQueryExpComparison()))));
// (width = 500px)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(500), MediaQueryOperator::kEq)))));
// (width = 400px)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kEq)))));
// (500px = width)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(500), MediaQueryOperator::kEq),
MediaQueryExpComparison()))));
// (400px = width)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kEq),
MediaQueryExpComparison()))));
// (400px < width < 600px)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"width",
MediaQueryExpBounds(
MediaQueryExpComparison(PxValue(400), MediaQueryOperator::kLt),
MediaQueryExpComparison(PxValue(600), MediaQueryOperator::kLt)))));
// (550px < width < 600px)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width",
MediaQueryExpBounds(
MediaQueryExpComparison(PxValue(550), MediaQueryOperator::kLt),
MediaQueryExpComparison(PxValue(600), MediaQueryOperator::kLt)))));
// (400px < width < 450px)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"width",
MediaQueryExpBounds(
MediaQueryExpComparison(PxValue(400), MediaQueryOperator::kLt),
MediaQueryExpComparison(PxValue(450), MediaQueryOperator::kLt)))));
// (aspect-ratio = 2/1)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"aspect-ratio", MediaQueryExpBounds(MediaQueryExpComparison(
RatioValue(2, 1), MediaQueryOperator::kEq)))));
// (aspect-ratio = 3/1)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"aspect-ratio", MediaQueryExpBounds(MediaQueryExpComparison(
RatioValue(3, 1), MediaQueryOperator::kEq)))));
// (aspect-ratio < 1/1)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"aspect-ratio", MediaQueryExpBounds(MediaQueryExpComparison(
RatioValue(1, 1), MediaQueryOperator::kLt)))));
// (aspect-ratio < 3/1)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"aspect-ratio", MediaQueryExpBounds(MediaQueryExpComparison(
RatioValue(3, 1), MediaQueryOperator::kLt)))));
// (aspect-ratio > 1/1)
EXPECT_TRUE(eval(MediaQueryExp::Create(
"aspect-ratio", MediaQueryExpBounds(MediaQueryExpComparison(
RatioValue(1, 1), MediaQueryOperator::kGt)))));
// (aspect-ratio > 3/1)
EXPECT_FALSE(eval(MediaQueryExp::Create(
"aspect-ratio", MediaQueryExpBounds(MediaQueryExpComparison(
RatioValue(3, 1), MediaQueryOperator::kGt)))));
}
TEST(MediaQueryEvaluatorTest, ExpNode) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 500;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
auto* width_lt_400 =
MakeGarbageCollected<MediaQueryFeatureExpNode>(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kLt))));
auto* width_lt_600 =
MakeGarbageCollected<MediaQueryFeatureExpNode>(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(600), MediaQueryOperator::kLt))));
auto* width_lt_800 =
MakeGarbageCollected<MediaQueryFeatureExpNode>(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(800), MediaQueryOperator::kLt))));
EXPECT_EQ(KleeneValue::kTrue, media_query_evaluator.Eval(*width_lt_600));
EXPECT_EQ(KleeneValue::kFalse, media_query_evaluator.Eval(*width_lt_400));
EXPECT_EQ(KleeneValue::kTrue,
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryNestedExpNode>(width_lt_600)));
EXPECT_EQ(KleeneValue::kFalse,
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryNestedExpNode>(width_lt_400)));
EXPECT_EQ(KleeneValue::kFalse,
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryNotExpNode>(width_lt_600)));
EXPECT_EQ(KleeneValue::kTrue,
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryNotExpNode>(width_lt_400)));
EXPECT_EQ(KleeneValue::kTrue, media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryAndExpNode>(
width_lt_600, width_lt_800)));
EXPECT_EQ(
KleeneValue::kFalse,
media_query_evaluator.Eval(*MakeGarbageCollected<MediaQueryAndExpNode>(
width_lt_600, width_lt_400)));
EXPECT_EQ(KleeneValue::kTrue, media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryOrExpNode>(
width_lt_600, width_lt_400)));
EXPECT_EQ(
KleeneValue::kFalse,
media_query_evaluator.Eval(*MakeGarbageCollected<MediaQueryOrExpNode>(
width_lt_400,
MakeGarbageCollected<MediaQueryNotExpNode>(width_lt_800))));
}
TEST(MediaQueryEvaluatorTest, DependentResults) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 300;
data.device_width = 400;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
// Viewport-dependent:
auto* width_lt_400 =
MakeGarbageCollected<MediaQueryFeatureExpNode>(MediaQueryExp::Create(
"width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(400), MediaQueryOperator::kLt))));
// Device-dependent:
auto* device_width_lt_600 =
MakeGarbageCollected<MediaQueryFeatureExpNode>(MediaQueryExp::Create(
"device-width", MediaQueryExpBounds(MediaQueryExpComparison(
PxValue(600), MediaQueryOperator::kLt))));
// Neither viewport- nor device-dependent:
auto* color =
MakeGarbageCollected<MediaQueryFeatureExpNode>(MediaQueryExp::Create(
"color",
MediaQueryExpBounds(MediaQueryExpComparison(MediaQueryExpValue()))));
// "(color)" should not be dependent on anything.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(*color, &result_flags);
EXPECT_FALSE(result_flags.is_viewport_dependent);
EXPECT_FALSE(result_flags.is_device_dependent);
}
// "(width < 400px)" should be viewport-dependent.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(*width_lt_400, &result_flags);
EXPECT_TRUE(result_flags.is_viewport_dependent);
EXPECT_FALSE(result_flags.is_device_dependent);
}
// "(device-width < 600px)" should be device-dependent.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(*device_width_lt_600, &result_flags);
EXPECT_TRUE(result_flags.is_device_dependent);
EXPECT_FALSE(result_flags.is_viewport_dependent);
}
// "((device-width < 600px))" should be device-dependent.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryNestedExpNode>(device_width_lt_600),
&result_flags);
EXPECT_FALSE(result_flags.is_viewport_dependent);
EXPECT_TRUE(result_flags.is_device_dependent);
}
// "not (device-width < 600px)" should be device-dependent.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryNotExpNode>(device_width_lt_600),
&result_flags);
EXPECT_FALSE(result_flags.is_viewport_dependent);
EXPECT_TRUE(result_flags.is_device_dependent);
}
// "(width < 400px) and (device-width < 600px)" should be both viewport- and
// device-dependent.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(*MakeGarbageCollected<MediaQueryAndExpNode>(
width_lt_400, device_width_lt_600),
&result_flags);
EXPECT_TRUE(result_flags.is_viewport_dependent);
EXPECT_TRUE(result_flags.is_device_dependent);
}
// "not (width < 400px) and (device-width < 600px)" should be
// viewport-dependent only.
//
// Note that the evaluation short-circuits on the first condition, making the
// the second condition irrelevant.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryAndExpNode>(
MakeGarbageCollected<MediaQueryNotExpNode>(width_lt_400),
device_width_lt_600),
&result_flags);
EXPECT_TRUE(result_flags.is_viewport_dependent);
EXPECT_FALSE(result_flags.is_device_dependent);
}
// "(width < 400px) or (device-width < 600px)" should be viewport-dependent
// only.
//
// Note that the evaluation short-circuits on the first condition, making the
// the second condition irrelevant.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(*MakeGarbageCollected<MediaQueryOrExpNode>(
width_lt_400, device_width_lt_600),
&result_flags);
EXPECT_TRUE(result_flags.is_viewport_dependent);
EXPECT_FALSE(result_flags.is_device_dependent);
}
// "not (width < 400px) or (device-width < 600px)" should be both viewport-
// and device-dependent.
{
MediaQueryResultFlags result_flags;
media_query_evaluator.Eval(
*MakeGarbageCollected<MediaQueryOrExpNode>(
MakeGarbageCollected<MediaQueryNotExpNode>(width_lt_400),
device_width_lt_600),
&result_flags);
EXPECT_TRUE(result_flags.is_viewport_dependent);
EXPECT_TRUE(result_flags.is_device_dependent);
}
}
TEST(MediaQueryEvaluatorTest, CSSMediaQueries4) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 500;
data.viewport_height = 500;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
MediaQueryEvaluatorTestCase test_cases[] = {
{"(width: 1px) or (width: 2px)", false},
{"(width: 1px) or (width: 2px) or (width: 3px)", false},
{"(width: 500px) or (width: 2px) or (width: 3px)", true},
{"(width: 1px) or (width: 500px) or (width: 3px)", true},
{"(width: 1px) or (width: 2px) or (width: 500px)", true},
{"((width: 1px))", false},
{"((width: 500px))", true},
{"(((width: 500px)))", true},
{"((width: 1px) or (width: 2px)) or (width: 3px)", false},
{"(width: 1px) or ((width: 2px) or (width: 500px))", true},
{"(width = 500px)", true},
{"(width >= 500px)", true},
{"(width <= 500px)", true},
{"(width < 500px)", false},
{"(500px = width)", true},
{"(500px >= width)", true},
{"(500px <= width)", true},
{"(499px < width)", true},
{"(499px > width)", false},
{"(499px < width < 501px)", true},
{"(499px < width <= 500px)", true},
{"(499px < width < 500px)", false},
{"(500px < width < 501px)", false},
{"(501px > width > 499px)", true},
{"(500px >= width > 499px)", true},
{"(501px > width >= 500px)", true},
{"(502px > width >= 501px)", false},
{"not (499px > width)", true},
{"(not (499px > width))", true},
{"(width >= 500px) and (not (499px > width))", true},
{"(width >= 500px) and ((499px > width) or (not (width = 500px)))",
false},
{nullptr, false} // Do not remove the terminator line.
};
TestMQEvaluator(test_cases, media_query_evaluator);
}
TEST(MediaQueryEvaluatorTest, GeneralEnclosed) {
MediaValuesCached::MediaValuesCachedData data;
data.viewport_width = 500;
data.viewport_height = 500;
auto* media_values = MakeGarbageCollected<MediaValuesCached>(data);
MediaQueryEvaluator media_query_evaluator(media_values);
MediaQueryEvaluatorTestCase tests[] = {
{"(unknown)", false},
{"((unknown: 1px))", false},
{"not (unknown: 1px)", false},
{"(width) or (unknown: 1px)", true},
{"(unknown: 1px) or (width)", true},
{"(width: 42px) or (unknown: 1px)", false},
{"(unknown: 1px) or (width: 42px)", false},
{"not ((width: 42px) or (unknown: 1px))", false},
{"not ((unknown: 1px) or (width: 42px))", false},
{"not ((width) or (unknown: 1px))", false},
{"not ((unknown: 1px) or (width))", false},
{"(width) and (unknown: 1px)", false},
{"(unknown: 1px) and (width)", false},
{"(width: 42px) and (unknown: 1px)", false},
{"(unknown: 1px) and (width: 42px)", false},
{"not ((width: 42px) and (unknown: 1px))", true},
{"not ((unknown: 1px) and (width: 42px))", true},
{"not ((width) and (unknown: 1px))", false},
{"not ((unknown: 1px) and (width))", false},
};
for (const MediaQueryEvaluatorTestCase& test : tests) {
SCOPED_TRACE(String(test.input));
String input(test.input);
MediaQuerySet* query_set =
MediaQueryParser::ParseMediaQuerySet(input, nullptr);
ASSERT_TRUE(query_set);
EXPECT_EQ(test.output, media_query_evaluator.Eval(*query_set));
}
}
class MediaQueryEvaluatorIdentifiabilityTest : public PageTestBase {
public:
MediaQueryEvaluatorIdentifiabilityTest()
: counts_{.response_for_is_active = true,
.response_for_is_anything_blocked = false,
.response_for_is_allowed = true} {
IdentifiabilityStudySettings::SetGlobalProvider(
std::make_unique<CountingSettingsProvider>(&counts_));
}
~MediaQueryEvaluatorIdentifiabilityTest() override {
IdentifiabilityStudySettings::ResetStateForTesting();
}
test::ScopedIdentifiabilityTestSampleCollector* collector() {
return &collector_;
}
protected:
CallCounts counts_;
test::ScopedIdentifiabilityTestSampleCollector collector_;
void UpdateAllLifecyclePhases() {
GetDocument().View()->UpdateAllLifecyclePhasesForTest();
}
};
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfacePrefersReducedMotion) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media (prefers-reduced-motion: reduce) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(static_cast<int>(
IdentifiableSurface::MediaFeatureName::kPrefersReducedMotion)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(
entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kPrefersReducedMotion)));
EXPECT_EQ(entry.metrics.begin()->value, IdentifiableToken(false));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfacePrefersReducedTransparency) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media (prefers-reduced-transparency: reduce) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(static_cast<int>(
IdentifiableSurface::MediaFeatureName::kPrefersReducedTransparency)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(IdentifiableSurface::MediaFeatureName::
kPrefersReducedTransparency)));
EXPECT_EQ(entry.metrics.begin()->value, IdentifiableToken(false));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceOrientation) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media (orientation: landscape) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kOrientation)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kOrientation)));
EXPECT_EQ(entry.metrics.begin()->value,
IdentifiableToken(CSSValueID::kLandscape));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceCollectOnce) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media (orientation: landscape) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
// Recompute layout twice but expect only one sample.
UpdateAllLifecyclePhases();
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kOrientation)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kOrientation)));
EXPECT_EQ(entry.metrics.begin()->value,
IdentifiableToken(CSSValueID::kLandscape));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceDisplayMode) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media all and (display-mode: browser) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kDisplayMode)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kDisplayMode)));
EXPECT_EQ(entry.metrics.begin()->value,
IdentifiableToken(blink::mojom::DisplayMode::kBrowser));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceDisplayState) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media all and (display-state: normal) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kDisplayState)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kDisplayState)));
EXPECT_EQ(entry.metrics.begin()->value,
IdentifiableToken(ui::SHOW_STATE_DEFAULT));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceResizable) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media all and (resizable: true) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kResizable)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kResizable)));
EXPECT_EQ(entry.metrics.begin()->value, IdentifiableToken(true));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceForcedColorsHover) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media all and (forced-colors: active) {
div { color: green }
}
</style>
<style>
@media all and (hover: hover) {
div { color: red }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kForcedColors)));
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kHover)));
EXPECT_EQ(collector()->entries().size(), 2u);
auto& entry_forced_colors = collector()->entries().front();
EXPECT_EQ(entry_forced_colors.metrics.size(), 1u);
EXPECT_EQ(entry_forced_colors.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kForcedColors)));
EXPECT_EQ(entry_forced_colors.metrics.begin()->value,
IdentifiableToken(ForcedColors::kNone));
auto& entry_hover = collector()->entries().back();
EXPECT_EQ(entry_hover.metrics.size(), 1u);
EXPECT_EQ(
entry_hover.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(IdentifiableSurface::MediaFeatureName::kHover)));
EXPECT_EQ(entry_hover.metrics.begin()->value,
IdentifiableToken(mojom::blink::HoverType::kHoverNone));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceAspectRatioNormalized) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media all and (min-aspect-ratio: 8/5) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(static_cast<int>(
IdentifiableSurface::MediaFeatureName::kAspectRatioNormalized)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(
entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kAspectRatioNormalized)));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceResolution) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media all and (min-resolution: 72dpi) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kResolution)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kResolution)));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceInvertedColors) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media (inverted-colors: inverted) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(static_cast<int>(
IdentifiableSurface::MediaFeatureName::kInvertedColors)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kInvertedColors)));
EXPECT_EQ(entry.metrics.begin()->value, IdentifiableToken(false));
}
TEST_F(MediaQueryEvaluatorIdentifiabilityTest,
MediaFeatureIdentifiableSurfaceScripting) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@media (scripting: enabled) {
div { color: green }
}
</style>
<div id="green"></div>
<span></span>
)HTML");
UpdateAllLifecyclePhases();
EXPECT_TRUE(GetDocument().WasMediaFeatureEvaluated(
static_cast<int>(IdentifiableSurface::MediaFeatureName::kScripting)));
EXPECT_EQ(collector()->entries().size(), 1u);
auto& entry = collector()->entries().front();
EXPECT_EQ(entry.metrics.size(), 1u);
EXPECT_EQ(entry.metrics.begin()->surface,
IdentifiableSurface::FromTypeAndToken(
IdentifiableSurface::Type::kMediaFeature,
IdentifiableToken(
IdentifiableSurface::MediaFeatureName::kScripting)));
EXPECT_EQ(entry.metrics.begin()->value, IdentifiableToken(Scripting::kNone));
}
} // namespace blink