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
Original file line number Diff line number Diff line change
Expand Up @@ -153,36 +153,6 @@
"parameters": ["firefox"],
"expectations": ["FAIL"]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[navigation.spec] navigation Page.waitForNavigation*should work with clicking on anchor links",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[navigation.spec] navigation Page.waitForNavigation*should work when subframe issues window.stop()",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[navigation.spec] *should wait for network idle to succeed navigation*",
Expand Down Expand Up @@ -633,21 +603,6 @@
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[launcher.spec] *",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[locator.spec] *",
Expand All @@ -663,21 +618,6 @@
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[click.spec] *",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[network.spec] *",
Expand Down Expand Up @@ -858,21 +798,6 @@
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[launcher.spec] PuppeteerSharp *",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[puppeteer-sharp.spec.ts] PuppeteerSharp *",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace PuppeteerSharp.Tests.LauncherTests
{
public class BrowserDisconnectTests : PuppeteerBrowserBaseTest
{
[Test, PuppeteerTest("launcher.spec", "Launcher specs Browser.disconnect", "should reject navigation when browser closes")]
[Test, PuppeteerTest("launcher.spec", "Launcher specs Puppeteer Browser.disconnect", "should reject navigation when browser closes")]
public async Task ShouldRejectNavigationWhenBrowserCloses()
{
Server.SetRoute("/one-style.css", _ => Task.Delay(10000));
Expand All @@ -16,7 +16,7 @@ public async Task ShouldRejectNavigationWhenBrowserCloses()
var remote = await Puppeteer.ConnectAsync(new ConnectOptions
{
BrowserWSEndpoint = browser.WebSocketEndpoint,
Protocol = ((Browser)Browser).Protocol,
Protocol = ((Browser)browser).Protocol,
});
var page = await remote.NewPageAsync();
var navigationTask = page.GoToAsync(TestConstants.ServerUrl + "/one-style.html", new NavigationOptions
Expand All @@ -34,7 +34,7 @@ public async Task ShouldRejectNavigationWhenBrowserCloses()
}.Any(value => exception!.Message.Contains(value)), Is.True);
}

[Test, PuppeteerTest("launcher.spec", "Launcher specs Browser.disconnect", "should reject waitForSelector when browser closes")]
[Test, PuppeteerTest("launcher.spec", "Launcher specs Puppeteer Browser.disconnect", "should reject waitForSelector when browser closes")]
public async Task ShouldRejectWaitForSelectorWhenBrowserCloses()
{
Server.SetRoute("/empty.html", _ => Task.Delay(10000));
Expand Down
69 changes: 60 additions & 9 deletions lib/PuppeteerSharp/Bidi/BidiBrowser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,18 @@ namespace PuppeteerSharp.Bidi;
/// </summary>
public class BidiBrowser : Browser
{
private readonly LaunchOptions _options;
/// <summary>
/// Time in milliseconds for process to exit gracefully.
/// </summary>
private const int CloseTimeout = 5000;

private readonly IBrowserOptions _options;
private readonly ConcurrentDictionary<UserContext, BidiBrowserContext> _browserContexts = new();
private readonly ILogger<BidiBrowser> _logger;
private readonly BidiBrowserTarget _target;
private bool _isClosed;

private BidiBrowser(Core.Browser browserCore, LaunchOptions options, ILoggerFactory loggerFactory)
private BidiBrowser(Core.Browser browserCore, IBrowserOptions options, ILoggerFactory loggerFactory)
{
_target = new BidiBrowserTarget(this);
_options = options;
Expand All @@ -59,6 +64,9 @@ private BidiBrowser(Core.Browser browserCore, LaunchOptions options, ILoggerFact
/// <inheritdoc />
public override bool IsConnected => !BrowserCore.IsDisconnected;

/// <inheritdoc />
public override string WebSocketEndpoint => Launcher?.EndPoint != null ? Launcher.EndPoint + "/session" : null;

/// <inheritdoc />
public override ITarget Target => _target;

Expand Down Expand Up @@ -115,19 +123,51 @@ public override void Disconnect()
/// <inheritdoc />
public override async Task CloseAsync()
{
if (_isClosed)
{
return;
}

_isClosed = true;
try
{
await BrowserCore.CloseAsync().ConfigureAwait(false);
try
{
await BrowserCore.CloseAsync().ConfigureAwait(false);

if (Launcher != null)
{
// Notify process that exit is expected, but should be enforced if it
// doesn't occur within the close timeout.
var closeTimeout = TimeSpan.FromMilliseconds(CloseTimeout);
await Launcher.EnsureExitAsync(closeTimeout).ConfigureAwait(false);
}
}
finally
{
try
{
await Driver.StopAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to stop driver");
}

Detach();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to close connection");
}
finally
{
await Driver.StopAsync().ConfigureAwait(false);
_logger.LogError(ex, "Failed to close browser");

if (Launcher != null)
{
await Launcher.KillAsync().ConfigureAwait(false);
}
}

OnClosed();
}

/// <inheritdoc />
Expand Down Expand Up @@ -160,11 +200,22 @@ public override IBrowserContext[] BrowserContexts() =>
"Reliability",
"CA2000:Dispose objects before losing scope",
Justification = "We return the session, the browser needs to dispose the session")]
internal static async Task<BidiBrowser> CreateAsync(
internal static Task<BidiBrowser> CreateAsync(
BiDiDriver driver,
LaunchOptions options,
ILoggerFactory loggerFactory,
LauncherBase launcher)
=> CreateAsync(driver, (IBrowserOptions)options, loggerFactory, launcher);

[SuppressMessage(
"Reliability",
"CA2000:Dispose objects before losing scope",
Justification = "We return the session, the browser needs to dispose the session")]
internal static async Task<BidiBrowser> CreateAsync(
BiDiDriver driver,
IBrowserOptions options,
ILoggerFactory loggerFactory,
LauncherBase launcher)
{
var session = await Session.FromAsync(
driver,
Expand Down
2 changes: 1 addition & 1 deletion lib/PuppeteerSharp/Browser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public abstract class Browser : IBrowser
public event EventHandler<TargetChangedArgs> TargetDiscovered;

/// <inheritdoc/>
public string WebSocketEndpoint => Connection.Url;
public virtual string WebSocketEndpoint => Connection.Url;

/// <inheritdoc/>
public SupportedBrowser BrowserType { get; protected init; }
Expand Down
40 changes: 35 additions & 5 deletions lib/PuppeteerSharp/Launcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,43 @@ public async Task<IBrowser> ConnectAsync(ConnectOptions options)
throw new PuppeteerException("Exactly one of browserWSEndpoint or browserURL must be passed to puppeteer.connect");
}

Connection connection = null;
var browserWSEndpoint = string.IsNullOrEmpty(options.BrowserURL)
? options.BrowserWSEndpoint
: await GetWSEndpointAsync(options.BrowserURL).ConfigureAwait(false);

if (options.Protocol == ProtocolType.WebdriverBiDi)
{
return await ConnectBidiAsync(browserWSEndpoint, options).ConfigureAwait(false);
}

return await ConnectCdpAsync(browserWSEndpoint, options).ConfigureAwait(false);
}

private async Task<IBrowser> ConnectBidiAsync(string browserWSEndpoint, ConnectOptions options)
{
BiDiDriver driver = null;
try
{
var browserWSEndpoint = string.IsNullOrEmpty(options.BrowserURL)
? options.BrowserWSEndpoint
: await GetWSEndpointAsync(options.BrowserURL).ConfigureAwait(false);
driver = new BiDiDriver(TimeSpan.FromMilliseconds(options.ProtocolTimeout));
await driver.StartAsync(browserWSEndpoint).ConfigureAwait(false);
return await BidiBrowser.CreateAsync(driver, options, _loggerFactory, null).ConfigureAwait(false);
}
catch (Exception ex)
{
if (driver != null)
{
await driver.StopAsync().ConfigureAwait(false);
}

throw new ProcessException("Failed to create connection", ex);
}
}

private async Task<IBrowser> ConnectCdpAsync(string browserWSEndpoint, ConnectOptions options)
{
Connection connection = null;
try
{
connection = await Connection.Create(browserWSEndpoint, options, _loggerFactory).ConfigureAwait(false);

var version = await connection.SendAsync<BrowserGetVersionResponse>("Browser.getVersion").ConfigureAwait(false);
Expand Down Expand Up @@ -210,7 +240,7 @@ private async Task<string> GetWSEndpointAsync(string browserURL)
}
catch (Exception ex)
{
throw new PuppeteerException($"Failed to fetch browser webSocket url from {browserURL}.", ex);
throw new ProcessException($"Failed to fetch browser webSocket url from {browserURL}.", ex);
}
}

Expand Down