Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
11 changes: 11 additions & 0 deletions src/Playwright.Tests/PageRunAndWaitForResponseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ public async Task ShouldWork()
Assert.AreEqual(Server.Prefix + "/digits/2.png", response.Url);
}

[PlaywrightTest]
public async Task ShouldWorkWithAsyncContinuation()
{
await Page.GotoAsync(Server.EmptyPage);
var response = await Page.RunAndWaitForResponseAsync(() => Page.EvaluateAsync<string>(@"() => {
fetch('/digits/1.png');
}"), Server.Prefix + "/digits/1.png");
// Should not deadlock here
Task.Run(() => Page.GotoAsync(Server.EmptyPage)).Wait();
}

[PlaywrightTest("page-wait-for-response.spec.ts", "should respect timeout")]
public Task ShouldRespectTimeout()
{
Expand Down
53 changes: 41 additions & 12 deletions src/Playwright/Core/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace Microsoft.Playwright.Core;
internal class Frame : ChannelOwner, IFrame
{
private readonly List<WaitUntilState> _loadStates = new();
private readonly object _loadStatesLock = new();
internal readonly List<Frame> _childFrames = new();

internal Frame(ChannelOwner parent, string guid, FrameInitializer initializer) : base(parent, guid)
Expand Down Expand Up @@ -111,13 +112,19 @@ internal void OnLoadState(WaitUntilState? add, WaitUntilState? remove)
{
if (add.HasValue)
{
_loadStates.Add(add.Value);
lock (_loadStatesLock)
{
_loadStates.Add(add.Value);
}
LoadState?.Invoke(this, add.Value);
}

if (remove.HasValue)
{
_loadStates.Remove(remove.Value);
lock (_loadStatesLock)
{
_loadStates.Remove(remove.Value);
}
}
if (this.ParentFrame == null && add == WaitUntilState.Load && this.Page != null)
{
Expand Down Expand Up @@ -238,7 +245,13 @@ public async Task WaitForLoadStateAsync(LoadState? state = default, FrameWaitFor
{
waiter = SetupNavigationWaiter("frame.WaitForLoadStateAsync", options?.Timeout);

if (_loadStates.Contains(loadState))
bool containsLoadState;
lock (_loadStatesLock)
{
containsLoadState = _loadStates.Contains(loadState);
}

if (containsLoadState)
{
waiter.Log($" not waiting, \"{state}\" event already fired");
}
Expand Down Expand Up @@ -326,16 +339,32 @@ await waiter.WaitForEventAsync<WaitUntilState>(this, "LoadState", s =>
await waiter.WaitForPromiseAsync(Task.FromException<object>(ex)).ConfigureAwait(false);
}

if (!_loadStates.Select(s => s.ToValueString()).Contains(waitUntil.Value.ToValueString()))
// Set the subscription first
var (loadStateTask, loadStateDispose) = waiter.GetWaitForEventTask<WaitUntilState>(
this,
"LoadState",
e =>
{
waiter.Log($" \"{e}\" event fired");
return e.ToValueString() == waitUntil.Value.ToValueString();
});

bool containsWaitUntilState;
lock (_loadStatesLock)
{
await waiter.WaitForEventAsync<WaitUntilState>(
this,
"LoadState",
e =>
{
waiter.Log($" \"{e}\" event fired");
return e.ToValueString() == waitUntil.Value.ToValueString();
}).ConfigureAwait(false);
containsWaitUntilState = _loadStates.Any(s => s.ToValueString() == waitUntil.Value.ToValueString());
}

if (containsWaitUntilState)
{
// State is already present, no need to wait
waiter.Log($" \"{waitUntil}\" event was already fired");
loadStateDispose();
}
else
{
// Wait for the event
await waiter.WaitForPromiseAsync(loadStateTask, loadStateDispose).ConfigureAwait(false);
}

var request = navigatedEvent.NewDocument?.Request;
Expand Down
4 changes: 2 additions & 2 deletions src/Playwright/Core/Waiter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ internal void RejectOnTimeout(int? timeout, string message)

var cts = new CancellationTokenSource();
RejectOn(
new TaskCompletionSource<bool>().Task.WithTimeout(timeout.Value, _ => new TimeoutException(message), cts.Token),
new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously).Task.WithTimeout(timeout.Value, _ => new TimeoutException(message), cts.Token),
() => cts.Cancel());
}

Expand All @@ -172,7 +172,7 @@ internal Task<object> WaitForEventAsync(object eventSource, string e)
{
var info = eventSource.GetType().GetEvent(e) ?? eventSource.GetType().BaseType.GetEvent(e);

var eventTsc = new TaskCompletionSource<T>();
var eventTsc = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
void EventHandler(object sender, T e)
{
try
Expand Down
Loading