Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions dotnet/src/webdriver/BiDi/BiDi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public async ValueTask DisposeAsync()

public T AsModule<T>() where T : Module, new()
{
return (T)_modules.GetOrAdd(typeof(T), _ => Module.Create<T>(this, Broker, GetJsonOptions()));
return (T)_modules.GetOrAdd(typeof(T), _ => Module.Create<T>(this, Broker));
}

private Broker Broker { get; }
Expand All @@ -94,25 +94,25 @@ private JsonSerializerOptions GetJsonOptions()
{
return new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
//PropertyNameCaseInsensitive = true,
//PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
//DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,

// BiDi returns special numbers such as "NaN" as strings
// Additionally, -0 is returned as a string "-0"
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals | JsonNumberHandling.AllowReadingFromString,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to remove runtime options completely? As I remember we implemented it as converter?..

Converters =
{
new BrowsingContextConverter(this),
new BrowserUserContextConverter(this),
new CollectorConverter(this),
new InterceptConverter(this),
new HandleConverter(this),
new InternalIdConverter(this),
new PreloadScriptConverter(this),
new RealmConverter(this),
//new BrowsingContextConverter(),
//new BrowserUserContextConverter(),
//new CollectorConverter(),
//new InterceptConverter(),
//new HandleConverter(),
//new InternalIdConverter(),
//new PreloadScriptConverter(),
//new RealmConverter(),
new DateTimeOffsetConverter(),
new WebExtensionConverter(this),
//new WebExtensionConverter(),
}
};
}
Expand Down
10 changes: 10 additions & 0 deletions dotnet/src/webdriver/BiDi/Broker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ private void ProcessReceivedMessage(byte[]? data)
var commandResult = JsonSerializer.Deserialize(ref resultReader, command.JsonResultTypeInfo)
?? throw new JsonException("Remote end returned null command result in the 'result' property.");

if (commandResult is IBiDiHydratable bidiHydratable)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any types with properties that are BiDi hydratable? We might need to handle nested structures.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hydrated couple just to make tests green. If we like the approach, I will review all top-level CommndResults/EventArgs to include nested properties.

{
bidiHydratable.Hydrate(_bidi);
}

command.TaskCompletionSource.SetResult((EmptyResult)commandResult);
}
catch (Exception ex)
Expand All @@ -304,6 +309,11 @@ private void ProcessReceivedMessage(byte[]? data)
{
var eventArgs = (EventArgs)JsonSerializer.Deserialize(ref paramsReader, eventInfo)!;

if (eventArgs is IBiDiHydratable bidiHydratable)
{
bidiHydratable.Hydrate(_bidi);
}

var messageEvent = (method, eventArgs);
_pendingEvents.Add(messageEvent);
}
Expand Down
17 changes: 10 additions & 7 deletions dotnet/src/webdriver/BiDi/Browser/BrowserModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
// under the License.
// </copyright>

using System.Text.Json;
using OpenQA.Selenium.BiDi.Json.Converters;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace OpenQA.Selenium.BiDi.Browser;

public sealed class BrowserModule : Module
{
private BrowserJsonSerializerContext _jsonContext = null!;
private static readonly BrowserJsonSerializerContext _jsonContext = BrowserJsonSerializerContext.Default;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we choose generated options, then rename -> s_jsonContext


public async Task<CloseResult> CloseAsync(CloseOptions? options = null)
{
Expand Down Expand Up @@ -77,11 +77,6 @@ public async Task<SetDownloadBehaviorResult> SetDownloadBehaviorDeniedAsync(SetD

return await Broker.ExecuteCommandAsync(new SetDownloadBehaviorCommand(@params), options, _jsonContext.SetDownloadBehaviorCommand, _jsonContext.SetDownloadBehaviorResult).ConfigureAwait(false);
}

protected override void Initialize(JsonSerializerOptions options)
{
_jsonContext = new BrowserJsonSerializerContext(options);
}
}

[JsonSerializable(typeof(CloseCommand))]
Expand All @@ -96,4 +91,12 @@ protected override void Initialize(JsonSerializerOptions options)
[JsonSerializable(typeof(GetClientWindowsResult))]
[JsonSerializable(typeof(SetDownloadBehaviorCommand))]
[JsonSerializable(typeof(SetDownloadBehaviorResult))]

#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better [ClsCompliant(false)] I think. You’ll probably run into dotnet/roslyn#68560 though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are CLS compliant:

[assembly: System.CLSCompliant(true)]

[JsonSourceGenerationOptions(
PropertyNameCaseInsensitive = true,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are duplicating shared json options. Is it good? Or do we want to return back the construction of json context in runtime rather than generated options?

DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
Converters = [typeof(DateTimeOffsetConverter)])]
#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant
internal partial class BrowserJsonSerializerContext : JsonSerializerContext;
44 changes: 4 additions & 40 deletions dotnet/src/webdriver/BiDi/Browser/UserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,10 @@
// under the License.
// </copyright>

using System;
using System.Threading.Tasks;
using OpenQA.Selenium.BiDi.Json.Converters;
using System.Text.Json.Serialization;

namespace OpenQA.Selenium.BiDi.Browser;

public sealed class UserContext : IEquatable<UserContext>, IAsyncDisposable
{
private readonly BiDi _bidi;

internal UserContext(BiDi bidi, string id)
{
_bidi = bidi;
Id = id;
}

internal string Id { get; }

public Task RemoveAsync()
{
return _bidi.Browser.RemoveUserContextAsync(this);
}

public async ValueTask DisposeAsync()
{
await RemoveAsync().ConfigureAwait(false);
}

public bool Equals(UserContext? other)
{
return other is not null && string.Equals(Id, other.Id, StringComparison.Ordinal);
}


public override bool Equals(object? obj)
{
return Equals(obj as UserContext);
}

public override int GetHashCode()
{
return StringComparer.Ordinal.GetHashCode(Id);
}
}
[JsonConverter(typeof(BrowserUserContextConverter))]
public sealed record UserContext(string Id);
13 changes: 10 additions & 3 deletions dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@
// under the License.
// </copyright>

using OpenQA.Selenium.BiDi.Json.Converters;
using System;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace OpenQA.Selenium.BiDi.BrowsingContext;

[JsonConverter(typeof(BrowsingContextConverter))]
public sealed class BrowsingContext
{
internal BrowsingContext(BiDi bidi, string id)
internal BrowsingContext(string id)
{
BiDi = bidi;
Id = id;
}

Expand All @@ -40,8 +41,14 @@ internal BrowsingContext(BiDi bidi, string id)

internal string Id { get; }

private BiDi? _bidi;

[JsonIgnore]
public BiDi BiDi { get; }
public BiDi BiDi
{
get => _bidi ?? throw new InvalidOperationException($"{nameof(BiDi)} instance has not been hydrated.");
internal set => _bidi = value;
}

[JsonIgnore]
public BrowsingContextLogModule Log => _logModule ?? Interlocked.CompareExchange(ref _logModule, new BrowsingContextLogModule(this, BiDi.Log), null) ?? _logModule;
Expand Down
17 changes: 10 additions & 7 deletions dotnet/src/webdriver/BiDi/BrowsingContext/BrowsingContextModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@
// under the License.
// </copyright>

using OpenQA.Selenium.BiDi.Json.Converters;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace OpenQA.Selenium.BiDi.BrowsingContext;

public sealed class BrowsingContextModule : Module
{
private BrowsingContextJsonSerializerContext _jsonContext = null!;
private static readonly BrowsingContextJsonSerializerContext _jsonContext = BrowsingContextJsonSerializerContext.Default;

public async Task<CreateResult> CreateAsync(ContextType type, CreateOptions? options = null)
{
Expand Down Expand Up @@ -251,11 +251,6 @@ public async Task<Subscription> OnUserPromptClosedAsync(Action<UserPromptClosedE
{
return await Broker.SubscribeAsync("browsingContext.userPromptClosed", handler, options, _jsonContext.UserPromptClosedEventArgs).ConfigureAwait(false);
}

protected override void Initialize(JsonSerializerOptions options)
{
_jsonContext = new BrowsingContextJsonSerializerContext(options);
}
}

[JsonSerializable(typeof(ActivateCommand))]
Expand Down Expand Up @@ -292,4 +287,12 @@ protected override void Initialize(JsonSerializerOptions options)
[JsonSerializable(typeof(NavigationInfo))]
[JsonSerializable(typeof(UserPromptOpenedEventArgs))]
[JsonSerializable(typeof(UserPromptClosedEventArgs))]

#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
[JsonSourceGenerationOptions(
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
Converters = [typeof(DateTimeOffsetConverter)])]
#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant
internal partial class BrowsingContextJsonSerializerContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
Expand Up @@ -25,52 +25,58 @@ namespace OpenQA.Selenium.BiDi.BrowsingContext;

public sealed class BrowsingContextNetworkModule(BrowsingContext context, NetworkModule networkModule)
{
public async Task<Intercept> InterceptRequestAsync(Func<InterceptedRequest, Task> handler, InterceptRequestOptions? options = null)
public async Task<Interception> InterceptRequestAsync(Func<InterceptedRequest, Task> handler, InterceptRequestOptions? options = null)
{
AddInterceptOptions addInterceptOptions = new(options)
{
Contexts = [context]
};

var intercept = await networkModule.AddInterceptAsync([InterceptPhase.BeforeRequestSent], addInterceptOptions).ConfigureAwait(false);
var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.BeforeRequestSent], addInterceptOptions).ConfigureAwait(false);

await intercept.OnBeforeRequestSentAsync(
Interception interception = new(context.BiDi, interceptResult.Intercept);

await interception.OnBeforeRequestSentAsync(
async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator, req.Intercepts)),
new() { Contexts = [context] }).ConfigureAwait(false);

return intercept;
return interception;
}

public async Task<Intercept> InterceptResponseAsync(Func<InterceptedResponse, Task> handler, InterceptResponseOptions? options = null)
public async Task<Interception> InterceptResponseAsync(Func<InterceptedResponse, Task> handler, InterceptResponseOptions? options = null)
{
AddInterceptOptions addInterceptOptions = new(options)
{
Contexts = [context]
};

var intercept = await networkModule.AddInterceptAsync([InterceptPhase.ResponseStarted], addInterceptOptions).ConfigureAwait(false);
var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.ResponseStarted], addInterceptOptions).ConfigureAwait(false);

Interception interception = new(context.BiDi, interceptResult.Intercept);

await intercept.OnResponseStartedAsync(
await interception.OnResponseStartedAsync(
async res => await handler(new(res.BiDi, res.Context, res.IsBlocked, res.Navigation, res.RedirectCount, res.Request, res.Timestamp, res.Response, res.Intercepts)),
new() { Contexts = [context] }).ConfigureAwait(false);

return intercept;
return interception;
}

public async Task<Intercept> InterceptAuthAsync(Func<InterceptedAuth, Task> handler, InterceptAuthOptions? options = null)
public async Task<Interception> InterceptAuthAsync(Func<InterceptedAuth, Task> handler, InterceptAuthOptions? options = null)
{
AddInterceptOptions addInterceptOptions = new(options)
{
Contexts = [context]
};

var intercept = await networkModule.AddInterceptAsync([InterceptPhase.AuthRequired], addInterceptOptions).ConfigureAwait(false);
var interceptResult = await networkModule.AddInterceptAsync([InterceptPhase.AuthRequired], addInterceptOptions).ConfigureAwait(false);

Interception interception = new(context.BiDi, interceptResult.Intercept);

await intercept.OnAuthRequiredAsync(
await interception.OnAuthRequiredAsync(
async auth => await handler(new(auth.BiDi, auth.Context, auth.IsBlocked, auth.Navigation, auth.RedirectCount, auth.Request, auth.Timestamp, auth.Response, auth.Intercepts)),
new() { Contexts = [context] }).ConfigureAwait(false);

return intercept;
return interception;
}

public Task<SetCacheBehaviorResult> SetCacheBehaviorAsync(CacheBehavior behavior, BrowsingContextSetCacheBehaviorOptions? options = null)
Expand Down
8 changes: 7 additions & 1 deletion dotnet/src/webdriver/BiDi/BrowsingContext/CreateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ public enum ContextType
Window
}

public sealed record CreateResult(BrowsingContext Context) : EmptyResult;
public sealed record CreateResult(BrowsingContext Context) : EmptyResult, IBiDiHydratable
{
void IBiDiHydratable.Hydrate(BiDi bidi)
{
Context.BiDi = bidi;
}
}
11 changes: 10 additions & 1 deletion dotnet/src/webdriver/BiDi/BrowsingContext/GetTreeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,13 @@ public sealed record BrowsingContextGetTreeOptions
public long? MaxDepth { get; set; }
}

public sealed record GetTreeResult(IReadOnlyList<BrowsingContextInfo> Contexts) : EmptyResult;
public sealed record GetTreeResult(IReadOnlyList<BrowsingContextInfo> Contexts) : EmptyResult, IBiDiHydratable
{
void IBiDiHydratable.Hydrate(BiDi bidi)
{
foreach (var contextInfo in Contexts)
{
contextInfo.Context.BiDi = bidi;
}
}
}
17 changes: 10 additions & 7 deletions dotnet/src/webdriver/BiDi/Emulation/EmulationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
// under the License.
// </copyright>

using System.Text.Json;
using OpenQA.Selenium.BiDi.Json.Converters;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace OpenQA.Selenium.BiDi.Emulation;

public sealed class EmulationModule : Module
{
private EmulationJsonSerializerContext _jsonContext = null!;
private static readonly EmulationJsonSerializerContext _jsonContext = EmulationJsonSerializerContext.Default;

public async Task<SetTimezoneOverrideResult> SetTimezoneOverrideAsync(string? timezone, SetTimezoneOverrideOptions? options = null)
{
Expand Down Expand Up @@ -91,11 +91,6 @@ public async Task<SetGeolocationOverrideResult> SetGeolocationPositionErrorOverr

return await Broker.ExecuteCommandAsync(new SetGeolocationOverrideCommand(@params), options, _jsonContext.SetGeolocationOverrideCommand, _jsonContext.SetGeolocationOverrideResult).ConfigureAwait(false);
}

protected override void Initialize(JsonSerializerOptions options)
{
_jsonContext = new EmulationJsonSerializerContext(options);
}
}

[JsonSerializable(typeof(SetTimezoneOverrideCommand))]
Expand All @@ -112,4 +107,12 @@ protected override void Initialize(JsonSerializerOptions options)
[JsonSerializable(typeof(SetScreenOrientationOverrideResult))]
[JsonSerializable(typeof(SetGeolocationOverrideCommand))]
[JsonSerializable(typeof(SetGeolocationOverrideResult))]

#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
[JsonSourceGenerationOptions(
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
Converters = [typeof(DateTimeOffsetConverter)])]
#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant
internal partial class EmulationJsonSerializerContext : JsonSerializerContext;
Loading