From 408fef35ebe178b422de65755566ffab8fa0ba75 Mon Sep 17 00:00:00 2001 From: Edwin van Vliet Date: Wed, 31 Jan 2024 16:51:17 +0100 Subject: [PATCH 1/2] Added SendCommands function to IDevToolsSession --- .../DevTools/DevToolsCommandSettings.cs | 36 ++++++++ .../src/webdriver/DevTools/DevToolsSession.cs | 84 +++++++++++++++++++ .../webdriver/DevTools/IDevToolsSession.cs | 10 +++ 3 files changed, 130 insertions(+) create mode 100644 dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs diff --git a/dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs b/dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs new file mode 100644 index 0000000000000..af4c6023dba84 --- /dev/null +++ b/dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs @@ -0,0 +1,36 @@ +// +// 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. +// + +namespace OpenQA.Selenium.DevTools +{ + public class DevToolsCommandSettings + { + public DevToolsCommandSettings(string commandName) + { + if (string.IsNullOrWhiteSpace(commandName)) + { + throw new ArgumentNullException(nameof(commandName)); + } + + CommandName = commandName; + } + public string SessionId { get; set; } + public string CommandName { get; set; } + public JToken CommandParameters { get; set; } + } +} diff --git a/dotnet/src/webdriver/DevTools/DevToolsSession.cs b/dotnet/src/webdriver/DevTools/DevToolsSession.cs index 311f4d74dbd4f..664eae19263b3 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSession.cs @@ -316,6 +316,90 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains return null; } + /// + /// Send a collection of and wait on all of their results. + /// + /// + /// + /// + /// + /// A list of command response object implementeing the interface. + public async Task> SendCommands(List commands, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true) + { + if (millisecondsTimeout.HasValue == false) + { + millisecondsTimeout = Convert.ToInt32(CommandTimeout.TotalMilliseconds); + } + + if (this.attachedTargetId == null) + { + LogTrace("Session not currently attached to a target; reattaching"); + await this.InitializeSession(); + } + + var messages = new List(); + foreach (var item in commands) + { + messages.Add(new DevToolsCommandData(Interlocked.Increment(ref this.currentCommandId), item.SessionId, item.CommandName, item.CommandParameters)); + } + + if (this.connection != null && this.connection.IsActive) + { + foreach (var message in messages) + { + var contents = JsonConvert.SerializeObject(message); + + this.pendingCommands.TryAdd(message.CommandId, message); + await this.connection.SendData(contents).ConfigureAwait(false); + } + + WaitHandle.WaitAll(messages.Select(x => x.SyncEvent.WaitHandle).ToArray(), millisecondsTimeout.Value); + + var noResponsesReceived = messages.Where(x => !x.SyncEvent.IsSet); + if (noResponsesReceived.Any() && throwExceptionIfResponseNotReceived) + { + throw new InvalidOperationException($"A command response was not received: {string.Join(", ", noResponsesReceived.Select(x => x.CommandName))}"); + } + + foreach (var message in messages) + { + DevToolsCommandData modified; + if (this.pendingCommands.TryRemove(message.CommandId, out modified)) + { + if (modified.IsError) + { + var errorMessage = modified.Result.Value("message"); + var errorData = modified.Result.Value("data"); + + var exceptionMessage = $"{message.CommandName}: {errorMessage}"; + if (!string.IsNullOrWhiteSpace(errorData)) + { + exceptionMessage = $"{exceptionMessage} - {errorData}"; + } + + LogTrace("Received Error Response {0}: {1} {2}", modified.CommandId, message, errorData); + throw new CommandResponseException(exceptionMessage) + { + Code = modified.Result.Value("code") + }; + } + } + } + + return messages.Select(x => new DevToolsCommandResponse + { + Result = x.Result, + SessionId = x.SessionId + }).ToList(); + } + else + { + LogTrace("WebSocket is not connected; not sending {0}", string.Join(", ", commands.Select(itm => itm.CommandName))); + } + + return null; + } + /// /// Releases all resources associated with this . /// diff --git a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs index b9d1d06191eb4..dce714ed02ff8 100644 --- a/dotnet/src/webdriver/DevTools/IDevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/IDevToolsSession.cs @@ -85,5 +85,15 @@ Task SendCommand(TCommand command, /// to throw an exception if a response is not received; otherwise, . /// The command response object implementing the interface. Task SendCommand(string commandName, JToken @params, CancellationToken cancellationToken, int? millisecondsTimeout, bool throwExceptionIfResponseNotReceived); + + /// + /// Returns a collection of based on a collection of commands. + /// + /// + /// + /// + /// + /// A list of command response object implementeing the interface. + Task> SendCommands(List commands, CancellationToken cancellationToken = default(CancellationToken), int? millisecondsTimeout = null, bool throwExceptionIfResponseNotReceived = true); } } From 929f2aa1581a0f754d11f3afe4a44cbc317646f9 Mon Sep 17 00:00:00 2001 From: Edwin van Vliet Date: Mon, 5 Feb 2024 09:03:03 +0100 Subject: [PATCH 2/2] Add missing using statement. --- dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs b/dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs index af4c6023dba84..9cf74e5bfc432 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsCommandSettings.cs @@ -16,6 +16,8 @@ // limitations under the License. // +using Newtonsoft.Json.Linq; + namespace OpenQA.Selenium.DevTools { public class DevToolsCommandSettings