Skip to content
14 changes: 14 additions & 0 deletions dotnet/src/webdriver/BiDi/BiDi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public sealed class BiDi : IAsyncDisposable
private Log.LogModule? _logModule;
private Storage.StorageModule? _storageModule;
private WebExtension.WebExtensionModule? _webExtensionModule;
private Emulation.EmulationModule? _emulationModule;

private readonly object _moduleLock = new();

Expand Down Expand Up @@ -164,6 +165,19 @@ public WebExtension.WebExtensionModule WebExtension
}
}

public Emulation.EmulationModule Emulation
{
get
{
if (_emulationModule is not null) return _emulationModule;
lock (_moduleLock)
{
_emulationModule ??= new Emulation.EmulationModule(_broker);
}
return _emulationModule;
}
}

public Task<Session.StatusResult> StatusAsync()
{
return SessionModule.StatusAsync();
Expand Down
1 change: 1 addition & 0 deletions dotnet/src/webdriver/BiDi/Communication/Broker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ internal Broker(BiDi bidi, Uri url)
new PreloadScriptConverter(_bidi),
new RealmConverter(_bidi),
new RealmTypeConverter(),
new ScreenOrientationTypeConverter(),
new DateTimeOffsetConverter(),
new PrintPageRangeConverter(),
new InputOriginConverter(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,12 @@ namespace OpenQA.Selenium.BiDi.Communication.Json;
[JsonSerializable(typeof(WebExtension.InstallCommand))]
[JsonSerializable(typeof(WebExtension.InstallResult))]
[JsonSerializable(typeof(WebExtension.UninstallCommand))]
[JsonSerializable(typeof(Emulation.SetTimezoneOverrideCommand))]
[JsonSerializable(typeof(Emulation.SetUserAgentOverrideCommand))]
[JsonSerializable(typeof(Emulation.SetLocaleOverrideCommand))]
[JsonSerializable(typeof(Emulation.SetForcedColorsModeThemeOverrideCommand))]
[JsonSerializable(typeof(Emulation.SetScriptingEnabledCommand))]
[JsonSerializable(typeof(Emulation.SetScreenOrientationOverrideCommand))]
[JsonSerializable(typeof(Emulation.SetGeolocationOverrideCommand))]

internal partial class BiDiJsonSerializerContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// <copyright file="ScreenOrientationTypeConverter.cs" company="Selenium Committers">
// 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.
// </copyright>

using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using OpenQA.Selenium.BiDi.Emulation;

namespace OpenQA.Selenium.BiDi.Communication.Json.Converters;

internal class ScreenOrientationTypeConverter : JsonConverter<ScreenOrientationType>
{
public override ScreenOrientationType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var str = reader.GetString();
return str switch
{
"portrait-primary" => ScreenOrientationType.PortraitPrimary,
"portrait-secondary" => ScreenOrientationType.PortraitSecondary,
"landscape-primary" => ScreenOrientationType.LandscapePrimary,
"landscape-secondary" => ScreenOrientationType.LandscapeSecondary,
_ => throw new JsonException($"Unrecognized '{str}' value of {typeof(ScreenOrientationType)}."),
};
}

public override void Write(Utf8JsonWriter writer, ScreenOrientationType value, JsonSerializerOptions options)
{
var str = value switch
{
ScreenOrientationType.PortraitPrimary => "portrait-primary",
ScreenOrientationType.PortraitSecondary => "portrait-secondary",
ScreenOrientationType.LandscapePrimary => "landscape-primary",
ScreenOrientationType.LandscapeSecondary => "landscape-secondary",
_ => throw new JsonException($"Unrecognized '{value}' value of {typeof(ScreenOrientationType)}."),
};

writer.WriteStringValue(str);
}
}
92 changes: 92 additions & 0 deletions dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// <copyright file="EmulationModule.cs" company="Selenium Committers">
// 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.
// </copyright>

using System;
using System.Threading.Tasks;
using OpenQA.Selenium.BiDi.Communication;

namespace OpenQA.Selenium.BiDi.Emulation;

public sealed class EmulationModule(Broker broker) : Module(broker)
{
public async Task<EmptyResult> SetTimezoneOverrideAsync(string? timezone, SetTimezoneOverrideOptions? options = null)
{
var @params = new SetTimezoneOverrideParameters(timezone, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetTimezoneOverrideCommand, EmptyResult>(new SetTimezoneOverrideCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetUserAgentOverrideAsync(string? userAgent, SetUserAgentOverrideOptions? options = null)
{
var @params = new SetUserAgentOverrideParameters(userAgent, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetUserAgentOverrideCommand, EmptyResult>(new SetUserAgentOverrideCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetLocaleOverrideAsync(string? locale, SetLocaleOverrideOptions? options = null)
{
var @params = new SetLocaleOverrideParameters(locale, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetLocaleOverrideCommand, EmptyResult>(new SetLocaleOverrideCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetForcedColorsModeThemeOverrideAsync(ForcedColorsModeTheme? theme, SetForcedColorsModeThemeOverrideOptions? options = null)
{
var @params = new SetForcedColorsModeThemeOverrideParameters(theme, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetForcedColorsModeThemeOverrideCommand, EmptyResult>(new SetForcedColorsModeThemeOverrideCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetScriptingEnabledAsync(bool? enabled, SetScriptingEnabledOptions? options = null)
{
var @params = new SetScriptingEnabledParameters(enabled, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetScriptingEnabledCommand, EmptyResult>(new SetScriptingEnabledCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetScreenOrientationOverrideAsync(ScreenOrientation? screenOrientation, SetScreenOrientationOverrideOptions? options = null)
{
var @params = new SetScreenOrientationOverrideParameters(screenOrientation, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetScreenOrientationOverrideCommand, EmptyResult>(new SetScreenOrientationOverrideCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetGeolocationCoordinatesOverrideAsync(double latitude, double longitude, SetGeolocationCoordinatesOverrideOptions? options = null)
{
var coordinates = new GeolocationCoordinates(latitude, longitude, options?.Accuracy, options?.Altitude, options?.AltitudeAccuracy, options?.Heading, options?.Speed);

var @params = new SetGeolocationOverrideCoordinatesParameters(coordinates, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetGeolocationOverrideCommand, EmptyResult>(new SetGeolocationOverrideCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetGeolocationCoordinatesOverrideAsync(SetGeolocationOverrideOptions? options = null)
{
var @params = new SetGeolocationOverrideCoordinatesParameters(null, options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetGeolocationOverrideCommand, EmptyResult>(new SetGeolocationOverrideCommand(@params), options).ConfigureAwait(false);
}

public async Task<EmptyResult> SetGeolocationPositionErrorOverrideAsync(SetGeolocationPositionErrorOverrideOptions? options = null)
{
var @params = new SetGeolocationOverridePositionErrorParameters(new GeolocationPositionError(), options?.Contexts, options?.UserContexts);

return await Broker.ExecuteCommandAsync<SetGeolocationOverrideCommand, EmptyResult>(new SetGeolocationOverrideCommand(@params), options).ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// <copyright file="SetForcedColorsModeThemeOverrideCommand.cs" company="Selenium Committers">
// 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.
// </copyright>

using System.Collections.Generic;
using System.Text.Json.Serialization;
using OpenQA.Selenium.BiDi.Communication;

namespace OpenQA.Selenium.BiDi.Emulation;

internal sealed class SetForcedColorsModeThemeOverrideCommand(SetForcedColorsModeThemeOverrideParameters @params)
: Command<SetForcedColorsModeThemeOverrideParameters, EmptyResult>(@params, "emulation.setForcedColorsModeThemeOverride");

internal sealed record SetForcedColorsModeThemeOverrideParameters([property: JsonIgnore(Condition = JsonIgnoreCondition.Never)] ForcedColorsModeTheme? Theme, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, IEnumerable<Browser.UserContext>? UserContexts) : Parameters;

public sealed class SetForcedColorsModeThemeOverrideOptions : CommandOptions
{
public IEnumerable<BrowsingContext.BrowsingContext>? Contexts { get; set; }

public IEnumerable<Browser.UserContext>? UserContexts { get; set; }
}

public enum ForcedColorsModeTheme
{
Light,
Dark
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// <copyright file="SetGeolocationOverrideCommand.cs" company="Selenium Committers">
// 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.
// </copyright>

using System.Collections.Generic;
using System.Text.Json.Serialization;
using OpenQA.Selenium.BiDi.Communication;

namespace OpenQA.Selenium.BiDi.Emulation;

internal sealed class SetGeolocationOverrideCommand(SetGeolocationOverrideParameters @params)
: Command<SetGeolocationOverrideParameters, EmptyResult>(@params, "emulation.setGeolocationOverride");

[JsonDerivedType(typeof(SetGeolocationOverrideCoordinatesParameters))]
[JsonDerivedType(typeof(SetGeolocationOverridePositionErrorParameters))]
internal abstract record SetGeolocationOverrideParameters(IEnumerable<BrowsingContext.BrowsingContext>? Contexts, IEnumerable<Browser.UserContext>? UserContexts) : Parameters;

internal sealed record SetGeolocationOverrideCoordinatesParameters([property: JsonIgnore(Condition = JsonIgnoreCondition.Never)] GeolocationCoordinates? Coordinates, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, IEnumerable<Browser.UserContext>? UserContexts) : SetGeolocationOverrideParameters(Contexts, UserContexts);

internal sealed record SetGeolocationOverridePositionErrorParameters(GeolocationPositionError Error, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, IEnumerable<Browser.UserContext>? UserContexts) : SetGeolocationOverrideParameters(Contexts, UserContexts);

internal sealed record GeolocationCoordinates(double Latitude, double Longitude, double? Accuracy, double? Altitude, double? AltitudeAccuracy, double? Heading, double? Speed);

internal sealed record GeolocationPositionError
{
[JsonInclude]
internal string Type { get; } = "positionUnavailable";
}

public class SetGeolocationOverrideOptions : CommandOptions
{
public IEnumerable<BrowsingContext.BrowsingContext>? Contexts { get; set; }

public IEnumerable<Browser.UserContext>? UserContexts { get; set; }
}

public sealed class SetGeolocationCoordinatesOverrideOptions : SetGeolocationOverrideOptions
{
public double? Accuracy { get; set; }
public double? Altitude { get; set; }
public double? AltitudeAccuracy { get; set; }
public double? Heading { get; set; }
public double? Speed { get; set; }
}

public sealed class SetGeolocationPositionErrorOverrideOptions : SetGeolocationOverrideOptions;
36 changes: 36 additions & 0 deletions dotnet/src/webdriver/BiDi/Emulation/SetLocaleOverrideCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// <copyright file="SetLocaleOverrideCommand.cs" company="Selenium Committers">
// 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.
// </copyright>

using System.Collections.Generic;
using System.Text.Json.Serialization;
using OpenQA.Selenium.BiDi.Communication;

namespace OpenQA.Selenium.BiDi.Emulation;

internal sealed class SetLocaleOverrideCommand(SetLocaleOverrideParameters @params)
: Command<SetLocaleOverrideParameters, EmptyResult>(@params, "emulation.setLocaleOverride");

internal sealed record SetLocaleOverrideParameters([property: JsonIgnore(Condition = JsonIgnoreCondition.Never)] string? Locale, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, IEnumerable<Browser.UserContext>? UserContexts) : Parameters;

public sealed class SetLocaleOverrideOptions : CommandOptions
{
public IEnumerable<BrowsingContext.BrowsingContext>? Contexts { get; set; }

public IEnumerable<Browser.UserContext>? UserContexts { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// <copyright file="SetScreenOrientationOverrideCommand.cs" company="Selenium Committers">
// 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.
// </copyright>

using System.Collections.Generic;
using System.Text.Json.Serialization;
using OpenQA.Selenium.BiDi.Communication;

namespace OpenQA.Selenium.BiDi.Emulation;

internal sealed class SetScreenOrientationOverrideCommand(SetScreenOrientationOverrideParameters @params)
: Command<SetScreenOrientationOverrideParameters, EmptyResult>(@params, "emulation.setScreenOrientationOverride");

internal sealed record SetScreenOrientationOverrideParameters([property: JsonIgnore(Condition = JsonIgnoreCondition.Never)] ScreenOrientation? ScreenOrientation, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, IEnumerable<Browser.UserContext>? UserContexts) : Parameters;

public sealed class SetScreenOrientationOverrideOptions : CommandOptions
{
public IEnumerable<BrowsingContext.BrowsingContext>? Contexts { get; set; }

public IEnumerable<Browser.UserContext>? UserContexts { get; set; }
}

public enum ScreenOrientationNatural
{
Portrait,
Landscape
}

public enum ScreenOrientationType
{
PortraitPrimary,
PortraitSecondary,
LandscapePrimary,
LandscapeSecondary
}

public sealed record ScreenOrientation(ScreenOrientationNatural Natural, ScreenOrientationType Type);
Loading