Skip to content
Closed
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
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

Expand Down Expand Up @@ -66,8 +67,10 @@ public async Task ShouldRejectNavigationWhenBrowserCloses()
});
await Server.WaitForRequest("/one-style.css");
remote.Disconnect();

var exception = await Assert.ThrowsAsync<NavigationException>(() => navigationTask);
Assert.Contains("Navigation failed because browser has disconnected!", exception.Message);
GC.Collect();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
using PuppeteerSharp.Helpers;

namespace PuppeteerSharp.Tests.PageTests
{
Expand Down Expand Up @@ -39,7 +36,7 @@ await Page.EvaluateOnNewDocumentAsync(@"function(){
await Page.AddScriptTagAsync(new AddTagOptions
{
Content = "window.e = 10;"
}).ContinueWith(_ => Task.CompletedTask);
}).WithExceptionIgnore();
Assert.Null(await Page.EvaluateExpressionAsync("window.e"));
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/PuppeteerSharp.Tests/PageTests/Events/ErrorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ public async Task ShouldThrowWhenPageCrashes()
{
string error = null;
Page.Error += (sender, e) => error = e.Error;
var errorTask = WaitForErrorAsync();
var gotoTask = Page.GoToAsync("chrome://crash");

await WaitForError();
await errorTask;
Assert.Equal("Page crashed!", error);
}
}
Expand Down
7 changes: 4 additions & 3 deletions lib/PuppeteerSharp.Tests/PageTests/SetBypassCSPTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
using PuppeteerSharp.Helpers;

namespace PuppeteerSharp.Tests.PageTests
{
Expand All @@ -19,7 +20,7 @@ public async Task ShouldBypassCSPMetaTag()
await Page.AddScriptTagAsync(new AddTagOptions
{
Content = "window.__injected = 42;"
}).ContinueWith(_ => Task.CompletedTask);
}).WithExceptionIgnore();
Assert.Null(await Page.EvaluateExpressionAsync("window.__injected"));

// By-pass CSP and try one more time.
Expand All @@ -41,7 +42,7 @@ public async Task ShouldBypassCSPHeader()
await Page.AddScriptTagAsync(new AddTagOptions
{
Content = "window.__injected = 42;"
}).ContinueWith(_ => Task.CompletedTask);
}).WithExceptionIgnore();
Assert.Null(await Page.EvaluateExpressionAsync("window.__injected"));

// By-pass CSP and try one more time.
Expand All @@ -65,7 +66,7 @@ await Page.AddScriptTagAsync(new AddTagOptions
});
Assert.Equal(42, await Page.EvaluateExpressionAsync<int>("window.__injected"));

await Page.GoToAsync(TestConstants.CrossProcessUrl+ "/csp.html");
await Page.GoToAsync(TestConstants.CrossProcessUrl + "/csp.html");
await Page.AddScriptTagAsync(new AddTagOptions
{
Content = "window.__injected = 42;"
Expand Down
4 changes: 4 additions & 0 deletions lib/PuppeteerSharp.Tests/PuppeteerBaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ protected void Initialize()
{
Server.Reset();
HttpsServer.Reset();

TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}

void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) => throw e.Exception;

protected static Task<JToken> WaitEvent(CDPSession emitter, string eventName)
{
var completion = new TaskCompletionSource<JToken>();
Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp.Tests/PuppeteerPageBaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class PuppeteerPageBaseTest : PuppeteerBrowserContextBaseTest
public PuppeteerPageBaseTest(ITestOutputHelper output) : base(output)
{
}

protected Page Page { get; private set; }

public override async Task InitializeAsync()
Expand All @@ -23,7 +23,7 @@ public override async Task DisposeAsync()
await base.DisposeAsync();
}

protected Task WaitForError()
protected Task WaitForErrorAsync()
{
var wrapper = new TaskCompletionSource<bool>();

Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ private async Task<JSHandle> EvaluateHandleAsync(string method, object args)

if (response.ExceptionDetails != null)
{
throw new EvaluationFailedException("Evaluation failed: " +
GetExceptionMessage(response.ExceptionDetails));
throw new EvaluationFailedException("Evaluation failed: " + GetExceptionMessage(response.ExceptionDetails) + " " +
method + " " + Newtonsoft.Json.JsonConvert.SerializeObject(args));
}

return CreateJSHandle(response.Result);
Expand Down
30 changes: 16 additions & 14 deletions lib/PuppeteerSharp/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,21 +557,23 @@ public async Task SetContentAsync(string html, NavigationOptions options = null)
{
var waitUntil = options?.WaitUntil ?? new[] { WaitUntilNavigation.Load };
var timeout = options?.Timeout ?? Puppeteer.DefaultTimeout;
var watcher = new LifecycleWatcher(FrameManager, this, waitUntil, timeout);

// We rely upon the fact that document.open() will reset frame lifecycle with "init"
// lifecycle event. @see https://crrev.com/608658
await EvaluateFunctionAsync(@"html => {
document.open();
document.write(html);
document.close();
}", html);

var watcherTask = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
watcher.LifecycleTask).ConfigureAwait(false);
using (var watcher = new LifecycleWatcher(FrameManager, this, waitUntil, timeout))
{

// We rely upon the fact that document.open() will reset frame lifecycle with "init"
// lifecycle event. @see https://crrev.com/608658
await EvaluateFunctionAsync(@"html => {
document.open();
document.write(html);
document.close();
}", html);

var watcherTask = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
watcher.LifecycleTask).ConfigureAwait(false);

await watcherTask.ConfigureAwait(false);
await watcherTask.ConfigureAwait(false);
}
}

/// <summary>
Expand Down
21 changes: 18 additions & 3 deletions lib/PuppeteerSharp/FrameManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,36 @@ public async Task<Response> NavigateFrameAsync(Frame frame, string url, Navigati

using (var watcher = new LifecycleWatcher(this, frame, options?.WaitUntil, timeout))
{
watcher.name = url;
try
{
var navigateTask = NavigateAsync(Client, url, referrer, frame.Id);
var task = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
navigateTask).ConfigureAwait(false);

await task;
if (watcher.TimeoutOrTerminationTask.IsFaulted)
{
await watcher.TimeoutOrTerminationTask.ConfigureAwait(false);
}
else
{
await task.ConfigureAwait(false);
}

task = await Task.WhenAny(
watcher.TimeoutOrTerminationTask,
_ensureNewDocumentNavigation ? watcher.NewDocumentNavigationTask : watcher.SameDocumentNavigationTask
).ConfigureAwait(false);

await task;
if (watcher.TimeoutOrTerminationTask.IsFaulted)
{
await watcher.TimeoutOrTerminationTask.ConfigureAwait(false);
}
else
{
await task.ConfigureAwait(false);
}
}
catch (Exception ex)
{
Expand Down Expand Up @@ -132,7 +147,7 @@ public async Task<Response> WaitForFrameNavigationAsync(Frame frame, NavigationO
).ConfigureAwait(false);

await raceTask;

watcher.Cleanup();
return watcher.NavigationResponse;
}
}
Expand Down
72 changes: 61 additions & 11 deletions lib/PuppeteerSharp/Helpers/TaskHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ public static class TaskHelper
/// <param name="task">Task to wait for.</param>
/// <param name="milliseconds">Milliseconds timeout.</param>
/// <param name="exceptionToThrow">Optional exception to be thrown.</param>
/// <param name="cancellationToken">Cancellation token</param>
public static Task WithTimeout(
this Task task,
int milliseconds = 1_000,
Exception exceptionToThrow = null)
Exception exceptionToThrow = null,
CancellationToken cancellationToken = default)
=> task.WithTimeout(
() => throw exceptionToThrow ?? new TimeoutException($"Timeout Exceeded: {milliseconds}ms exceeded"),
milliseconds);
milliseconds,
cancellationToken);

//Recipe from https://blogs.msdn.microsoft.com/pfxteam/2012/10/05/how-do-i-cancel-non-cancelable-async-operations/
/// <summary>
Expand All @@ -33,17 +36,22 @@ public static Task WithTimeout(
/// <param name="task">Task to wait for.</param>
/// <param name="timeoutAction">Action to be executed on Timeout.</param>
/// <param name="milliseconds">Milliseconds timeout.</param>
/// <param name="cancellationToken">Cancellation token</param>
public static async Task WithTimeout(
this Task task,
Func<Task> timeoutAction,
int milliseconds = 1_000)
int milliseconds = 1_000,
CancellationToken cancellationToken = default)
{
if (await TimeoutTask(task, milliseconds))
if (await TimeoutTask(task, milliseconds) && !cancellationToken.IsCancellationRequested)
{
await timeoutAction();
}

await task;
if (!cancellationToken.IsCancellationRequested)
{
await task;
}
}

//Recipe from https://blogs.msdn.microsoft.com/pfxteam/2012/10/05/how-do-i-cancel-non-cancelable-async-operations/
Expand All @@ -54,18 +62,24 @@ public static async Task WithTimeout(
/// <param name="task">Task to wait for.</param>
/// <param name="timeoutAction">Action to be executed on Timeout.</param>
/// <param name="milliseconds">Milliseconds timeout.</param>
/// <param name="cancellationToken">Cancellation token</param>
public static async Task<T> WithTimeout<T>(
this Task<T> task,
Action timeoutAction,
int milliseconds = 1_000)
int milliseconds = 1_000,
CancellationToken cancellationToken = default)
{
if (await TimeoutTask(task, milliseconds))
if (await TimeoutTask(task, milliseconds) && !cancellationToken.IsCancellationRequested)
{
timeoutAction();
return default;
}

return await task;
if (!cancellationToken.IsCancellationRequested)
{
return await task;
}
return default;
}

//Recipe from https://blogs.msdn.microsoft.com/pfxteam/2012/10/05/how-do-i-cancel-non-cancelable-async-operations/
Expand All @@ -76,18 +90,25 @@ public static async Task<T> WithTimeout<T>(
/// <param name="task">Task to wait for.</param>
/// <param name="milliseconds">Milliseconds timeout.</param>
/// <param name="exceptionToThrow">Optional exception to be thrown.</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <typeparam name="T">Task return type.</typeparam>
public static async Task<T> WithTimeout<T>(
this Task<T> task,
int milliseconds = 1_000,
Exception exceptionToThrow = null)
Exception exceptionToThrow = null,
CancellationToken cancellationToken = default)
{
if (await TimeoutTask(task, milliseconds))
if (await TimeoutTask(task, milliseconds) && !cancellationToken.IsCancellationRequested)
{
throw exceptionToThrow ?? new TimeoutException($"Timeout Exceeded: {milliseconds}ms exceeded");
}

return await task;
if (!cancellationToken.IsCancellationRequested)
{
return await task;
}

return default;
}

private static async Task<bool> TimeoutTask(Task task, int milliseconds)
Expand All @@ -108,5 +129,34 @@ private static async Task<bool> TimeoutTask(Task task, int milliseconds)
}
return false;
}

/// <summary>
/// Observes and ignores any exception
/// </summary>
/// <returns>Awaited task.</returns>
/// <param name="task">Task.</param>
public static async Task<T> WithExceptionIgnore<T>(this Task<T> task)
{
try
{
return await task.ConfigureAwait(false);
}
catch
{
return default;
}
}

/// <summary>
/// Observes and ignores any exception
/// </summary>
/// <returns>Awaited task.</returns>
public static Task WithExceptionIgnore(this Task task)
{
task.ContinueWith(c => { var ignored = c.Exception; },
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return task;
}
}
}
Loading