diff --git a/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json b/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json index 1c26431ec..72c644e21 100644 --- a/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json +++ b/lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json @@ -98,21 +98,6 @@ "FAIL" ] }, - { - "comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one", - "testIdPattern": "[navigation.spec] *should navigate to about:blank*", - "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*", diff --git a/lib/PuppeteerSharp/Bidi/BidiFrame.cs b/lib/PuppeteerSharp/Bidi/BidiFrame.cs index 10df07299..2f2b7cc75 100644 --- a/lib/PuppeteerSharp/Bidi/BidiFrame.cs +++ b/lib/PuppeteerSharp/Bidi/BidiFrame.cs @@ -23,8 +23,10 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using PuppeteerSharp.Bidi.Core; +using PuppeteerSharp.Helpers; namespace PuppeteerSharp.Bidi; @@ -102,40 +104,55 @@ public override async Task GoToAsync(string url, NavigationOptions op } /// - public override Task WaitForNavigationAsync(NavigationOptions options = null) + public override async Task WaitForNavigationAsync(NavigationOptions options = null) { - // TODO: This logic is missing tons of things. - var navigationTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var timeout = options?.Timeout ?? TimeoutSettings.NavigationTimeout; - // TODO: Async void is not safe. Refactor code. - BrowsingContext.Navigation += (sender, args) => + async Task WaitForEventNavigationAsync() { - args.Navigation.RequestCreated += async (o, eventArgs) => + // TODO: This logic is missing tons of things. + var navigationTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + // TODO: Async void is not safe. Refactor code. + BrowsingContext.Navigation += (sender, args) => navigationTcs.TrySetResult(args.Navigation); + + await navigationTcs.Task.ConfigureAwait(false); + + var waitForLoadTask = WaitForLoadAsync(options); + + // TODO: Add frame detached event. + // TODO: Add fragment, failed and aborted events. + await Task.WhenAny(waitForLoadTask).WithTimeout(timeout).ConfigureAwait(false); + + return navigationTcs.Task.Result; + } + + var waitForEventNavigationTask = WaitForEventNavigationAsync(); + var waitForNetworkIdleTask = WaitForNetworkIdleAsync(options); + + var waitForResponse = new Func>(async () => + { + await Task.WhenAll(waitForEventNavigationTask, waitForNetworkIdleTask).ConfigureAwait(false); + var navigation = waitForEventNavigationTask.Result; + var request = navigation.Request; + + if (navigation.Request == null) { - try - { - var httpRequest = await BidiHttpRequest.Requests.GetItemAsync(args.Navigation.Request) - .ConfigureAwait(false); - - if (httpRequest.Response != null) - { - navigationTcs.TrySetResult(httpRequest.Response); - return; - } - - args.Navigation.Request.Success += (o, eventArgs) => - { - navigationTcs.TrySetResult(httpRequest.Response); - }; - } - catch (Exception ex) - { - navigationTcs.TrySetException(ex); - } - }; - }; + return null; + } + + var lastRequest = request.LastRedirect ?? request; + BidiHttpRequest.Requests.TryGetValue(lastRequest, out var httpRequest); - return navigationTcs.Task; + return httpRequest.Response; + }); + + var waitForResponseTask = waitForResponse(); + + // TODO: Listen to frame detached event. + await Task.WhenAny(waitForResponseTask).WithTimeout(timeout).ConfigureAwait(false); + + return waitForResponseTask.Result; } internal static BidiFrame From(BidiPage parentPage, BidiFrame parentFrame, BrowsingContext browsingContext) @@ -155,6 +172,37 @@ private PuppeteerException RewriteNavigationError(Exception ex, string url, int : new PuppeteerException("Navigation failed: " + ex.Message, ex); } + private Task WaitForLoadAsync(NavigationOptions options) + { + var waitUntil = options?.WaitUntil ?? new[] { WaitUntilNavigation.Load }; + var timeout = options?.Timeout ?? TimeoutSettings.NavigationTimeout; + + List tasks = new(); + + if (waitUntil.Contains(WaitUntilNavigation.Load)) + { + var loadTcs = new TaskCompletionSource(); + BrowsingContext.Load += (sender, args) => loadTcs.TrySetResult(true); + tasks.Add(loadTcs.Task); + } + + if (waitUntil.Contains(WaitUntilNavigation.DOMContentLoaded)) + { + var domContentLoadedTcs = new TaskCompletionSource(); + BrowsingContext.DomContentLoaded += (sender, args) => domContentLoadedTcs.TrySetResult(true); + tasks.Add(domContentLoadedTcs.Task); + } + + // TODO: Check frame detached event. + return Task.WhenAll(tasks).WithTimeout(timeout); + } + + private Task WaitForNetworkIdleAsync(NavigationOptions options) + { + // TODO: Complete this method. + return Task.CompletedTask; + } + private async Task NavigateAsync(string url) { // Some implementations currently only report errors when the diff --git a/lib/PuppeteerSharp/Bidi/Core/Request.cs b/lib/PuppeteerSharp/Bidi/Core/Request.cs index 75fa229e5..c76128705 100644 --- a/lib/PuppeteerSharp/Bidi/Core/Request.cs +++ b/lib/PuppeteerSharp/Bidi/Core/Request.cs @@ -58,6 +58,8 @@ private Request(BrowsingContext browsingContext, BeforeRequestSentEventArgs args public FetchTimingInfo Timings { get; private set; } + public Request LastRedirect { get; set; } + public static Request From(BrowsingContext browsingContext, BeforeRequestSentEventArgs args) { var request = new Request(browsingContext, args);