From 9b1d5f22f8176a99b8c7e3d7272024c8cac1269e Mon Sep 17 00:00:00 2001 From: Jim Evans Date: Tue, 12 Oct 2021 13:56:48 -0400 Subject: [PATCH] [dotnet] Add CDP support for Chromium 95 --- dotnet/selenium-dotnet-version.bzl | 1 + .../src/webdriver/DevTools/DevToolsDomains.cs | 1 + .../src/webdriver/DevTools/v95/V95Domains.cs | 66 ++++ .../webdriver/DevTools/v95/V95JavaScript.cs | 186 ++++++++++ dotnet/src/webdriver/DevTools/v95/V95Log.cs | 80 +++++ .../src/webdriver/DevTools/v95/V95Network.cs | 334 ++++++++++++++++++ .../src/webdriver/DevTools/v95/V95Target.cs | 120 +++++++ 7 files changed, 788 insertions(+) create mode 100644 dotnet/src/webdriver/DevTools/v95/V95Domains.cs create mode 100644 dotnet/src/webdriver/DevTools/v95/V95JavaScript.cs create mode 100644 dotnet/src/webdriver/DevTools/v95/V95Log.cs create mode 100644 dotnet/src/webdriver/DevTools/v95/V95Network.cs create mode 100644 dotnet/src/webdriver/DevTools/v95/V95Target.cs diff --git a/dotnet/selenium-dotnet-version.bzl b/dotnet/selenium-dotnet-version.bzl index 90fbba08f1e94..e1c5a6c583bf7 100644 --- a/dotnet/selenium-dotnet-version.bzl +++ b/dotnet/selenium-dotnet-version.bzl @@ -9,6 +9,7 @@ SUPPORTED_DEVTOOLS_VERSIONS = [ "v85", "v93", "v94", + "v95", ] ASSEMBLY_COMPANY = "Selenium Committers" diff --git a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs index fdae48577957f..b6e9a8525603d 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs @@ -37,6 +37,7 @@ public abstract class DevToolsDomains // added to this dictionary. private static readonly Dictionary SupportedDevToolsVersions = new Dictionary() { + { 95, typeof(V95.V95Domains) }, { 94, typeof(V94.V94Domains) }, { 93, typeof(V93.V93Domains) }, { 85, typeof(V85.V85Domains) } diff --git a/dotnet/src/webdriver/DevTools/v95/V95Domains.cs b/dotnet/src/webdriver/DevTools/v95/V95Domains.cs new file mode 100644 index 0000000000000..03fd3962275b7 --- /dev/null +++ b/dotnet/src/webdriver/DevTools/v95/V95Domains.cs @@ -0,0 +1,66 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenQA.Selenium.DevTools.V95 +{ + /// + /// Class containing the domain implementation for version 94 of the DevTools Protocol. + /// + public class V95Domains : DevToolsDomains + { + private DevToolsSessionDomains domains; + + public V95Domains(DevToolsSession session) + { + this.domains = new DevToolsSessionDomains(session); + } + + /// + /// Gets the DevTools Protocol version for which this class is valid. + /// + public static int DevToolsVersion => 95; + + /// + /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. + /// + public override DevTools.DevToolsSessionDomains VersionSpecificDomains => this.domains; + + /// + /// Gets the object used for manipulating network information in the browser. + /// + public override DevTools.Network Network => new V95Network(domains.Network, domains.Fetch); + + /// + /// Gets the object used for manipulating the browser's JavaScript execution. + /// + public override JavaScript JavaScript => new V95JavaScript(domains.Runtime, domains.Page); + + /// + /// Gets the object used for manipulating DevTools Protocol targets. + /// + public override DevTools.Target Target => new V95Target(domains.Target); + + /// + /// Gets the object used for manipulating the browser's logs. + /// + public override DevTools.Log Log => new V95Log(domains.Log); + } +} diff --git a/dotnet/src/webdriver/DevTools/v95/V95JavaScript.cs b/dotnet/src/webdriver/DevTools/v95/V95JavaScript.cs new file mode 100644 index 0000000000000..bf29389d628d2 --- /dev/null +++ b/dotnet/src/webdriver/DevTools/v95/V95JavaScript.cs @@ -0,0 +1,186 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using OpenQA.Selenium.DevTools.V95.Page; +using OpenQA.Selenium.DevTools.V95.Runtime; + +namespace OpenQA.Selenium.DevTools.V95 +{ + /// + /// Class containing the JavaScript implementation for version 89 of the DevTools Protocol. + /// + public class V95JavaScript : JavaScript + { + private RuntimeAdapter runtime; + private PageAdapter page; + + /// + /// Initializes a new instance of the class. + /// + /// The DevTools Protocol adapter for the Runtime domain. + /// The DevTools Protocol adapter for the Page domain. + public V95JavaScript(RuntimeAdapter runtime, PageAdapter page) + { + this.runtime = runtime; + this.page = page; + this.runtime.BindingCalled += OnRuntimeBindingCalled; + this.runtime.ConsoleAPICalled += OnRuntimeConsoleApiCalled; + this.runtime.ExceptionThrown += OnRuntimeExceptionThrown; + } + + /// + /// Asynchronously enables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableRuntime() + { + await runtime.Enable(); + } + + /// + /// Asynchronously disables the Runtime domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableRuntime() + { + await runtime.Disable(); + } + + /// + /// Asynchronously enables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task EnablePage() + { + await page.Enable(); + } + + /// + /// Asynchronously disables the Page domain in the DevTools Protocol. + /// + /// A task that represents the asynchronous operation. + public override async Task DisablePage() + { + await page.Disable(); + } + + /// + /// Adds a binding to a specific JavaScript name. + /// + /// The name to which to bind to. + /// A task that represents the asynchronous operation. + public override async Task AddBinding(string name) + { + await runtime.AddBinding(new AddBindingCommandSettings() { Name = name }); + } + + /// + /// Removes a binding from a specific JavaScript name. + /// + /// The name to which to remove the bind from. + /// A task that represents the asynchronous operation. + public override async Task RemoveBinding(string name) + { + await runtime.RemoveBinding(new RemoveBindingCommandSettings() { Name = name }); + } + + /// + /// Adds a JavaScript snippet to evaluate when a new document is opened. + /// + /// The script to add to be evaluated when a new document is opened. + /// A task that represents the asynchronous operation. The task result contains the internal ID of the script. + public override async Task AddScriptToEvaluateOnNewDocument(string script) + { + var result = await page.AddScriptToEvaluateOnNewDocument(new AddScriptToEvaluateOnNewDocumentCommandSettings() { Source = script }); + return result.Identifier; + } + + /// + /// Removes a JavaScript snippet from evaluate when a new document is opened. + /// + /// The ID of the script to be removed. + /// A task that represents the asynchronous operation. + public override async Task RemoveScriptToEvaluateOnNewDocument(string scriptId) + { + await page.RemoveScriptToEvaluateOnNewDocument(new RemoveScriptToEvaluateOnNewDocumentCommandSettings() { Identifier = scriptId }); + } + + /// + /// Evaluates a JavaScript snippet. It does not return a value. + /// + /// The script to evaluate + /// A task that represents the asynchronous operation. + /// + /// This method is internal to the operation of pinned scripts in Selenium, and + /// is therefore internal by design. + /// + internal override async Task Evaluate(string script) + { + await runtime.Evaluate(new EvaluateCommandSettings { Expression = script }); + } + + private void OnRuntimeBindingCalled(object sender, Runtime.BindingCalledEventArgs e) + { + BindingCalledEventArgs wrapped = new BindingCalledEventArgs() + { + ExecutionContextId = e.ExecutionContextId, + Name = e.Name, + Payload = e.Payload + }; + + this.OnBindingCalled(wrapped); + } + + private void OnRuntimeExceptionThrown(object sender, Runtime.ExceptionThrownEventArgs e) + { + // TODO: Collect stack trace elements + var wrapped = new ExceptionThrownEventArgs() + { + Timestamp = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + Message = e.ExceptionDetails.Text + }; + + this.OnExceptionThrown(wrapped); + } + + private void OnRuntimeConsoleApiCalled(object sender, ConsoleAPICalledEventArgs e) + { + List args = new List(); + foreach (var arg in e.Args) + { + string argValue = null; + if (arg.Value != null) + { + argValue = arg.Value.ToString(); + } + args.Add(new ConsoleApiArgument() { Type = arg.Type.ToString(), Value = argValue }); + } + + var wrapped = new ConsoleApiCalledEventArgs() + { + Timestamp = new DateTime(1979, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(e.Timestamp), + Type = e.Type, + Arguments = args.AsReadOnly() + }; + + this.OnConsoleApiCalled(wrapped); + } + } +} diff --git a/dotnet/src/webdriver/DevTools/v95/V95Log.cs b/dotnet/src/webdriver/DevTools/v95/V95Log.cs new file mode 100644 index 0000000000000..44751c7d9dccf --- /dev/null +++ b/dotnet/src/webdriver/DevTools/v95/V95Log.cs @@ -0,0 +1,80 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using OpenQA.Selenium.DevTools.V95.Log; + +namespace OpenQA.Selenium.DevTools.V95 +{ + /// + /// Class containing the browser's log as referenced by version 89 of the DevTools Protocol. + /// + public class V95Log : DevTools.Log + { + private LogAdapter adapter; + + /// + /// Initializes a new instance of the class. + /// + /// The adapter for the Log domain. + public V95Log(LogAdapter adapter) + { + this.adapter = adapter; + this.adapter.EntryAdded += OnAdapterEntryAdded; + } + + /// + /// Asynchronously enables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Enable() + { + await adapter.Enable(); + } + + /// + /// Asynchronously disables manipulation of the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Disable() + { + await adapter.Disable(); + } + + /// + /// Asynchronously clears the browser's log. + /// + /// A task that represents the asynchronous operation. + public override async Task Clear() + { + await adapter.Clear(); + } + + private void OnAdapterEntryAdded(object sender, Log.EntryAddedEventArgs e) + { + EntryAddedEventArgs propagated = new EntryAddedEventArgs(); + propagated.Entry = new LogEntry(); + propagated.Entry.Kind = e.Entry.Source.ToString(); + propagated.Entry.Message = e.Entry.Text; + this.OnEntryAdded(propagated); + } + } +} diff --git a/dotnet/src/webdriver/DevTools/v95/V95Network.cs b/dotnet/src/webdriver/DevTools/v95/V95Network.cs new file mode 100644 index 0000000000000..6b80a7b467321 --- /dev/null +++ b/dotnet/src/webdriver/DevTools/v95/V95Network.cs @@ -0,0 +1,334 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using OpenQA.Selenium.DevTools.V95.Fetch; +using OpenQA.Selenium.DevTools.V95.Network; + +namespace OpenQA.Selenium.DevTools.V95 +{ + /// + /// Class providing functionality for manipulating network calls using version 89 of the DevTools Protocol + /// + public class V95Network : DevTools.Network + { + private FetchAdapter fetch; + private NetworkAdapter network; + + /// + /// Initializes a new instance of the class. + /// + /// The adapter for the Network domain. + /// The adapter for the Fetch domain. + public V95Network(NetworkAdapter network, FetchAdapter fetch) + { + this.network = network; + this.fetch = fetch; + fetch.AuthRequired += OnFetchAuthRequired; + fetch.RequestPaused += OnFetchRequestPaused; + } + + /// + /// Asynchronously disables network caching. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableNetworkCaching() + { + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = true }); + } + + /// + /// Asynchronously enables network caching. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableNetworkCaching() + { + await network.SetCacheDisabled(new SetCacheDisabledCommandSettings() { CacheDisabled = false }); + } + + public override async Task EnableNetwork() + { + await network.Enable(new Network.EnableCommandSettings()); + } + + public override async Task DisableNetwork() + { + await network.Disable(); + } + + /// + /// Asynchronously enables the fetch domain for all URL patterns. + /// + /// A task that represents the asynchronous operation. + public override async Task EnableFetchForAllPatterns() + { + await fetch.Enable(new OpenQA.Selenium.DevTools.V95.Fetch.EnableCommandSettings() + { + Patterns = new OpenQA.Selenium.DevTools.V95.Fetch.RequestPattern[] + { + new OpenQA.Selenium.DevTools.V95.Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Request }, + new OpenQA.Selenium.DevTools.V95.Fetch.RequestPattern() { UrlPattern = "*", RequestStage = RequestStage.Response } + }, + HandleAuthRequests = true + }); + } + + /// + /// Asynchronously diables the fetch domain. + /// + /// A task that represents the asynchronous operation. + public override async Task DisableFetch() + { + await fetch.Disable(); + } + + /// + /// Asynchronously sets the override of the user agent settings. + /// + /// A object containing the user agent values to override. + /// A task that represents the asynchronous operation. + public override async Task SetUserAgentOverride(UserAgent userAgent) + { + await network.SetUserAgentOverride(new SetUserAgentOverrideCommandSettings() + { + UserAgent = userAgent.UserAgentString, + AcceptLanguage = userAgent.AcceptLanguage, + Platform = userAgent.Platform + }); + } + + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// A task that represents the asynchronous operation. + public override async Task ContinueRequest(HttpRequestData requestData) + { + var commandSettings = new ContinueRequestCommandSettings() + { + RequestId = requestData.RequestId, + Method = requestData.Method, + Url = requestData.Url, + }; + + if (requestData.Headers.Count > 0) + { + List headers = new List(); + foreach (KeyValuePair headerPair in requestData.Headers) + { + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); + } + + commandSettings.Headers = headers.ToArray(); + } + + if (!string.IsNullOrEmpty(requestData.PostData)) + { + commandSettings.PostData = requestData.PostData; + } + + await fetch.ContinueRequest(commandSettings); + } + + /// + /// Asynchronously continues an intercepted network request. + /// + /// The of the request. + /// The with which to respond to the request + /// A task that represents the asynchronous operation. + public override async Task ContinueRequestWithResponse(HttpRequestData requestData, HttpResponseData responseData) + { + var commandSettings = new FulfillRequestCommandSettings() + { + RequestId = requestData.RequestId, + ResponseCode = responseData.StatusCode, + }; + + if (responseData.Headers.Count > 0 || responseData.CookieHeaders.Count > 0) + { + List headers = new List(); + foreach (KeyValuePair headerPair in responseData.Headers) + { + headers.Add(new HeaderEntry() { Name = headerPair.Key, Value = headerPair.Value }); + } + + foreach (string cookieHeader in responseData.CookieHeaders) + { + headers.Add(new HeaderEntry() { Name = "Set-Cookie", Value = cookieHeader }); + } + + commandSettings.ResponseHeaders = headers.ToArray(); + } + + if (!string.IsNullOrEmpty(responseData.Body)) + { + commandSettings.Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(responseData.Body)); + } + + await fetch.FulfillRequest(commandSettings); + } + + /// + /// Asynchronously contines an intercepted network call without modification. + /// + /// The of the network call. + /// A task that represents the asynchronous operation. + public override async Task ContinueRequestWithoutModification(HttpRequestData requestData) + { + await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = requestData.RequestId }); + } + + /// + /// Asynchronously continues an intercepted network call using authentication. + /// + /// The ID of the network request for which to continue with authentication. + /// The user name with which to authenticate. + /// The password with which to authenticate. + /// A task that represents the asynchronous operation. + public override async Task ContinueWithAuth(string requestId, string userName, string password) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + { + RequestId = requestId, + AuthChallengeResponse = new V95.Fetch.AuthChallengeResponse() + { + Response = V95.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, + Username = userName, + Password = password + } + }); + } + + /// + /// Asynchronously cancels authorization of an intercepted network request. + /// + /// The ID of the network request for which to cancel authentication. + /// A task that represents the asynchronous operation. + public override async Task CancelAuth(string requestId) + { + await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() + { + RequestId = requestId, + AuthChallengeResponse = new OpenQA.Selenium.DevTools.V95.Fetch.AuthChallengeResponse() + { + Response = V95.Fetch.AuthChallengeResponseResponseValues.CancelAuth + } + }); + } + + /// + /// Asynchronously adds the response body to the provided object. + /// + /// The object to which to add the response body. + /// A task that represents the asynchronous operation. + public override async Task AddResponseBody(HttpResponseData responseData) + { + var bodyResponse = await fetch.GetResponseBody(new Fetch.GetResponseBodyCommandSettings() { RequestId = responseData.RequestId }); + if (bodyResponse.Base64Encoded) + { + responseData.Body = Encoding.UTF8.GetString(Convert.FromBase64String(bodyResponse.Body)); + } + else + { + responseData.Body = bodyResponse.Body; + } + } + + /// + /// Asynchronously contines an intercepted network response without modification. + /// + /// The of the network response. + /// A task that represents the asynchronous operation. + public override async Task ContinueResponseWithoutModification(HttpResponseData responseData) + { + await fetch.ContinueRequest(new ContinueRequestCommandSettings() { RequestId = responseData.RequestId }); + } + + private void OnFetchAuthRequired(object sender, Fetch.AuthRequiredEventArgs e) + { + AuthRequiredEventArgs wrapped = new AuthRequiredEventArgs() + { + RequestId = e.RequestId, + Uri = e.Request.Url + }; + + this.OnAuthRequired(wrapped); + } + + private void OnFetchRequestPaused(object sender, Fetch.RequestPausedEventArgs e) + { + if (e.ResponseErrorReason == null && e.ResponseStatusCode == null) + { + RequestPausedEventArgs wrapped = new RequestPausedEventArgs(); + wrapped.RequestData = new HttpRequestData() + { + RequestId = e.RequestId, + Method = e.Request.Method, + Url = e.Request.Url, + PostData = e.Request.PostData, + Headers = new Dictionary(e.Request.Headers) + }; + + this.OnRequestPaused(wrapped); + } + else + { + ResponsePausedEventArgs wrappedResponse = new ResponsePausedEventArgs(); + wrappedResponse.ResponseData = new HttpResponseData() + { + RequestId = e.RequestId, + Url = e.Request.Url, + ResourceType = e.ResourceType.ToString() + }; + + if (e.ResponseStatusCode.HasValue) + { + wrappedResponse.ResponseData.StatusCode = e.ResponseStatusCode.Value; + } + + if (e.ResponseHeaders != null) + { + foreach (var header in e.ResponseHeaders) + { + if (header.Name.ToLowerInvariant() == "set-cookie") + { + wrappedResponse.ResponseData.CookieHeaders.Add(header.Value); + } + else + { + if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name)) + { + string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name]; + wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value; + } + else + { + wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value); + } + } + } + } + + this.OnResponsePaused(wrappedResponse); + } + } + } +} diff --git a/dotnet/src/webdriver/DevTools/v95/V95Target.cs b/dotnet/src/webdriver/DevTools/v95/V95Target.cs new file mode 100644 index 0000000000000..1202280c2f741 --- /dev/null +++ b/dotnet/src/webdriver/DevTools/v95/V95Target.cs @@ -0,0 +1,120 @@ +// +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Threading.Tasks; +using OpenQA.Selenium.DevTools.V95.Target; + +namespace OpenQA.Selenium.DevTools.V95 +{ + /// + /// Class providing functionality for manipulating targets for version 94 of the DevTools Protocol + /// + public class V95Target : DevTools.Target + { + private TargetAdapter adapter; + + /// + /// Initializes a new instance of the class. + /// + /// The adapter for the Target domain. + public V95Target(TargetAdapter adapter) + { + this.adapter = adapter; + adapter.DetachedFromTarget += OnDetachedFromTarget; + } + + /// + /// Asynchronously gets the targets available for this session. + /// + /// + /// A task that represents the asynchronous operation. The task result + /// contains the list of objects describing the + /// targets available for this session. + /// + public override async Task> GetTargets() + { + List targets = new List(); + var response = await adapter.GetTargets(); + for (int i = 0; i < response.TargetInfos.Length; i++) + { + var targetInfo = response.TargetInfos[i]; + var mapped = new TargetInfo() + { + TargetId = targetInfo.TargetId, + Title = targetInfo.Title, + Type = targetInfo.Type, + Url = targetInfo.Url, + OpenerId = targetInfo.OpenerId, + BrowserContextId = targetInfo.BrowserContextId, + IsAttached = targetInfo.Attached + }; + targets.Add(mapped); + } + + return targets.AsReadOnly(); + } + + /// + /// Asynchronously attaches to a target. + /// + /// The ID of the target to which to attach. + /// + /// A task representing the asynchronous attach operation. The task result contains the + /// session ID established for commands to the target attached to. + /// + public override async Task AttachToTarget(string targetId) + { + var result = await adapter.AttachToTarget(new AttachToTargetCommandSettings() { TargetId = targetId, Flatten = true }); + return result.SessionId; + } + + /// + /// Asynchronously detaches from a target. + /// + /// The ID of the session of the target from which to detach. + /// The ID of the target from which to detach. + /// + /// A task representing the asynchronous detach operation. + public override async Task DetachFromTarget(string sessionId = null, string targetId = null) + { + await adapter.DetachFromTarget(new DetachFromTargetCommandSettings() + { + SessionId = sessionId, + TargetId = targetId + }); + } + + /// + /// Asynchronously sets the DevTools Protocol connection to automatically attach to new targets. + /// + /// A task that represents the asynchronous operation. + public override async Task SetAutoAttach() + { + await adapter.SetAutoAttach(new SetAutoAttachCommandSettings() { AutoAttach = true, WaitForDebuggerOnStart = false, Flatten = true }); + } + + private void OnDetachedFromTarget(object sender, DetachedFromTargetEventArgs e) + { + this.OnTargetDetached(new TargetDetachedEventArgs() { SessionId = e.SessionId, TargetId = e.TargetId }); + } + } +}