From f9df9430399d9e2c9e5ec41478b5518e0df62d51 Mon Sep 17 00:00:00 2001 From: Marius Thesing Date: Sun, 8 Dec 2024 12:56:52 +0100 Subject: [PATCH] add net8.0 target - add net8.0 target to projects - fix all compiler warnings introduced by the new targets - removed unneeded dependencies since they are part of the .NET runtime fixes #3033 --- src/.editorconfig | 1 + src/Common/Dependencies.props | 4 - src/Playwright.NUnit/Playwright.NUnit.csproj | 2 +- src/Playwright.NUnit/SkipAttribute.cs | 2 +- src/Playwright.Tests/PageEvaluateTests.cs | 2 +- src/Playwright/Core/APIRequest.cs | 4 + src/Playwright/Core/APIRequestContext.cs | 4 + src/Playwright/Core/Browser.cs | 4 + src/Playwright/Core/BrowserContext.cs | 4 + src/Playwright/Core/BrowserType.cs | 2 + src/Playwright/Core/ElementHandle.cs | 8 +- src/Playwright/Core/Frame.cs | 15 +++- src/Playwright/Core/Page.cs | 8 ++ src/Playwright/Core/Request.cs | 11 ++- src/Playwright/Core/Route.cs | 4 + src/Playwright/Core/Waiter.cs | 12 ++- src/Playwright/Helpers/Driver.cs | 9 +- .../Helpers/SetInputFilesHelpers.cs | 18 ++-- src/Playwright/Helpers/StringExtensions.cs | 4 + src/Playwright/Helpers/URLMatch.cs | 4 + src/Playwright/Playwright.cs | 2 + src/Playwright/Playwright.csproj | 10 ++- src/Playwright/Transport/Connection.cs | 89 +++++++------------ .../EvaluateArgumentValueConverter.cs | 18 ++-- src/Playwright/Transport/StdIOTransport.cs | 11 ++- 25 files changed, 156 insertions(+), 96 deletions(-) diff --git a/src/.editorconfig b/src/.editorconfig index b5d069ce8e..a7814561c3 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -184,6 +184,7 @@ dotnet_diagnostic.CA1305.severity = error dotnet_diagnostic.CA1307.severity = none dotnet_diagnostic.CA1308.severity = none dotnet_diagnostic.CA1508.severity = none +dotnet_diagnostic.CA1510.severity = none dotnet_diagnostic.CA1725.severity = error dotnet_diagnostic.CA1801.severity = suggestion dotnet_diagnostic.CA1812.severity = none diff --git a/src/Common/Dependencies.props b/src/Common/Dependencies.props index ba44be21a3..e569218942 100644 --- a/src/Common/Dependencies.props +++ b/src/Common/Dependencies.props @@ -18,10 +18,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - all - runtime; build; native; contentfiles; analyzers - all runtime; build; native; contentfiles; analyzers diff --git a/src/Playwright.NUnit/Playwright.NUnit.csproj b/src/Playwright.NUnit/Playwright.NUnit.csproj index 34131840d8..91971b038d 100644 --- a/src/Playwright.NUnit/Playwright.NUnit.csproj +++ b/src/Playwright.NUnit/Playwright.NUnit.csproj @@ -9,7 +9,7 @@ and fixtures to enable using it within NUnit. icon.png - netcoreapp3.1;net462 + net8.0;net462 true true Microsoft.Playwright.NUnit diff --git a/src/Playwright.NUnit/SkipAttribute.cs b/src/Playwright.NUnit/SkipAttribute.cs index 728cd9ce7f..75134cd388 100644 --- a/src/Playwright.NUnit/SkipAttribute.cs +++ b/src/Playwright.NUnit/SkipAttribute.cs @@ -63,7 +63,7 @@ public void ApplyToTest(Test test) { if (_combinations.Any(combination => { - var requirements = (Enum.GetValues(typeof(Targets)) as Targets[]).Where(x => combination.HasFlag(x)); + var requirements = ((Targets[])Enum.GetValues(typeof(Targets))).Where(x => combination.HasFlag(x)); return requirements.All(flag => flag switch { diff --git a/src/Playwright.Tests/PageEvaluateTests.cs b/src/Playwright.Tests/PageEvaluateTests.cs index 9fafc56364..b5e3c437d7 100644 --- a/src/Playwright.Tests/PageEvaluateTests.cs +++ b/src/Playwright.Tests/PageEvaluateTests.cs @@ -549,7 +549,7 @@ public async Task ShouldNotLeakHandles() [PlaywrightTest("page-evaluate.spec.ts", "should evaluate exception with a function on the stack")] public async Task ShouldEvaluateExceptionWithAFunctionOnTheStack() { - var exception = await Page.EvaluateAsync(@"() => { + var exception = await Page.EvaluateAsync(@"() => { return (function functionOnStack() { return new Error('error message'); })(); diff --git a/src/Playwright/Core/APIRequest.cs b/src/Playwright/Core/APIRequest.cs index a52c4db15b..07096e5edb 100644 --- a/src/Playwright/Core/APIRequest.cs +++ b/src/Playwright/Core/APIRequest.cs @@ -60,7 +60,11 @@ async Task IAPIRequest.NewContextAsync(APIRequestNewContextO throw new PlaywrightException($"The specified storage state file does not exist: {options?.StorageStatePath}"); } +#if NET + storageState = await File.ReadAllTextAsync(options?.StorageStatePath).ConfigureAwait(false); +#else storageState = File.ReadAllText(options?.StorageStatePath); +#endif } if (!string.IsNullOrEmpty(storageState)) { diff --git a/src/Playwright/Core/APIRequestContext.cs b/src/Playwright/Core/APIRequestContext.cs index 91737ede16..6bd4ac286e 100644 --- a/src/Playwright/Core/APIRequestContext.cs +++ b/src/Playwright/Core/APIRequestContext.cs @@ -220,7 +220,11 @@ await SendMessageToServerAsync("storageState").ConfigureAwait(fals if (!string.IsNullOrEmpty(options?.Path)) { +#if NET + await File.WriteAllTextAsync(options?.Path, state).ConfigureAwait(false); +#else File.WriteAllText(options?.Path, state); +#endif } return state; diff --git a/src/Playwright/Core/Browser.cs b/src/Playwright/Core/Browser.cs index 783162a114..30b4c51dc9 100644 --- a/src/Playwright/Core/Browser.cs +++ b/src/Playwright/Core/Browser.cs @@ -150,7 +150,11 @@ public async Task NewContextAsync(BrowserNewContextOptions opti throw new PlaywrightException($"The specified storage state file does not exist: {options.StorageStatePath}"); } +#if NET + storageState = await File.ReadAllTextAsync(options.StorageStatePath).ConfigureAwait(false); +#else storageState = File.ReadAllText(options.StorageStatePath); +#endif } if (!string.IsNullOrEmpty(storageState)) diff --git a/src/Playwright/Core/BrowserContext.cs b/src/Playwright/Core/BrowserContext.cs index 4af6c800a7..515f016c32 100644 --- a/src/Playwright/Core/BrowserContext.cs +++ b/src/Playwright/Core/BrowserContext.cs @@ -544,7 +544,11 @@ await SendMessageToServerAsync("storageState").ConfigureAwait(fals if (!string.IsNullOrEmpty(options?.Path)) { +#if NET + await File.WriteAllTextAsync(options?.Path, state).ConfigureAwait(false); +#else File.WriteAllText(options?.Path, state); +#endif } return state; diff --git a/src/Playwright/Core/BrowserType.cs b/src/Playwright/Core/BrowserType.cs index 46bd747fd9..9b41786781 100644 --- a/src/Playwright/Core/BrowserType.cs +++ b/src/Playwright/Core/BrowserType.cs @@ -181,7 +181,9 @@ void ClosePipe() { pipe.CloseAsync().IgnoreException(); } +#pragma warning disable CA2000 // Dispose objects before losing scope var connection = new Connection(_connection.LocalUtils); +#pragma warning restore CA2000 // Dispose objects before losing scope connection.MarkAsRemote(); connection.Close += (_, _) => ClosePipe(); diff --git a/src/Playwright/Core/ElementHandle.cs b/src/Playwright/Core/ElementHandle.cs index 6fcf3e3128..acae5d3540 100644 --- a/src/Playwright/Core/ElementHandle.cs +++ b/src/Playwright/Core/ElementHandle.cs @@ -122,7 +122,11 @@ public async Task ScreenshotAsync(ElementHandleScreenshotOptions options if (!string.IsNullOrEmpty(options.Path)) { Directory.CreateDirectory(new FileInfo(options.Path).Directory.FullName); +#if NET + await File.WriteAllBytesAsync(options.Path, result).ConfigureAwait(false); +#else File.WriteAllBytes(options.Path, result); +#endif } return result; @@ -204,7 +208,7 @@ public async Task SetInputFilesAsync(IEnumerable files, ElementHandleSet { throw new PlaywrightException("Cannot set input files to detached element."); } - var converted = await SetInputFilesHelpers.ConvertInputFilesAsync(files, (BrowserContext)frame.Page.Context).ConfigureAwait(false); + var converted = await SetInputFilesHelpers.ConvertInputFilesAsync(files.ToList(), (BrowserContext)frame.Page.Context).ConfigureAwait(false); await SendMessageToServerAsync("setInputFiles", new Dictionary { ["payloads"] = converted.Payloads, @@ -221,7 +225,7 @@ public Task SetInputFilesAsync(FilePayload files, ElementHandleSetInputFilesOpti public async Task SetInputFilesAsync(IEnumerable files, ElementHandleSetInputFilesOptions options = default) { - var converted = SetInputFilesHelpers.ConvertInputFiles(files); + var converted = SetInputFilesHelpers.ConvertInputFiles(files.ToList()); await SendMessageToServerAsync("setInputFiles", new Dictionary { ["payloads"] = converted.Payloads, diff --git a/src/Playwright/Core/Frame.cs b/src/Playwright/Core/Frame.cs index dbb2d6c2b6..f68e976452 100644 --- a/src/Playwright/Core/Frame.cs +++ b/src/Playwright/Core/Frame.cs @@ -478,7 +478,12 @@ public async Task AddScriptTagAsync(FrameAddScriptTagOptions opt var content = options?.Content; if (!string.IsNullOrEmpty(options?.Path)) { - content = ScriptsHelper.AddSourceUrlToScript(File.ReadAllText(options.Path), options.Path); +#if NET + string source = await File.ReadAllTextAsync(options.Path).ConfigureAwait(false); +#else + string source = File.ReadAllText(options.Path); +#endif + content = ScriptsHelper.AddSourceUrlToScript(source, options.Path); } return await SendMessageToServerAsync("addScriptTag", new Dictionary @@ -496,7 +501,11 @@ public async Task AddStyleTagAsync(FrameAddStyleTagOptions optio var content = options?.Content; if (!string.IsNullOrEmpty(options?.Path)) { +#if NET + content = await File.ReadAllTextAsync(options.Path).ConfigureAwait(false); +#else content = File.ReadAllText(options.Path); +#endif content += "//# sourceURL=" + options.Path.Replace("\n", string.Empty); } @@ -515,7 +524,7 @@ public Task SetInputFilesAsync(string selector, string files, FrameSetInputFiles [MethodImpl(MethodImplOptions.NoInlining)] public async Task SetInputFilesAsync(string selector, IEnumerable files, FrameSetInputFilesOptions options = default) { - var converted = await SetInputFilesHelpers.ConvertInputFilesAsync(files, (BrowserContext)Page.Context).ConfigureAwait(false); + var converted = await SetInputFilesHelpers.ConvertInputFilesAsync(files.ToList(), (BrowserContext)Page.Context).ConfigureAwait(false); #pragma warning disable CS0612 // Type or member is obsolete await _setInputFilesAsync(selector, converted, options?.NoWaitAfter, options?.Timeout, options?.Strict).ConfigureAwait(false); #pragma warning restore CS0612 // Type or member is obsolete @@ -528,7 +537,7 @@ public Task SetInputFilesAsync(string selector, FilePayload files, FrameSetInput [MethodImpl(MethodImplOptions.NoInlining)] public async Task SetInputFilesAsync(string selector, IEnumerable files, FrameSetInputFilesOptions options = default) { - var converted = SetInputFilesHelpers.ConvertInputFiles(files); + var converted = SetInputFilesHelpers.ConvertInputFiles(files.ToList()); #pragma warning disable CS0612 // Type or member is obsolete await _setInputFilesAsync(selector, converted, noWaitAfter: options?.NoWaitAfter, timeout: options?.Timeout, options?.Strict).ConfigureAwait(false); #pragma warning restore CS0612 // Type or member is obsolete diff --git a/src/Playwright/Core/Page.cs b/src/Playwright/Core/Page.cs index 45f0f9dc37..cd9cf8a515 100644 --- a/src/Playwright/Core/Page.cs +++ b/src/Playwright/Core/Page.cs @@ -711,7 +711,11 @@ public async Task ScreenshotAsync(PageScreenshotOptions options = defaul if (!string.IsNullOrEmpty(options.Path)) { Directory.CreateDirectory(new FileInfo(options.Path).Directory.FullName); +#if NET + await File.WriteAllBytesAsync(options.Path, result).ConfigureAwait(false); +#else File.WriteAllBytes(options.Path, result); +#endif } return result; @@ -916,7 +920,11 @@ public async Task PdfAsync(PagePdfOptions options = default) if (!string.IsNullOrEmpty(options?.Path)) { Directory.CreateDirectory(new FileInfo(options.Path).Directory.FullName); +#if NET + await File.WriteAllBytesAsync(options.Path, result).ConfigureAwait(false); +#else File.WriteAllBytes(options.Path, result); +#endif } return result; diff --git a/src/Playwright/Core/Request.cs b/src/Playwright/Core/Request.cs index f3b65b3fed..f94aa7b5f1 100644 --- a/src/Playwright/Core/Request.cs +++ b/src/Playwright/Core/Request.cs @@ -70,12 +70,11 @@ public IFrame Frame var frame = _initializer.Frame; if (frame.Page == null) { - throw new PlaywrightException(string.Join("\n", new string[] - { - "Frame for this navigation request is not available, because the request", - "was issued before the frame is created. You can check whether the request", - "is a navigation request by calling isNavigationRequest() method.", - })); + throw new PlaywrightException(""" + Frame for this navigation request is not available, because the request + was issued before the frame is created. You can check whether the request + is a navigation request by calling isNavigationRequest() method. + """); } return frame; } diff --git a/src/Playwright/Core/Route.cs b/src/Playwright/Core/Route.cs index 0ac2ef1561..2113f5404c 100644 --- a/src/Playwright/Core/Route.cs +++ b/src/Playwright/Core/Route.cs @@ -192,7 +192,11 @@ private async Task> NormalizeFulfillParametersAsync( if (!string.IsNullOrEmpty(path)) { +#if NET + byte[] content = await File.ReadAllBytesAsync(path).ConfigureAwait(false); +#else byte[] content = File.ReadAllBytes(path); +#endif resultBody = Convert.ToBase64String(content); isBase64 = true; length = resultBody.Length; diff --git a/src/Playwright/Core/Waiter.cs b/src/Playwright/Core/Waiter.cs index e46ad8d32e..bc55bd96ea 100644 --- a/src/Playwright/Core/Waiter.cs +++ b/src/Playwright/Core/Waiter.cs @@ -150,10 +150,16 @@ internal void RejectOnTimeout(int? timeout, string message) return; } +#pragma warning disable CA2000 // Dispose objects before losing scope var cts = new CancellationTokenSource(); +#pragma warning restore CA2000 // Dispose objects before losing scope RejectOn( new TaskCompletionSource().Task.WithTimeout(timeout.Value, _ => new TimeoutException(message), cts.Token), - () => cts.Cancel()); + () => + { + cts.Cancel(); + cts.Dispose(); + }); } internal Task WaitForEventAsync(object eventSource, string e, Func predicate) @@ -250,7 +256,11 @@ private static async Task WrapActionAsync(Func action, CancellationTokenSo } catch { +#if NET + await cts.CancelAsync().ConfigureAwait(false); +#else cts.Cancel(); +#endif throw; } } diff --git a/src/Playwright/Helpers/Driver.cs b/src/Playwright/Helpers/Driver.cs index 3ac3bad489..1270736c03 100644 --- a/src/Playwright/Helpers/Driver.cs +++ b/src/Playwright/Helpers/Driver.cs @@ -100,9 +100,14 @@ private static bool TryGetCodeBase(Assembly assembly, out Uri codeBase) { try { - // assembly.CodeBase might throw with: - // System.NotSupportedException: CodeBase is not supported on assemblies loaded from a single-file bundle. + // assembly.Location/CodeBase might throw with: + // System.NotSupportedException: Location/CodeBase is not supported on assemblies loaded from a single-file bundle. + // Still using CodeBase for legacy .NET because of behaviour difference with shadow-copy (see https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.location?view=net-8.0#remarks) +#if NET + Uri.TryCreate(assembly.Location, UriKind.Absolute, out codeBase); +#else Uri.TryCreate(assembly.CodeBase, UriKind.Absolute, out codeBase); +#endif return true; } catch (NotSupportedException) diff --git a/src/Playwright/Helpers/SetInputFilesHelpers.cs b/src/Playwright/Helpers/SetInputFilesHelpers.cs index a077784ecc..72c92bb890 100644 --- a/src/Playwright/Helpers/SetInputFilesHelpers.cs +++ b/src/Playwright/Helpers/SetInputFilesHelpers.cs @@ -65,7 +65,7 @@ private static (string[] LocalPaths, string LocalDirectory) ResolvePathsAndDirec return (localPaths?.ToArray(), localDirectory); } - private static IEnumerable GetFilesRecursive(string directory) + private static List GetFilesRecursive(string directory) { var files = new List(); files.AddRange(Directory.GetFiles(directory)); @@ -76,16 +76,16 @@ private static IEnumerable GetFilesRecursive(string directory) return files; } - public static async Task ConvertInputFilesAsync(IEnumerable files, BrowserContext context) + public static async Task ConvertInputFilesAsync(List files, BrowserContext context) { - if (!files.Any()) + if (files.Count == 0) { return new() { Payloads = [] }; } - var (localPaths, localDirectory) = ResolvePathsAndDirectoryForInputFiles(files.ToList()); + var (localPaths, localDirectory) = ResolvePathsAndDirectoryForInputFiles(files); if (context._connection.IsRemote) { - files = localDirectory != null ? GetFilesRecursive(localDirectory) : localPaths; + files = localDirectory != null ? GetFilesRecursive(localDirectory) : localPaths.ToList(); var result = await context.SendMessageToServerAsync("createTempFiles", new Dictionary { ["rootDirName"] = localDirectory != null ? new DirectoryInfo(localDirectory).Name : null, @@ -97,12 +97,12 @@ public static async Task ConvertInputFilesAsync(IEnumerable< }).ToArray(), }).ConfigureAwait(false); var writableStreams = result.Value.GetProperty("writableStreams").EnumerateArray(); - if (writableStreams.Count() != files.Count()) + if (writableStreams.Count() != files.Count) { - throw new Exception("Mismatch between the number of files and the number of writeable streams"); + throw new PlaywrightException("Mismatch between the number of files and the number of writeable streams"); } var streams = new List(); - for (var i = 0; i < files.Count(); i++) + for (var i = 0; i < files.Count; i++) { #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task await using (var writeableStream = context._connection.GetObject(writableStreams.ElementAt(i).GetProperty("guid").ToString()) as WritableStream) @@ -122,7 +122,7 @@ public static async Task ConvertInputFilesAsync(IEnumerable< return new() { LocalPaths = localPaths, LocalDirectory = localDirectory }; } - public static SetInputFilesFiles ConvertInputFiles(IEnumerable files) + public static SetInputFilesFiles ConvertInputFiles(List files) { var filePayloadExceedsSizeLimit = files.Sum(f => f.Buffer.Length) > SizeLimitInBytes; if (filePayloadExceedsSizeLimit) diff --git a/src/Playwright/Helpers/StringExtensions.cs b/src/Playwright/Helpers/StringExtensions.cs index 47dc084ccb..87dccd2ae2 100644 --- a/src/Playwright/Helpers/StringExtensions.cs +++ b/src/Playwright/Helpers/StringExtensions.cs @@ -615,7 +615,11 @@ public static Dictionary ParseQueryString(this string query) var result = new Dictionary(); +#if NET + if (query.StartsWith('?')) +#else if (query.StartsWith("?", StringComparison.InvariantCultureIgnoreCase)) +#endif { query = query.Substring(1, query.Length - 1); } diff --git a/src/Playwright/Helpers/URLMatch.cs b/src/Playwright/Helpers/URLMatch.cs index ff5c60922e..62b3df9517 100644 --- a/src/Playwright/Helpers/URLMatch.cs +++ b/src/Playwright/Helpers/URLMatch.cs @@ -65,7 +65,11 @@ public bool Match(string url) internal static string JoinWithBaseURL(string baseUrl, string url) { if (string.IsNullOrEmpty(baseUrl) +#if NET + || (url?.StartsWith('*') ?? false) +#else || (url?.StartsWith("*", StringComparison.InvariantCultureIgnoreCase) ?? false) +#endif || !Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) { return url; diff --git a/src/Playwright/Playwright.cs b/src/Playwright/Playwright.cs index b1c493a39d..e9f5ecf451 100644 --- a/src/Playwright/Playwright.cs +++ b/src/Playwright/Playwright.cs @@ -40,8 +40,10 @@ public static class Playwright /// A that completes when the playwright driver is ready to be used. public static async Task CreateAsync() { +#pragma warning disable CA2000 // Dispose objects before losing scope var transport = new StdIOTransport(); var connection = new Connection(); +#pragma warning restore CA2000 // Dispose objects before losing scope transport.MessageReceived += (_, message) => { Connection.TraceMessage("pw:channel:recv", message); diff --git a/src/Playwright/Playwright.csproj b/src/Playwright/Playwright.csproj index 9ae89e6229..95255bccd1 100644 --- a/src/Playwright/Playwright.csproj +++ b/src/Playwright/Playwright.csproj @@ -7,7 +7,7 @@ The .NET port of Playwright, used to automate Chromium, Firefox and WebKit with a single API. Playwright enables reliable end-to-end testing for modern web apps. It is built to enable cross-browser web automation that is ever-green, capable, reliable and fast. Learn more at https://playwright.dev/dotnet/. icon.png - netstandard2.0 + netstandard2.0;net8.0 true Microsoft.Playwright.xml true @@ -25,15 +25,17 @@ + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - diff --git a/src/Playwright/Transport/Connection.cs b/src/Playwright/Transport/Connection.cs index 23d837217b..41b9eb59ab 100644 --- a/src/Playwright/Transport/Connection.cs +++ b/src/Playwright/Transport/Connection.cs @@ -314,105 +314,78 @@ internal void Dispatch(PlaywrightServerMessage message) private ChannelOwner CreateRemoteObject(string parentGuid, ChannelOwnerType type, string guid, JsonElement? initializer) { - ChannelOwner result = null; var parent = string.IsNullOrEmpty(parentGuid) ? _rootObject : Objects[parentGuid]; switch (type) { case ChannelOwnerType.APIRequestContext: - result = new APIRequestContext(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new APIRequestContext(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Artifact: - result = new Artifact(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Artifact(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.BindingCall: - result = new BindingCall(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new BindingCall(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Playwright: - result = new PlaywrightImpl(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new PlaywrightImpl(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Browser: var browserInitializer = initializer?.ToObject(DefaultJsonSerializerOptions); - result = new Browser(parent, guid, browserInitializer); - break; + return new Browser(parent, guid, browserInitializer); case ChannelOwnerType.BrowserType: var browserTypeInitializer = initializer?.ToObject(DefaultJsonSerializerOptions); - result = new Core.BrowserType(parent, guid, browserTypeInitializer); - break; + return new Core.BrowserType(parent, guid, browserTypeInitializer); case ChannelOwnerType.BrowserContext: var browserContextInitializer = initializer?.ToObject(DefaultJsonSerializerOptions); - result = new BrowserContext(parent, guid, browserContextInitializer); - break; + return new BrowserContext(parent, guid, browserContextInitializer); case ChannelOwnerType.CDPSession: - result = new CDPSession(parent, guid); - break; + return new CDPSession(parent, guid); case ChannelOwnerType.Dialog: - result = new Dialog(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Dialog(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.ElementHandle: - result = new ElementHandle(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new ElementHandle(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Frame: - result = new Frame(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Frame(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.JSHandle: - result = new JSHandle(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new JSHandle(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.JsonPipe: - result = new JsonPipe(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new JsonPipe(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.LocalUtils: - result = new LocalUtils(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); + var localUtils = new LocalUtils(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); if (LocalUtils == null) { - LocalUtils = result as LocalUtils; + LocalUtils = localUtils; } - break; + return localUtils; case ChannelOwnerType.Page: - result = new Page(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Page(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Request: - result = new Request(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Request(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Response: - result = new Response(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Response(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Route: - result = new Route(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Route(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Worker: - result = new Worker(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new Worker(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.WebSocket: - result = new WebSocket(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new WebSocket(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.WebSocketRoute: - result = new WebSocketRoute(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); - break; + return new WebSocketRoute(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); case ChannelOwnerType.Selectors: - result = new Selectors(parent, guid); - break; + return new Selectors(parent, guid); case ChannelOwnerType.SocksSupport: - result = new SocksSupport(parent, guid); - break; + return new SocksSupport(parent, guid); case ChannelOwnerType.Stream: - result = new Stream(parent, guid); - break; + return new Stream(parent, guid); case ChannelOwnerType.WritableStream: - result = new WritableStream(parent, guid); - break; + return new WritableStream(parent, guid); case ChannelOwnerType.Tracing: - result = new Tracing(parent, guid); - break; + return new Tracing(parent, guid); case ChannelOwnerType.Electron: case ChannelOwnerType.Android: - result = null; break; default: Debug.Fail($"Missing Playwright type binding for '{type}'"); break; } - return result; + return null; } internal void DoClose(Exception cause = null) @@ -507,7 +480,11 @@ internal async Task WrapApiCallAsync(Func> action, bool isInternal { apiBoundaryReached = true; } +#if NET + var hasCleanMethodName = !methodName.StartsWith('<'); +#else var hasCleanMethodName = !methodName.StartsWith("<", StringComparison.InvariantCultureIgnoreCase); +#endif if (hasCleanMethodName) { lastInternalApiName = methodName; diff --git a/src/Playwright/Transport/Converters/EvaluateArgumentValueConverter.cs b/src/Playwright/Transport/Converters/EvaluateArgumentValueConverter.cs index aed915e39a..e375acba0c 100644 --- a/src/Playwright/Transport/Converters/EvaluateArgumentValueConverter.cs +++ b/src/Playwright/Transport/Converters/EvaluateArgumentValueConverter.cs @@ -30,7 +30,6 @@ using System.Globalization; using System.Linq; using System.Numerics; -using System.Runtime.Serialization; using System.Text.Json; using System.Text.RegularExpressions; using Microsoft.Playwright.Core; @@ -345,7 +344,7 @@ private static object ParseEvaluateResultToExpando(JsonElement result, IDictiona if (result.TryGetProperty("e", out var error)) { - return new Exception(error.GetProperty("s").ToString()); + return new PlaywrightException(error.GetProperty("s").ToString()); } if (result.TryGetProperty("r", out var regex)) @@ -399,16 +398,25 @@ internal class VisitorInfo internal VisitorInfo() { Visited = new Dictionary(); - IDGenerator = new ObjectIDGenerator(); + ObjectToId = new Dictionary(); } internal Dictionary Visited { get; set; } internal int LastId { get; set; } - private ObjectIDGenerator IDGenerator { get; } + private Dictionary ObjectToId { get; } internal long Identity(object obj) - => IDGenerator.GetId(obj, out _); + { + if (ObjectToId.TryGetValue(obj, out long id)) + { + return id; + } + + id = ObjectToId.Count + 1; + ObjectToId.Add(obj, id); + return id; + } } } diff --git a/src/Playwright/Transport/StdIOTransport.cs b/src/Playwright/Transport/StdIOTransport.cs index 94c578b642..0ba6086423 100644 --- a/src/Playwright/Transport/StdIOTransport.cs +++ b/src/Playwright/Transport/StdIOTransport.cs @@ -100,8 +100,13 @@ public async Task SendAsync(byte[] message) ll[2] = (byte)((len >> 16) & 0xFF); ll[3] = (byte)((len >> 24) & 0xFF); +#if NET + await _process.StandardInput.BaseStream.WriteAsync(ll.AsMemory(0, 4), _readerCancellationSource.Token).ConfigureAwait(false); + await _process.StandardInput.BaseStream.WriteAsync(message.AsMemory(0, len), _readerCancellationSource.Token).ConfigureAwait(false); +#else await _process.StandardInput.BaseStream.WriteAsync(ll, 0, 4, _readerCancellationSource.Token).ConfigureAwait(false); await _process.StandardInput.BaseStream.WriteAsync(message, 0, len, _readerCancellationSource.Token).ConfigureAwait(false); +#endif await _process.StandardInput.BaseStream.FlushAsync(_readerCancellationSource.Token).ConfigureAwait(false); } } @@ -186,7 +191,7 @@ private static void StartProcessWithUTF8IOEncoding(Process process) } } - private static Task ScheduleTransportTaskAsync(Func func, CancellationToken cancellationToken) + private static Task ScheduleTransportTaskAsync(Func func, CancellationToken cancellationToken) => Task.Factory.StartNew(() => func(cancellationToken), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); private void Dispose(bool disposing) @@ -210,7 +215,11 @@ private async Task GetResponseAsync(CancellationToken token) while (!token.IsCancellationRequested && !_process.HasExited) { +#if NET + int read = await stream.BaseStream.ReadAsync(buffer.AsMemory(0, DefaultBufferSize), token).ConfigureAwait(false); +#else int read = await stream.BaseStream.ReadAsync(buffer, 0, DefaultBufferSize, token).ConfigureAwait(false); +#endif if (!token.IsCancellationRequested) { _data.AddRange(new ArraySegment(buffer, 0, read));