From 57dd288869473a75da791ded0484770c23c7758d Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:49:17 +0300 Subject: [PATCH 1/3] [dotnet] [bidi] SetMediaFeaturesOverride command in Emulation module --- .../BiDi/Emulation/EmulationModule.cs | 11 + .../BiDi/Emulation/IEmulationModule.cs | 1 + .../Emulation/SetMediaFeaturesOverride.cs | 338 ++++++++++++++++++ .../BiDi/Emulation/EmulationTests.cs | 26 ++ 4 files changed, 376 insertions(+) create mode 100644 dotnet/src/webdriver/BiDi/Emulation/SetMediaFeaturesOverride.cs diff --git a/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs b/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs index aaf01d929b3df..9c248b3e1cd20 100644 --- a/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs +++ b/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs @@ -33,6 +33,9 @@ internal sealed class EmulationModule : Module, IEmulationModule private static readonly Command SetLocaleOverrideCommand = new( "emulation.setLocaleOverride", Default.SetLocaleOverrideParameters, Default.SetLocaleOverrideResult); + private static readonly Command SetMediaFeaturesOverrideCommand = new( + "emulation.setMediaFeaturesOverride", Default.SetMediaFeaturesOverrideParameters, Default.SetMediaFeaturesOverrideResult); + private static readonly Command SetForcedColorsModeThemeOverrideCommand = new( "emulation.setForcedColorsModeThemeOverride", Default.SetForcedColorsModeThemeOverrideParameters, Default.SetForcedColorsModeThemeOverrideResult); @@ -75,6 +78,12 @@ public async Task SetLocaleOverrideAsync(string? locale return await ExecuteAsync(SetLocaleOverrideCommand, @params, options, cancellationToken).ConfigureAwait(false); } + public async Task SetMediaFeaturesOverrideAsync(MediaFeatures? mediaFeatures, SetMediaFeaturesOverrideOptions? options = null, CancellationToken cancellationToken = default) + { + var @params = new SetMediaFeaturesOverrideParameters(mediaFeatures, options?.Contexts, options?.UserContexts); + return await ExecuteAsync(SetMediaFeaturesOverrideCommand, @params, options, cancellationToken).ConfigureAwait(false); + } + public async Task SetForcedColorsModeThemeOverrideAsync(ForcedColorsModeTheme? theme, SetForcedColorsModeThemeOverrideOptions? options = null, CancellationToken cancellationToken = default) { var @params = new SetForcedColorsModeThemeOverrideParameters(theme, options?.Contexts, options?.UserContexts); @@ -141,6 +150,8 @@ public async Task SetNetworkConditionsAsync(NetworkC [JsonSerializable(typeof(SetUserAgentOverrideResult))] [JsonSerializable(typeof(SetLocaleOverrideParameters))] [JsonSerializable(typeof(SetLocaleOverrideResult))] +[JsonSerializable(typeof(SetMediaFeaturesOverrideParameters))] +[JsonSerializable(typeof(SetMediaFeaturesOverrideResult))] [JsonSerializable(typeof(SetForcedColorsModeThemeOverrideParameters))] [JsonSerializable(typeof(SetForcedColorsModeThemeOverrideResult))] [JsonSerializable(typeof(SetScriptingEnabledParameters))] diff --git a/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs b/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs index db879ec8792cf..80881b5923092 100644 --- a/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs +++ b/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs @@ -24,6 +24,7 @@ public interface IEmulationModule Task SetForcedColorsModeThemeOverrideAsync(ForcedColorsModeTheme? theme, SetForcedColorsModeThemeOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetGeolocationOverrideAsync(GeolocationOverride? geolocationOverride, SetGeolocationOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetLocaleOverrideAsync(string? locale, SetLocaleOverrideOptions? options = null, CancellationToken cancellationToken = default); + Task SetMediaFeaturesOverrideAsync(MediaFeatures? mediaFeatures, SetMediaFeaturesOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetNetworkConditionsAsync(NetworkConditions? networkConditions, SetNetworkConditionsOptions? options = null, CancellationToken cancellationToken = default); Task SetScreenOrientationOverrideAsync(ScreenOrientation? screenOrientation, SetScreenOrientationOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetScreenSettingsOverrideAsync(ScreenArea? screenArea, SetScreenSettingsOverrideOptions? options = null, CancellationToken cancellationToken = default); diff --git a/dotnet/src/webdriver/BiDi/Emulation/SetMediaFeaturesOverride.cs b/dotnet/src/webdriver/BiDi/Emulation/SetMediaFeaturesOverride.cs new file mode 100644 index 0000000000000..e7d143ce30465 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Emulation/SetMediaFeaturesOverride.cs @@ -0,0 +1,338 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +using System.Text.Json.Serialization; +using OpenQA.Selenium.BiDi.Json.Converters; + +namespace OpenQA.Selenium.BiDi.Emulation; + +internal sealed record SetMediaFeaturesOverrideParameters( + [property: JsonIgnore(Condition = JsonIgnoreCondition.Never)] MediaFeatures? Features, + ImmutableArray? Contexts, + ImmutableArray? UserContexts) + : Parameters; + +public sealed record SetMediaFeaturesOverrideOptions : CommandOptions +{ + public ImmutableArray? Contexts { get; init; } + + public ImmutableArray? UserContexts { get; init; } +} + +public sealed record MediaFeatures +{ + [JsonPropertyName("any-hover")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? AnyHover { get; init; } + + [JsonPropertyName("any-pointer")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? AnyPointer { get; init; } + + [JsonPropertyName("color")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Color { get; init; } + + [JsonPropertyName("color-gamut")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? ColorGamut { get; init; } + + [JsonPropertyName("color-index")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? ColorIndex { get; init; } + + [JsonPropertyName("display-mode")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? DisplayMode { get; init; } + + [JsonPropertyName("dynamic-range")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? DynamicRange { get; init; } + + [JsonPropertyName("environment-blending")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? EnvironmentBlending { get; init; } + + [JsonPropertyName("forced-colors")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? ForcedColors { get; init; } + + [JsonPropertyName("grid")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Grid { get; init; } + + [JsonPropertyName("horizontal-viewport-segments")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? HorizontalViewportSegments { get; init; } + + [JsonPropertyName("hover")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Hover { get; init; } + + [JsonPropertyName("inverted-colors")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? InvertedColors { get; init; } + + [JsonPropertyName("monochrome")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Monochrome { get; init; } + + [JsonPropertyName("nav-controls")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? NavControls { get; init; } + + [JsonPropertyName("overflow-block")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? OverflowBlock { get; init; } + + [JsonPropertyName("overflow-inline")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? OverflowInline { get; init; } + + [JsonPropertyName("pointer")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Pointer { get; init; } + + [JsonPropertyName("prefers-color-scheme")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? PrefersColorScheme { get; init; } + + [JsonPropertyName("prefers-contrast")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? PrefersContrast { get; init; } + + [JsonPropertyName("prefers-reduced-data")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? PrefersReducedData { get; init; } + + [JsonPropertyName("prefers-reduced-motion")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? PrefersReducedMotion { get; init; } + + [JsonPropertyName("prefers-reduced-transparency")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? PrefersReducedTransparency { get; init; } + + [JsonPropertyName("scan")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Scan { get; init; } + + [JsonPropertyName("scripting")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Scripting { get; init; } + + [JsonPropertyName("update")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? Update { get; init; } + + [JsonPropertyName("vertical-viewport-segments")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? VerticalViewportSegments { get; init; } + + [JsonPropertyName("video-color-gamut")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? VideoColorGamut { get; init; } + + [JsonPropertyName("video-dynamic-range")] + [JsonConverter(typeof(OptionalConverter))] + public Optional? VideoDynamicRange { get; init; } +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum AnyHover +{ + None, + Hover +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum AnyPointer +{ + None, + Coarse, + Fine +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum ColorGamut +{ + Srgb, + P3, + Rec2020 +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum DisplayMode +{ + Fullscreen, + Standalone, + MinimalUi, + Browser, + PictureInPicture +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum DynamicRange +{ + Standard, + High +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum EnvironmentBlending +{ + Opaque, + Additive, + Subtractive +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum ForcedColors +{ + None, + Active +} + +public enum Grid +{ + Zero = 0, + One = 1 +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum Hover +{ + None, + Hover +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum InvertedColors +{ + None, + Inverted +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum NavControls +{ + None, + Back +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum OverflowBlock +{ + None, + Scroll, + OptionalPaged, + Paged +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum OverflowInline +{ + None, + Scroll +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum Pointer +{ + None, + Coarse, + Fine +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum PrefersColorScheme +{ + Light, + Dark +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum PrefersContrast +{ + NoPreference, + More, + Less, + Custom +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum PrefersReducedData +{ + NoPreference, + Reduce +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum PrefersReducedMotion +{ + NoPreference, + Reduce +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum PrefersReducedTransparency +{ + NoPreference, + Reduce +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum Scan +{ + Interlace, + Progressive +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum Scripting +{ + None, + InitialOnly, + Enabled +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum Update +{ + None, + Slow, + Fast +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum VideoColorGamut +{ + Srgb, + P3, + Rec2020 +} + +[JsonConverter(typeof(KebabCaseEnumConverter))] +public enum VideoDynamicRange +{ + Standard, + High +} + +public sealed record SetMediaFeaturesOverrideResult : EmptyResult; diff --git a/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs b/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs index 4a682f8d887d1..600a26f0bcd4e 100644 --- a/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs +++ b/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs @@ -83,6 +83,32 @@ public void CanSetLocaleOverrideToDefault() Throws.Nothing); } + [Test] + public void CanSetMediaFeaturesOverride() + { + Assert.That(async () => + { + await bidi.Emulation.SetMediaFeaturesOverrideAsync(new MediaFeatures + { + AnyHover = AnyHover.None, + PrefersColorScheme = PrefersColorScheme.Dark, + Color = 8, + ColorGamut = ColorGamut.Rec2020 + }, new() { Contexts = [context] }); + }, + Throws.Nothing); + } + + [Test] + public void CanSetMediaFeaturesOverrideToDefault() + { + Assert.That(async () => + { + await bidi.Emulation.SetMediaFeaturesOverrideAsync(null, new() { Contexts = [context] }); + }, + Throws.Nothing); + } + [Test] [IgnoreBrowser(Infrastructure.Browser.Chrome, "Not supported yet?")] [IgnoreBrowser(Infrastructure.Browser.Edge, "Not supported yet?")] From a21a4c4b39e613fe294d4bacaa3a7828085bae4f Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:31:34 +0300 Subject: [PATCH 2/3] Ignore tests --- dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs b/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs index 600a26f0bcd4e..fd7127f88cb80 100644 --- a/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs +++ b/dotnet/test/webdriver/BiDi/Emulation/EmulationTests.cs @@ -84,6 +84,9 @@ public void CanSetLocaleOverrideToDefault() } [Test] + [IgnoreBrowser(Infrastructure.Browser.Chrome, "Not supported yet?")] + [IgnoreBrowser(Infrastructure.Browser.Edge, "Not supported yet?")] + [IgnoreBrowser(Infrastructure.Browser.Firefox, "Not supported yet?")] public void CanSetMediaFeaturesOverride() { Assert.That(async () => @@ -100,6 +103,9 @@ await bidi.Emulation.SetMediaFeaturesOverrideAsync(new MediaFeatures } [Test] + [IgnoreBrowser(Infrastructure.Browser.Chrome, "Not supported yet?")] + [IgnoreBrowser(Infrastructure.Browser.Edge, "Not supported yet?")] + [IgnoreBrowser(Infrastructure.Browser.Firefox, "Not supported yet?")] public void CanSetMediaFeaturesOverrideToDefault() { Assert.That(async () => From aabbe45a248d967dbef4a6662f9f93d44d725e49 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:37:39 +0300 Subject: [PATCH 3/3] Rename media features arg to features --- dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs | 4 ++-- dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs b/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs index 9c248b3e1cd20..acb046bf8ff8b 100644 --- a/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs +++ b/dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs @@ -78,9 +78,9 @@ public async Task SetLocaleOverrideAsync(string? locale return await ExecuteAsync(SetLocaleOverrideCommand, @params, options, cancellationToken).ConfigureAwait(false); } - public async Task SetMediaFeaturesOverrideAsync(MediaFeatures? mediaFeatures, SetMediaFeaturesOverrideOptions? options = null, CancellationToken cancellationToken = default) + public async Task SetMediaFeaturesOverrideAsync(MediaFeatures? features, SetMediaFeaturesOverrideOptions? options = null, CancellationToken cancellationToken = default) { - var @params = new SetMediaFeaturesOverrideParameters(mediaFeatures, options?.Contexts, options?.UserContexts); + var @params = new SetMediaFeaturesOverrideParameters(features, options?.Contexts, options?.UserContexts); return await ExecuteAsync(SetMediaFeaturesOverrideCommand, @params, options, cancellationToken).ConfigureAwait(false); } diff --git a/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs b/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs index 80881b5923092..ffc94143dbfff 100644 --- a/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs +++ b/dotnet/src/webdriver/BiDi/Emulation/IEmulationModule.cs @@ -24,7 +24,7 @@ public interface IEmulationModule Task SetForcedColorsModeThemeOverrideAsync(ForcedColorsModeTheme? theme, SetForcedColorsModeThemeOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetGeolocationOverrideAsync(GeolocationOverride? geolocationOverride, SetGeolocationOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetLocaleOverrideAsync(string? locale, SetLocaleOverrideOptions? options = null, CancellationToken cancellationToken = default); - Task SetMediaFeaturesOverrideAsync(MediaFeatures? mediaFeatures, SetMediaFeaturesOverrideOptions? options = null, CancellationToken cancellationToken = default); + Task SetMediaFeaturesOverrideAsync(MediaFeatures? features, SetMediaFeaturesOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetNetworkConditionsAsync(NetworkConditions? networkConditions, SetNetworkConditionsOptions? options = null, CancellationToken cancellationToken = default); Task SetScreenOrientationOverrideAsync(ScreenOrientation? screenOrientation, SetScreenOrientationOverrideOptions? options = null, CancellationToken cancellationToken = default); Task SetScreenSettingsOverrideAsync(ScreenArea? screenArea, SetScreenSettingsOverrideOptions? options = null, CancellationToken cancellationToken = default);