Skip to content

Commit

Permalink
chore: move channel events into impl classes (#2793)
Browse files Browse the repository at this point in the history
  • Loading branch information
mxschmitt authored Dec 9, 2023
1 parent ac61942 commit 0ebacd6
Show file tree
Hide file tree
Showing 22 changed files with 368 additions and 506 deletions.
12 changes: 11 additions & 1 deletion src/Playwright/Core/Browser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Playwright.Transport;
using Microsoft.Playwright.Transport.Channels;
Expand All @@ -44,7 +45,6 @@ internal Browser(IChannelOwner parent, string guid, BrowserInitializer initializ
{
Channel = new(guid, parent.Connection, this);
IsConnected = true;
Channel.Closed += (_, _) => DidClose();
_initializer = initializer;
}

Expand All @@ -66,6 +66,16 @@ internal Browser(IChannelOwner parent, string guid, BrowserInitializer initializ

public IBrowserType BrowserType => _browserType;

internal override void OnMessage(string method, JsonElement? serverParams)
{
switch (method)
{
case "close":
DidClose();
break;
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
public async Task CloseAsync(BrowserCloseOptions options = default)
{
Expand Down
190 changes: 112 additions & 78 deletions src/Playwright/Core/BrowserContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,83 +60,6 @@ internal BrowserContext(IChannelOwner parent, string guid, BrowserContextInitial
_browser = parent as Browser;
_browser?._contexts.Add(this);
Channel = new(guid, parent.Connection, this);
Channel.Close += (_, _) => OnClose();
Channel.Console += (_, consoleMessageEvent) =>
{
var consoleMessage = new ConsoleMessage(consoleMessageEvent);
_consoleImpl?.Invoke(this, consoleMessage);
if (consoleMessage.Page != null)
{
(consoleMessage.Page as Page).FireConsole(consoleMessage);
}
};
Channel.Dialog += (_, dialog) =>
{
bool hasListeners = _dialogImpl?.GetInvocationList().Length > 0 || ((dialog?.Page as Page)?.HasDialogListenersAttached() ?? false);
if (!hasListeners)
{
// Although we do similar handling on the server side, we still need this logic
// on the client side due to a possible race condition between two async calls:
// a) removing "dialog" listener subscription (client->server)
// b) actual "dialog" event (server->client)
if ("beforeunload".Equals(dialog.Type, StringComparison.Ordinal))
{
dialog.AcceptAsync().IgnoreException();
}
else
{
dialog.DismissAsync().IgnoreException();
}
}
else
{
_dialogImpl?.Invoke(this, dialog);
(dialog.Page as Page)?.FireDialog(dialog);
}
};
Channel.Page += Channel_OnPage;
Channel.PageError += (_, e) =>
{
var pageObject = e.Page?.Object;
var parsedError = string.IsNullOrEmpty(e.Error.Error.Stack) ? $"{e.Error.Error.Name}: {e.Error.Error.Message}" : e.Error.Error.Stack;
WebError?.Invoke(this, new WebError(pageObject, parsedError));
pageObject?.FirePageError(parsedError);
};
Channel.BindingCall += Channel_BindingCall;
Channel.Route += Channel_Route;
Channel.RequestFailed += (_, e) =>
{
e.Request.Failure = e.FailureText;
e.Request.SetResponseEndTiming(e.ResponseEndTiming);
_requestFailedImpl?.Invoke(this, e.Request);
e.Page?.FireRequestFailed(e.Request);
e.Response?.ReportFinished(e.FailureText);
};
Channel.Request += (_, e) =>
{
_requestImpl?.Invoke(this, e.Request);
e.Page?.FireRequest(e.Request);
};
Channel.RequestFinished += (_, e) =>
{
e.Request.SetResponseEndTiming(e.ResponseEndTiming);
e.Request.Sizes = e.RequestSizes;
_requestFinishedImpl?.Invoke(this, e.Request);
e.Page?.FireRequestFinished(e.Request);
e.Response?.ReportFinished();
};
Channel.Response += (_, e) =>
{
_responseImpl?.Invoke(this, e.Response);
e.Page?.FireResponse(e.Response);
};

Channel.ServiceWorker += (_, serviceWorker) =>
{
((Worker)serviceWorker).Context = this;
_serviceWorkers.Add(serviceWorker);
ServiceWorker?.Invoke(this, serviceWorker);
};

_tracing = initializer.Tracing;
_request = initializer.RequestContext;
Expand Down Expand Up @@ -225,6 +148,117 @@ public ITracing Tracing

public IReadOnlyList<IWorker> ServiceWorkers => _serviceWorkers;

internal override void OnMessage(string method, JsonElement? serverParams)
{
switch (method)
{
case "close":
OnClose();
break;
case "bindingCall":
Channel_BindingCall(
serverParams?.GetProperty("binding").ToObject<BindingCallChannel>(_connection.DefaultJsonSerializerOptions).Object);
break;
case "dialog":
OnDialog(serverParams?.GetProperty("dialog").ToObject<DialogChannel>(_connection.DefaultJsonSerializerOptions).Object);
break;
case "console":
var consoleMessage = new ConsoleMessage(serverParams?.ToObject<BrowserContextConsoleEvent>(_connection.DefaultJsonSerializerOptions));
_consoleImpl?.Invoke(this, consoleMessage);
if (consoleMessage.Page != null)
{
(consoleMessage.Page as Page).FireConsole(consoleMessage);
}
break;
case "route":
var route = serverParams?.GetProperty("route").ToObject<RouteChannel>(_connection.DefaultJsonSerializerOptions).Object;
Channel_Route(this, route);
break;
case "page":
Channel_OnPage(
this,
serverParams?.GetProperty("page").ToObject<PageChannel>(_connection.DefaultJsonSerializerOptions));
break;
case "pageError":
{
var error = serverParams?.GetProperty("error").ToObject<SerializedError>(_connection.DefaultJsonSerializerOptions);
var pageChannel = serverParams?.GetProperty("page").ToObject<PageChannel>(_connection.DefaultJsonSerializerOptions);
var pageObject = pageChannel?.Object;
var parsedError = string.IsNullOrEmpty(error.Error.Stack) ? $"{error.Error.Name}: {error.Error.Message}" : error.Error.Stack;
WebError?.Invoke(this, new WebError(pageObject, parsedError));
pageObject?.FirePageError(parsedError);
break;
}
case "serviceWorker":
{
var serviceWorker = serverParams?.GetProperty("worker").ToObject<WorkerChannel>(_connection.DefaultJsonSerializerOptions).Object;
((Worker)serviceWorker).Context = this;
_serviceWorkers.Add(serviceWorker);
ServiceWorker?.Invoke(this, serviceWorker);
break;
}
case "request":
{
var e = serverParams?.ToObject<BrowserContextChannelRequestEventArgs>(_connection.DefaultJsonSerializerOptions);
_requestImpl?.Invoke(this, e.Request);
e.Page?.FireRequest(e.Request);
break;
}
case "requestFinished":
{
var e = serverParams?.ToObject<BrowserContextChannelRequestEventArgs>(_connection.DefaultJsonSerializerOptions);
e.Request.SetResponseEndTiming(e.ResponseEndTiming);
e.Request.Sizes = e.RequestSizes;
_requestFinishedImpl?.Invoke(this, e.Request);
e.Page?.FireRequestFinished(e.Request);
e.Response?.ReportFinished();
break;
}
case "requestFailed":
{
var e = serverParams?.ToObject<BrowserContextChannelRequestEventArgs>(_connection.DefaultJsonSerializerOptions);
e.Request.Failure = e.FailureText;
e.Request.SetResponseEndTiming(e.ResponseEndTiming);
_requestFailedImpl?.Invoke(this, e.Request);
e.Page?.FireRequestFailed(e.Request);
e.Response?.ReportFinished(e.FailureText);
}
break;
case "response":
{
var e = serverParams?.ToObject<BrowserContextChannelResponseEventArgs>(_connection.DefaultJsonSerializerOptions);
_responseImpl?.Invoke(this, e.Response);
e.Page?.FireResponse(e.Response);
}
break;
}
}

internal void OnDialog(IDialog dialog)
{
bool hasListeners = _dialogImpl?.GetInvocationList().Length > 0 || ((dialog?.Page as Page)?.HasDialogListenersAttached() ?? false);
if (!hasListeners)
{
// Although we do similar handling on the server side, we still need this logic
// on the client side due to a possible race condition between two async calls:
// a) removing "dialog" listener subscription (client->server)
// b) actual "dialog" event (server->client)
if ("beforeunload".Equals(dialog.Type, StringComparison.Ordinal))
{
dialog.AcceptAsync().IgnoreException();
}
else
{
dialog.DismissAsync().IgnoreException();
}
}
else
{
_dialogImpl?.Invoke(this, dialog);
(dialog.Page as Page)?.FireDialog(dialog);
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
public Task AddCookiesAsync(IEnumerable<Cookie> cookies) => Channel.AddCookiesAsync(cookies);

Expand Down Expand Up @@ -637,7 +671,7 @@ private void Channel_OnPage(object sender, PageChannel pageChannel)
}
}

private void Channel_BindingCall(object sender, BindingCall bindingCall)
private void Channel_BindingCall(BindingCall bindingCall)
{
if (_bindings.TryGetValue(bindingCall.Name, out var binding))
{
Expand Down
18 changes: 13 additions & 5 deletions src/Playwright/Core/CDPSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,34 @@ internal class CDPSession : ChannelOwnerBase, ICDPSession, IChannelOwner<CDPSess
public CDPSession(IChannelOwner parent, string guid) : base(parent, guid)
{
_channel = new(guid, parent.Connection, this);

_channel.CDPEvent += OnCDPEvent;
}

ChannelBase IChannelOwner.Channel => _channel;

IChannel<CDPSession> IChannelOwner<CDPSession>.Channel => _channel;

internal override void OnMessage(string method, JsonElement? serverParams)
{
switch (method)
{
case "event":
OnCDPEvent(serverParams!.Value.GetProperty("method").ToString(), serverParams.Value.GetProperty("params"));
break;
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
public Task DetachAsync() => _channel.DetachAsync();

[MethodImpl(MethodImplOptions.NoInlining)]
public Task<JsonElement?> SendAsync(string method, Dictionary<string, object>? args = null)
=> _channel.SendAsync(method, args);

private void OnCDPEvent(object sender, (string Name, JsonElement? Params) @event)
private void OnCDPEvent(string name, JsonElement? @params)
{
if (_cdpSessionEvents.TryGetValue(@event.Name, out var cdpNamedEvent))
if (_cdpSessionEvents.TryGetValue(name, out var cdpNamedEvent))
{
cdpNamedEvent.RaiseEvent(@event.Params);
cdpNamedEvent.RaiseEvent(@params);
}
}

Expand Down
11 changes: 10 additions & 1 deletion src/Playwright/Core/ElementHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ internal class ElementHandle : JSHandle, IElementHandle, IChannelOwner<ElementHa
internal ElementHandle(IChannelOwner parent, string guid, ElementHandleInitializer initializer) : base(parent, guid, initializer)
{
_channel = new(guid, parent.Connection, this);
_channel.PreviewUpdated += (_, newPreview) => Preview = newPreview;
}

ChannelBase IChannelOwner.Channel => _channel;
Expand All @@ -50,6 +49,16 @@ internal ElementHandle(IChannelOwner parent, string guid, ElementHandleInitializ

internal IChannel<ElementHandle> ElementChannel => _channel;

internal override void OnMessage(string method, JsonElement? serverParams)
{
switch (method)
{
case "previewUpdated":
Preview = serverParams.Value.GetProperty("preview").ToString();
break;
}
}

public async Task<IElementHandle> WaitForSelectorAsync(string selector, ElementHandleWaitForSelectorOptions options = default)
=> (await _channel.WaitForSelectorAsync(
selector: selector,
Expand Down
Loading

0 comments on commit 0ebacd6

Please sign in to comment.