Skip to content
Merged
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
56 changes: 0 additions & 56 deletions lib/PuppeteerSharp/Cdp/CdpPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,62 +419,6 @@ await Task.WhenAll(
return navigationTask.Result;
}

/// <inheritdoc/>
public override async Task WaitForNetworkIdleAsync(WaitForNetworkIdleOptions options = null)
{
var timeout = options?.Timeout ?? DefaultTimeout;
var idleTime = options?.IdleTime ?? 500;

var networkIdleTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

var idleTimer = new Timer { Interval = idleTime, };

idleTimer.Elapsed += (_, _) => { networkIdleTcs.TrySetResult(true); };

var networkManager = FrameManager.NetworkManager;

void Evaluate()
{
idleTimer.Stop();

if (networkManager.NumRequestsInProgress == 0)
{
idleTimer.Start();
}
}

void RequestEventListener(object sender, RequestEventArgs e) => Evaluate();
void ResponseEventListener(object sender, ResponseCreatedEventArgs e) => Evaluate();

void Cleanup()
{
idleTimer.Stop();
idleTimer.Dispose();

networkManager.Request -= RequestEventListener;
networkManager.Response -= ResponseEventListener;
}

networkManager.Request += RequestEventListener;
networkManager.Response += ResponseEventListener;

Evaluate();

await Task.WhenAny(networkIdleTcs.Task, SessionClosedTask).WithTimeout(timeout, t =>
{
Cleanup();

return new TimeoutException($"Timeout of {t.TotalMilliseconds} ms exceeded");
}).ConfigureAwait(false);

Cleanup();

if (SessionClosedTask.IsFaulted)
{
await SessionClosedTask.ConfigureAwait(false);
}
}

/// <inheritdoc/>
public override async Task<IRequest> WaitForRequestAsync(Func<IRequest, bool> predicate, WaitForOptions options = null)
{
Expand Down
3 changes: 0 additions & 3 deletions lib/PuppeteerSharp/Cdp/NetworkEventManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ internal class NetworkEventManager
private readonly ConcurrentDictionary<string, List<RedirectInfo>> _queuedRedirectInfoMap = new();
private readonly ConcurrentDictionary<string, List<ResponseReceivedExtraInfoResponse>> _responseReceivedExtraInfoMap = new();

public int NumRequestsInProgress
=> _httpRequestsMap.Values.Count(r => r.Response == null);

internal void Forget(string requestId)
{
_requestWillBeSentMap.TryRemove(requestId, out _);
Expand Down
2 changes: 0 additions & 2 deletions lib/PuppeteerSharp/Cdp/NetworkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ internal NetworkManager(bool acceptInsecureCerts, IFrameProvider frameManager, I

internal Dictionary<string, string> ExtraHTTPHeaders => _extraHTTPHeaders?.Clone();

internal int NumRequestsInProgress => _networkEventManager.NumRequestsInProgress;

internal Task AddClientAsync(ICDPSession client)
{
if (_clients.ContainsKey(client))
Expand Down
68 changes: 66 additions & 2 deletions lib/PuppeteerSharp/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using System.Timers;
using PuppeteerSharp.Cdp;
using PuppeteerSharp.Cdp.Messaging;
using PuppeteerSharp.Helpers;
Expand Down Expand Up @@ -41,10 +43,14 @@ public abstract class Page : IPage

private readonly TaskQueue _screenshotTaskQueue;
private readonly ConcurrentSet<Func<IRequest, Task>> _requestInterceptionTask = [];
private readonly ConcurrentSet<IRequest> _requests = new();
private readonly TaskCompletionSource<bool> _closeTaskCompletionSource =
new(TaskCreationOptions.RunContinuationsAsynchronously);

internal Page(TaskQueue screenshotTaskQueue)
{
_screenshotTaskQueue = screenshotTaskQueue;
Request += (_, e) => _requests.Add(e.Request);
}

/// <inheritdoc/>
Expand Down Expand Up @@ -228,6 +234,9 @@ public int DefaultTimeout
/// </summary>
protected ScreenshotOptions ScreenshotBurstModeOptions { get; set; }

private int NumRequestsInProgress
=> _requests.Count(r => r.Response == null);

/// <inheritdoc/>
public abstract Task SetGeolocationAsync(GeolocationOption options);

Expand Down Expand Up @@ -683,7 +692,58 @@ public Task<IResponse> WaitForNavigationAsync(NavigationOptions options = null)
=> MainFrame.WaitForNavigationAsync(options);

/// <inheritdoc/>
public abstract Task WaitForNetworkIdleAsync(WaitForNetworkIdleOptions options = null);
public async Task WaitForNetworkIdleAsync(WaitForNetworkIdleOptions options = null)
{
var timeout = options?.Timeout ?? DefaultTimeout;
var idleTime = options?.IdleTime ?? 500;

var networkIdleTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

var idleTimer = new Timer { Interval = idleTime, };

idleTimer.Elapsed += (_, _) => { networkIdleTcs.TrySetResult(true); };

void Evaluate()
{
idleTimer.Stop();

if (NumRequestsInProgress <= (options?.Concurrency ?? 0))
{
idleTimer.Start();
}
}

void RequestEventListener(object sender, RequestEventArgs e) => Evaluate();
void ResponseEventListener(object sender, ResponseCreatedEventArgs e) => Evaluate();

void Cleanup()
{
idleTimer.Stop();
idleTimer.Dispose();

Request -= RequestEventListener;
Response -= ResponseEventListener;
}

Request += RequestEventListener;
Response += ResponseEventListener;

Evaluate();

await Task.WhenAny(networkIdleTcs.Task, _closeTaskCompletionSource.Task).WithTimeout(timeout, t =>
{
Cleanup();

return new TimeoutException($"Timeout of {t.TotalMilliseconds} ms exceeded");
}).ConfigureAwait(false);

Cleanup();

if (_closeTaskCompletionSource.Task.IsFaulted)
{
await _closeTaskCompletionSource.Task.ConfigureAwait(false);
}
}

/// <inheritdoc/>
public Task<IRequest> WaitForRequestAsync(string url, WaitForOptions options = null)
Expand Down Expand Up @@ -885,7 +945,11 @@ protected void OnRequest(IRequest request)
/// <summary>
/// Raises the <see cref="Close"/> event.
/// </summary>
protected void OnClose() => Close?.Invoke(this, EventArgs.Empty);
protected void OnClose()
{
_closeTaskCompletionSource?.TrySetException(new TargetClosedException("Target closed", "Session closed"));
Close?.Invoke(this, EventArgs.Empty);
}

/// <summary>
/// Raises the <see cref="Console"/> event.
Expand Down
5 changes: 5 additions & 0 deletions lib/PuppeteerSharp/WaitForNetworkIdleOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ public class WaitForNetworkIdleOptions : WaitForOptions
/// How long to wait for no network requests in milliseconds, defaults to 500 milliseconds.
/// </summary>
public int? IdleTime { get; set; }

/// <summary>
/// Maximum number concurrent of network connections to be considered inactive.
/// </summary>
public int Concurrency { get; set; }
}
}
Loading