diff --git a/src/PlaywrightNative.Tests/OfficialClientCertificateExtras.cs b/src/PlaywrightNative.Tests/OfficialClientCertificateExtras.cs index dc56a1b9..f849c440 100644 --- a/src/PlaywrightNative.Tests/OfficialClientCertificateExtras.cs +++ b/src/PlaywrightNative.Tests/OfficialClientCertificateExtras.cs @@ -113,15 +113,13 @@ public async ValueTask DisposeAsync() internal sealed class OfficialTlsSniRejectServer : IAsyncDisposable { private readonly TcpListener _listener; - private readonly X509Certificate2 _cert; private readonly CancellationTokenSource _cts = new(); private readonly Task _acceptLoop; private readonly SslProtocols _protocol; - private OfficialTlsSniRejectServer(TcpListener listener, X509Certificate2 cert, SslProtocols protocol) + private OfficialTlsSniRejectServer(TcpListener listener, SslProtocols protocol) { _listener = listener; - _cert = cert; _protocol = protocol; Port = ((IPEndPoint)listener.LocalEndpoint).Port; _acceptLoop = AcceptLoopAsync(); @@ -133,12 +131,9 @@ private OfficialTlsSniRejectServer(TcpListener listener, X509Certificate2 cert, internal static OfficialTlsSniRejectServer Start(SslProtocols protocol) { - X509Certificate2 cert = OfficialClientCertificateServer.LoadPem( - OfficialClientCertificateServer.Asset("client-certificates/server/server_cert.pem"), - OfficialClientCertificateServer.Asset("client-certificates/server/server_key.pem")); TcpListener listener = new(IPAddress.Loopback, 0); listener.Start(); - return new OfficialTlsSniRejectServer(listener, cert, protocol); + return new OfficialTlsSniRejectServer(listener, protocol); } public async ValueTask DisposeAsync() @@ -161,7 +156,6 @@ public async ValueTask DisposeAsync() } _cts.Dispose(); - _cert.Dispose(); } private async Task AcceptLoopAsync() @@ -198,9 +192,12 @@ private async Task HandleAsync(TcpClient client) await using (NetworkStream stream = client.GetStream()) await using (SslStream ssl = new(stream, leaveInnerStreamOpen: false)) { + // Upstream rejects from SNICallback, after the ClientHello + // arrives. SslStream only runs the selection callback when no + // ServerCertificate is set; setting both throws before the + // handshake reads anything. SslServerAuthenticationOptions options = new() { - ServerCertificate = _cert, ClientCertificateRequired = true, EnabledSslProtocols = _protocol, CertificateRevocationCheckMode = X509RevocationMode.NoCheck, diff --git a/src/PlaywrightNative/Helpers/ScreenshotDecorations.cs b/src/PlaywrightNative/Helpers/ScreenshotDecorations.cs index 629a3821..39dba40f 100644 --- a/src/PlaywrightNative/Helpers/ScreenshotDecorations.cs +++ b/src/PlaywrightNative/Helpers/ScreenshotDecorations.cs @@ -20,6 +20,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Playwright; +using PlaywrightNative.Chromium; using PlaywrightNative.WebKit; namespace PlaywrightNative.Helpers @@ -404,7 +405,7 @@ private static async Task InjectStyleAsync(IPage page, string css, List + /// Upstream decorates frames with nonStallingEvaluateInExistingContext: + /// a Chromium child frame whose document has no live execution context yet + /// (a stalled subframe navigation) is skipped instead of waiting for one. + /// + /// The frame to decorate. + /// when evaluating would wait for a new context. + private static bool LacksExistingContext(IFrame frame) + => frame is ChromiumFrame chromium + && (chromium.Frame.ExecutionContext == null || chromium.Frame.ExecutionContext.Destroyed.IsCompleted); + private static Task FinishAnimationsAsync(IPage page) { // Must share EvaluateInFramesAsync with RestoreAnimationsJs so