From 103af24a446fe6f6a80840695a5efc1e567c9811 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 14 Nov 2025 22:31:28 +0300 Subject: [PATCH 01/18] Run continuation asynchronously in context of events --- src/Playwright/Core/Waiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index c5128d035..7178163bd 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -172,7 +172,7 @@ internal Task WaitForEventAsync(object eventSource, string e) { var info = eventSource.GetType().GetEvent(e) ?? eventSource.GetType().BaseType.GetEvent(e); - var eventTsc = new TaskCompletionSource(); + var eventTsc = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); void EventHandler(object sender, T e) { try From 031d721655e92d487e737a4c85a17fc483c8edb1 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:22:50 +0300 Subject: [PATCH 02/18] Add unit test --- .../PageRunAndWaitForResponseTests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Playwright.Tests/PageRunAndWaitForResponseTests.cs b/src/Playwright.Tests/PageRunAndWaitForResponseTests.cs index 89750a4b9..40a8b8c67 100644 --- a/src/Playwright.Tests/PageRunAndWaitForResponseTests.cs +++ b/src/Playwright.Tests/PageRunAndWaitForResponseTests.cs @@ -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(@"() => { + 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() { From 2adcb7ca84519432578de60d342f890872986fda Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:10:37 +0300 Subject: [PATCH 03/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e86428d6..f39b58d1f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Playwright for .NET 🎭 +# Playwright for .NET 🎭 [![NuGet version](https://img.shields.io/nuget/v/Microsoft.Playwright?color=%2345ba4b)](https://www.nuget.org/packages/Microsoft.Playwright) [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord) | | Linux | macOS | Windows | From 42e63be22d27600b7ee13f72529b57c068c01ee5 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:10:47 +0300 Subject: [PATCH 04/18] Revert "Update README.md" Kick off CI build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f39b58d1f..9e86428d6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Playwright for .NET 🎭 +# Playwright for .NET 🎭 [![NuGet version](https://img.shields.io/nuget/v/Microsoft.Playwright?color=%2345ba4b)](https://www.nuget.org/packages/Microsoft.Playwright) [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord) | | Linux | macOS | Windows | From f413f93add636ae4b0ef3fd7d7c61debc6056210 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:11:51 +0300 Subject: [PATCH 05/18] Fix collection was modified issue --- src/Playwright/Core/Frame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright/Core/Frame.cs b/src/Playwright/Core/Frame.cs index 883c54023..33772fe49 100644 --- a/src/Playwright/Core/Frame.cs +++ b/src/Playwright/Core/Frame.cs @@ -326,7 +326,7 @@ await waiter.WaitForEventAsync(this, "LoadState", s => await waiter.WaitForPromiseAsync(Task.FromException(ex)).ConfigureAwait(false); } - if (!_loadStates.Select(s => s.ToValueString()).Contains(waitUntil.Value.ToValueString())) + if (!_loadStates.ToArray().Select(s => s.ToValueString()).Contains(waitUntil.Value.ToValueString())) { await waiter.WaitForEventAsync( this, From a5a51d736581044954c2359dd3dae2beb5359957 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:12:28 +0300 Subject: [PATCH 06/18] Revert "Fix collection was modified issue" This reverts commit f413f93add636ae4b0ef3fd7d7c61debc6056210. --- src/Playwright/Core/Frame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright/Core/Frame.cs b/src/Playwright/Core/Frame.cs index 33772fe49..883c54023 100644 --- a/src/Playwright/Core/Frame.cs +++ b/src/Playwright/Core/Frame.cs @@ -326,7 +326,7 @@ await waiter.WaitForEventAsync(this, "LoadState", s => await waiter.WaitForPromiseAsync(Task.FromException(ex)).ConfigureAwait(false); } - if (!_loadStates.ToArray().Select(s => s.ToValueString()).Contains(waitUntil.Value.ToValueString())) + if (!_loadStates.Select(s => s.ToValueString()).Contains(waitUntil.Value.ToValueString())) { await waiter.WaitForEventAsync( this, From a976a91a33cdcc08d63ad2ed3a9c72f2e675a105 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:50:31 +0300 Subject: [PATCH 07/18] RunContinuationsAsynchronously for RejectOn method --- src/Playwright/Core/Waiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index 7178163bd..83ada1cf5 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -152,7 +152,7 @@ internal void RejectOnTimeout(int? timeout, string message) var cts = new CancellationTokenSource(); RejectOn( - new TaskCompletionSource().Task.WithTimeout(timeout.Value, _ => new TimeoutException(message), cts.Token), + new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously).Task.WithTimeout(timeout.Value, _ => new TimeoutException(message), cts.Token), () => cts.Cancel()); } From d7df026ddba8746449eece14e517114ddbd7389c Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:15:58 +0300 Subject: [PATCH 08/18] Minimize race condition when awaiting navigation events --- src/Playwright/Core/Frame.cs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/Playwright/Core/Frame.cs b/src/Playwright/Core/Frame.cs index 883c54023..21961513d 100644 --- a/src/Playwright/Core/Frame.cs +++ b/src/Playwright/Core/Frame.cs @@ -326,16 +326,26 @@ await waiter.WaitForEventAsync(this, "LoadState", s => await waiter.WaitForPromiseAsync(Task.FromException(ex)).ConfigureAwait(false); } - if (!_loadStates.Select(s => s.ToValueString()).Contains(waitUntil.Value.ToValueString())) + // Set the subscription first + var (loadStateTask, loadStateDispose) = waiter.GetWaitForEventTask( + this, + "LoadState", + e => + { + waiter.Log($" \"{e}\" event fired"); + return e.ToValueString() == waitUntil.Value.ToValueString(); + }); + + if (_loadStates.Any(s => s.ToValueString() == waitUntil.Value.ToValueString())) { - await waiter.WaitForEventAsync( - this, - "LoadState", - e => - { - waiter.Log($" \"{e}\" event fired"); - return e.ToValueString() == waitUntil.Value.ToValueString(); - }).ConfigureAwait(false); + // 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; From b5adaa7b2a834ee7206bb4853b98a9f7b2896a6c Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:49:37 +0300 Subject: [PATCH 09/18] Temporary increase test timeout to avoid interruption --- src/Playwright.Tests/TestConstants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright.Tests/TestConstants.cs b/src/Playwright.Tests/TestConstants.cs index cf776f036..e778b6883 100644 --- a/src/Playwright.Tests/TestConstants.cs +++ b/src/Playwright.Tests/TestConstants.cs @@ -31,7 +31,7 @@ internal static class TestConstants { public static string BrowserName { get; set; } = null!; - public const int DefaultTestTimeout = 30_000; + public const int DefaultTestTimeout = 60_000; public const int SlowTestTimeout = DefaultTestTimeout * 5; internal static bool IsChromium => BrowserName == BrowserType.Chromium; From 249deb021db2d477e999f56b554e744ed1093798 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sat, 31 Jan 2026 02:31:55 +0300 Subject: [PATCH 10/18] Default task scheduler --- src/Playwright/Core/Waiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index 83ada1cf5..fe8953bfc 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -139,7 +139,7 @@ internal void RejectOnEvent( var (task, dispose) = GetWaitForEventTask(eventSource, e, predicate); RejectOn( - task.ContinueWith(_ => throw navigationException(), _onDisposeCts.Token, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Current), + task.ContinueWith(_ => throw navigationException(), _onDisposeCts.Token, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default), dispose); } From 795cf7bd8ee31a63dd8bbaf4bb67f12d64754034 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sat, 31 Jan 2026 12:57:32 +0300 Subject: [PATCH 11/18] Remove event handler --- src/Playwright/Core/Waiter.cs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index fe8953bfc..d237b2ef4 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -170,32 +170,34 @@ internal Task WaitForEventAsync(object eventSource, string e) internal (Task Task, Action Dispose) GetWaitForEventTask(object eventSource, string e, Func? predicate) { - var info = eventSource.GetType().GetEvent(e) ?? eventSource.GetType().BaseType.GetEvent(e); - + var info = (eventSource.GetType().GetEvent(e) ?? eventSource.GetType().BaseType?.GetEvent(e)) + ?? throw new ArgumentException($"Event '{e}' not found on type {eventSource.GetType().Name}"); var eventTsc = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - void EventHandler(object sender, T e) + EventHandler? handler = null; + + handler = (sender, eventArgs) => { try { - if (predicate == null || predicate(e)) + if (predicate == null || predicate(eventArgs)) { - eventTsc.TrySetResult(e); - } - else - { - return; + if (eventTsc.TrySetResult(eventArgs)) + { + info.RemoveEventHandler(eventSource, handler); + } } } catch (Exception ex) { - eventTsc.TrySetException(ex); + if (eventTsc.TrySetException(ex)) + { + info.RemoveEventHandler(eventSource, handler); + } } + }; - info.RemoveEventHandler(eventSource, (EventHandler)EventHandler); - } - - info.AddEventHandler(eventSource, (EventHandler)EventHandler); - return (eventTsc.Task, () => info.RemoveEventHandler(eventSource, (EventHandler)EventHandler)); + info.AddEventHandler(eventSource, handler); + return (eventTsc.Task, () => info.RemoveEventHandler(eventSource, handler)); } internal async Task WaitForPromiseAsync(Task task, Action? dispose = null) From fa760ec8440fdf433b6abde5204ed91eb1941724 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:09:18 +0300 Subject: [PATCH 12/18] Thread safe changing load states --- src/Playwright/Core/Frame.cs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/Playwright/Core/Frame.cs b/src/Playwright/Core/Frame.cs index 21961513d..02de19d36 100644 --- a/src/Playwright/Core/Frame.cs +++ b/src/Playwright/Core/Frame.cs @@ -42,7 +42,8 @@ namespace Microsoft.Playwright.Core; internal class Frame : ChannelOwner, IFrame { - private readonly List _loadStates = new(); + private readonly HashSet _loadStates = new(); + private readonly object _loadStatesLock = new(); internal readonly List _childFrames = new(); internal Frame(ChannelOwner parent, string guid, FrameInitializer initializer) : base(parent, guid) @@ -50,7 +51,11 @@ internal Frame(ChannelOwner parent, string guid, FrameInitializer initializer) : Url = initializer.Url; Name = initializer.Name; ParentFrame = initializer.ParentFrame; - _loadStates = initializer.LoadStates; + + foreach (var state in initializer.LoadStates) + { + _loadStates.Add(state); + } } /// @@ -111,13 +116,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) { @@ -238,7 +249,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"); } @@ -336,7 +353,13 @@ await waiter.WaitForEventAsync(this, "LoadState", s => return e.ToValueString() == waitUntil.Value.ToValueString(); }); - if (_loadStates.Any(s => s.ToValueString() == waitUntil.Value.ToValueString())) + bool containsWaitUntilState; + lock (_loadStatesLock) + { + 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"); From ba153bcc241d4af89822b7a3e22e8ed4cfb7ab6b Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:31:46 +0300 Subject: [PATCH 13/18] Avoid UnobserveException in ContinueWith --- src/Playwright/Core/Waiter.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index d237b2ef4..8cd9f5c4f 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -139,7 +139,15 @@ internal void RejectOnEvent( var (task, dispose) = GetWaitForEventTask(eventSource, e, predicate); RejectOn( - task.ContinueWith(_ => throw navigationException(), _onDisposeCts.Token, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default), + task.ContinueWith( + t => + { + _ = t.Exception; // Observe the antecedent's exception + throw navigationException(); + }, + _onDisposeCts.Token, + TaskContinuationOptions.RunContinuationsAsynchronously, + TaskScheduler.Default), dispose); } From 82a8a7a7196e708dcaa9722ae89796bfd95b1ff9 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Sat, 31 Jan 2026 14:34:59 +0300 Subject: [PATCH 14/18] Return back 30s test timeout --- src/Playwright.Tests/TestConstants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright.Tests/TestConstants.cs b/src/Playwright.Tests/TestConstants.cs index e778b6883..cf776f036 100644 --- a/src/Playwright.Tests/TestConstants.cs +++ b/src/Playwright.Tests/TestConstants.cs @@ -31,7 +31,7 @@ internal static class TestConstants { public static string BrowserName { get; set; } = null!; - public const int DefaultTestTimeout = 60_000; + public const int DefaultTestTimeout = 30_000; public const int SlowTestTimeout = DefaultTestTimeout * 5; internal static bool IsChromium => BrowserName == BrowserType.Chromium; From 74290cbb7e6cf6d042928cdeaa874e8bfdaa72fd Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:00:53 +0300 Subject: [PATCH 15/18] Revert "Avoid UnobserveException in ContinueWith" This reverts commit ba153bcc241d4af89822b7a3e22e8ed4cfb7ab6b. --- src/Playwright/Core/Waiter.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index 8cd9f5c4f..d237b2ef4 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -139,15 +139,7 @@ internal void RejectOnEvent( var (task, dispose) = GetWaitForEventTask(eventSource, e, predicate); RejectOn( - task.ContinueWith( - t => - { - _ = t.Exception; // Observe the antecedent's exception - throw navigationException(); - }, - _onDisposeCts.Token, - TaskContinuationOptions.RunContinuationsAsynchronously, - TaskScheduler.Default), + task.ContinueWith(_ => throw navigationException(), _onDisposeCts.Token, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default), dispose); } From df39fd537c5ea0dbd274537014e6d8ed5d1cb35e Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:32:19 +0300 Subject: [PATCH 16/18] Revert "Remove event handler" This reverts commit 795cf7bd8ee31a63dd8bbaf4bb67f12d64754034. --- src/Playwright/Core/Waiter.cs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index d237b2ef4..fe8953bfc 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -170,34 +170,32 @@ internal Task WaitForEventAsync(object eventSource, string e) internal (Task Task, Action Dispose) GetWaitForEventTask(object eventSource, string e, Func? predicate) { - var info = (eventSource.GetType().GetEvent(e) ?? eventSource.GetType().BaseType?.GetEvent(e)) - ?? throw new ArgumentException($"Event '{e}' not found on type {eventSource.GetType().Name}"); - var eventTsc = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - EventHandler? handler = null; + var info = eventSource.GetType().GetEvent(e) ?? eventSource.GetType().BaseType.GetEvent(e); - handler = (sender, eventArgs) => + var eventTsc = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + void EventHandler(object sender, T e) { try { - if (predicate == null || predicate(eventArgs)) + if (predicate == null || predicate(e)) { - if (eventTsc.TrySetResult(eventArgs)) - { - info.RemoveEventHandler(eventSource, handler); - } + eventTsc.TrySetResult(e); + } + else + { + return; } } catch (Exception ex) { - if (eventTsc.TrySetException(ex)) - { - info.RemoveEventHandler(eventSource, handler); - } + eventTsc.TrySetException(ex); } - }; - info.AddEventHandler(eventSource, handler); - return (eventTsc.Task, () => info.RemoveEventHandler(eventSource, handler)); + info.RemoveEventHandler(eventSource, (EventHandler)EventHandler); + } + + info.AddEventHandler(eventSource, (EventHandler)EventHandler); + return (eventTsc.Task, () => info.RemoveEventHandler(eventSource, (EventHandler)EventHandler)); } internal async Task WaitForPromiseAsync(Task task, Action? dispose = null) From fbb6c4212f638bab5692995f283af0cae2359a39 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 5 Feb 2026 18:01:11 +0300 Subject: [PATCH 17/18] Revert "Default task scheduler" This reverts commit 249deb021db2d477e999f56b554e744ed1093798. --- src/Playwright/Core/Waiter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index fe8953bfc..83ada1cf5 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -139,7 +139,7 @@ internal void RejectOnEvent( var (task, dispose) = GetWaitForEventTask(eventSource, e, predicate); RejectOn( - task.ContinueWith(_ => throw navigationException(), _onDisposeCts.Token, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default), + task.ContinueWith(_ => throw navigationException(), _onDisposeCts.Token, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Current), dispose); } From 3efaecb1f0952f4c994a054afa3cad4cf422230d Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:04:29 +0300 Subject: [PATCH 18/18] Return original List type for loadStates --- src/Playwright/Core/Frame.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Playwright/Core/Frame.cs b/src/Playwright/Core/Frame.cs index 02de19d36..2fdfc8dde 100644 --- a/src/Playwright/Core/Frame.cs +++ b/src/Playwright/Core/Frame.cs @@ -42,7 +42,7 @@ namespace Microsoft.Playwright.Core; internal class Frame : ChannelOwner, IFrame { - private readonly HashSet _loadStates = new(); + private readonly List _loadStates = new(); private readonly object _loadStatesLock = new(); internal readonly List _childFrames = new(); @@ -51,11 +51,7 @@ internal Frame(ChannelOwner parent, string guid, FrameInitializer initializer) : Url = initializer.Url; Name = initializer.Name; ParentFrame = initializer.ParentFrame; - - foreach (var state in initializer.LoadStates) - { - _loadStates.Add(state); - } + _loadStates = initializer.LoadStates; } ///