diff --git a/Runtime/BrowserView.cs b/Runtime/BrowserView.cs index 32bc7be6bf9ec035d013557dfcfd552f14a591b0..ce92496ae381430383ad788a99e6f44d74f26040 100644 --- a/Runtime/BrowserView.cs +++ b/Runtime/BrowserView.cs @@ -30,8 +30,13 @@ public class BrowserView : MonoBehaviour var c = StartCoroutine(browser.OpenNewTab(targetUrl, (BrowserTab bt) => { tab = bt; - StartCoroutine(tab.ReadWsMessage()); - StartCoroutine(createScreenshots()); + StartCoroutine(tab.Update()); + //StartCoroutine(createScreenshots()); + StartCoroutine(tab.StartStream(900, 560, (frame) => + { + Debug.Log("update texture"); + rawImage.texture = frame; + })); })); } diff --git a/Runtime/ChromeDevtools/BrowserTab.cs b/Runtime/ChromeDevtools/BrowserTab.cs index 2faf128f558cd83644180929cc91fedefa12335f..c0c03895a96102755e55489f1ad27793ce843de3 100644 --- a/Runtime/ChromeDevtools/BrowserTab.cs +++ b/Runtime/ChromeDevtools/BrowserTab.cs @@ -1,178 +1,28 @@ -using ChromeDevTools.Protocol; using ChromeDevTools.Protocol.Page; using ChromeDevTools.Protocol.Target; -using Newtonsoft.Json; using System; using System.Collections; -using System.Collections.Concurrent; -using System.IO; -using System.Net.WebSockets; -using System.Text; using System.Threading; -using System.Threading.Tasks; using UnityEngine; namespace ChromeDevTools { - record ResponseTypeAndCallback - { - public ResponseTypeAndCallback(Type responseType, Action<IDevtoolsResponse> callback) - { - this.responseType = responseType; - this.callback = callback; - } - - public Type responseType { get; } - public Action<IDevtoolsResponse> callback { get; } - } public class BrowserTab { - private ClientWebSocket ws = new ClientWebSocket(); private PageTargetInfo pageTarget; - private ConcurrentDictionary<long, ResponseTypeAndCallback> commandResponseDict = new ConcurrentDictionary<long, ResponseTypeAndCallback>(); - + private DevtoolsProtocolHandler devtools; public BrowserTab(PageTargetInfo pageTarget) { this.pageTarget = pageTarget; Debug.Log($"tab WebSocket: '{pageTarget.WebSocketDebuggerUrl}'"); - // open remote devtools websocket connection - /** - * Note: For webgl compatibility you need to use the JavaScript plugin, see also https://docs.unity3d.com/Manual/webgl-networking.html#UsingWebSockets - */ - ws.ConnectAsync(new Uri(pageTarget.WebSocketDebuggerUrl), Browser.cancellationTokenSource.Token); - } - - - /// <summary> - /// Coroutine wrapper for _SendWsMessagel� - /// json serializes and sends a command over the devtools websocket - /// </summary> - /// <typeparam name="T">IDevtoolsCommand</typeparam> - /// <param name="command"></param> - /// <returns></returns> - private IEnumerator SendWsMessage<T>(T command, System.Action<IDevtoolsResponse> callback) where T : IDevtoolsCommand - { - // wait if the websocket is not yet open - if (ws.State != WebSocketState.Open) - { - yield return new WaitUntil(() => ws.State == WebSocketState.Open); - } - - var sendTask = _SendWsMessage(command, callback); - // wait until the command has been send - yield return new WaitUntil(() => sendTask.IsCompleted); - } - - /// <summary> - /// json serializes and sends a command over the devtools websocket - /// </summary> - /// <typeparam name="T">IDevtoolsCommand</typeparam> - /// <param name="command"></param> - /// <returns>Task that resoves then the command is send</returns> - private async Task _SendWsMessage<T>(T command, System.Action<IDevtoolsResponse> callback) where T : IDevtoolsCommand - { - // wait if the websocket is not yet open - if (ws.State != WebSocketState.Open) - { - throw new InvalidOperationException("Websocket is not open"); - } - - // apply the message wrapper - var wrappedCommand = new DevtoolsCommandWrapper<T>(command); - - // get the response type from the commands attribute - CommandResponseAttribute cra = (CommandResponseAttribute) Attribute.GetCustomAttribute( - command.GetType(), - typeof(CommandResponseAttribute) - ); - Type responseType = cra.responseType; - - // register the response callback for this command id - if (!commandResponseDict.TryAdd(wrappedCommand.Id, new ResponseTypeAndCallback(responseType, callback))) - { - throw new InvalidOperationException($"could not add response callback for command '{wrappedCommand.Id}' to commandResponseDict"); - } - - // json serialize the command and send it - var json = JsonConvert.SerializeObject(wrappedCommand, Browser.serializerSettings); - Debug.Log($"ws send: '{json}'"); - await ws.SendAsync(Encoding.UTF8.GetBytes(json), WebSocketMessageType.Text, true, Browser.cancellationTokenSource.Token); + this.devtools = new DevtoolsProtocolHandler(new DevtoolsWebsocket(pageTarget.WebSocketDebuggerUrl)); } - /// <summary> - /// read and deserialize a json message from the devtools websocket - /// </summary> - /// <returns></returns> - public IEnumerator ReadWsMessage() + public IEnumerator Update() { - // wait if the websocket is not yet open - if (ws.State != WebSocketState.Open) - { - yield return new WaitUntil(() => ws.State == WebSocketState.Open); - } - - // start the message receive loop (it will exit when the coroutine is stopped) - while (true) - { - async Task<string> read() - { - // create a MemoryStream to reconstruct the message - using (var ms = new MemoryStream()) - { - WebSocketReceiveResult result; - - /* A part of the message will be read into the message buffer and than be transfered - * to the MemoryStream. This will be repeated until the complete message has been received. - */ - do - { - var messageBuffer = WebSocket.CreateClientBuffer(1024, 16); - result = await ws.ReceiveAsync(messageBuffer, Browser.cancellationTokenSource.Token); - - // write the messageBuffer to the MemoryStream - ms.Write(messageBuffer.Array, messageBuffer.Offset, result.Count); - } while (!result.EndOfMessage); - - - // If the webSocket message type isn't text ignore this message - if (!(result.MessageType == WebSocketMessageType.Text)) - { - Debug.LogError($"Unexpected WebSocketMessageType: {result.MessageType}"); - return await read(); - } - - // convert the message stream to string - return Encoding.UTF8.GetString(ms.ToArray()); - } - } - - - var readTask = read(); - // yield the coroutine until the async Task "reseiveTask" is completed - yield return new WaitUntil(() => readTask.IsCompleted); - - string msgString = readTask.Result; - - Debug.Log($"ws reseived: '{msgString}'"); - - // deserialize the devtools response wrapper - var response = JsonConvert.DeserializeObject<DevtoolsResponseWrapper>(msgString, Browser.serializerSettings); - - // get the callback and the type for this response - ResponseTypeAndCallback responseTypeAndCallback; - if (!commandResponseDict.TryRemove(response.Id, out responseTypeAndCallback)) - { - throw new InvalidOperationException($"There is no command waiting for the response '{response.Id}'"); - } - - // deserialize the result - IDevtoolsResponse commandResponse = (IDevtoolsResponse) response.Result.ToObject(responseTypeAndCallback.responseType, Browser.serializer); - - // pass the response to the callback - responseTypeAndCallback.callback(commandResponse); - } + yield return devtools.readLoop(); } public IEnumerator CreateScreenshot(double width, double height, Action<Texture2D> callback) @@ -182,35 +32,57 @@ namespace ChromeDevTools screenshotCommand.Clip.Width = width; screenshotCommand.Clip.Height = height; - return SendWsMessage(screenshotCommand, + return devtools.SendCommand(screenshotCommand, (response) => { CaptureScreenshotCommandResponse screenshotResponse = (CaptureScreenshotCommandResponse) response; // parse the base64 encoded screenshot to a texture var screenshotTexture = new Texture2D(1, 1); screenshotTexture.LoadImage( Convert.FromBase64String(screenshotResponse.Data) ); - //myTexture.Apply(); - + // return the texture via callback callback(screenshotTexture); }); } + public IEnumerator StartStream(int width, int height, Action<Texture2D> callback) + { + // register screencast frame event handler + // TODO: deregister on stop stream + devtools.screencastFrameEventHandler += (screencastFrameEvent frameEvent) => + { + Debug.Log($"screencast frame, '{frameEvent.sessionId}'"); + + // send an ack for this frame + var frameAck = new screencastFrameAck(); + frameAck.sessionId = frameEvent.sessionId; + _ = devtools.SendCommandAsync(frameAck); + + // parse the base64 encoded frame to a texture + var frameTexture = new Texture2D(1, 1); // new Texture2D only works on the main thread + frameTexture.LoadImage(Convert.FromBase64String(frameEvent.Data)); + + // return the texture via callback + callback(frameTexture); + }; + + var startScreencast = new startScreencast(); + startScreencast.MaxWidth = width; + startScreencast.maxHeight = height; + startScreencast.everyNthFrame = 1; + + return devtools.SendCommand(startScreencast); + } + /** * close this tab */ public void Close() { Debug.Log($"BrowserTab close called for: '{pageTarget.Url}'"); - //TODO: fix SendWsMessage without coroutine - _ = _SendWsMessage(new closeTarget(pageTarget.Id), delegate + _ = devtools.SendCommandAsync(new closeTarget(pageTarget.Id), delegate { - ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "tab closed", CancellationToken.None) - .ContinueWith(delegate - { - Debug.Log($"ws CloseAsync State:\n{ws.State}"); - ws.Dispose(); - }); + devtools.Dispose(); }); } } diff --git a/Runtime/ChromeDevtools/DevtoolsWebsocket.cs b/Runtime/ChromeDevtools/DevtoolsWebsocket.cs new file mode 100644 index 0000000000000000000000000000000000000000..83a7c021bd382d0406a7e542828262c89608ea4d --- /dev/null +++ b/Runtime/ChromeDevtools/DevtoolsWebsocket.cs @@ -0,0 +1,128 @@ +using ChromeDevTools.Protocol; +using System; +using System.IO; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +namespace ChromeDevTools +{ + /// <summary> + /// Manages a chrome devtools websocket connection and parses the messages to C# objects + /// </summary> + public class DevtoolsWebsocket: IDevtoolsConnection + { + private Uri webSocketDebuggerUrl; + private ClientWebSocket ws = new ClientWebSocket(); + private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + public event Action<string> receivedMessage; + + public DevtoolsWebsocket(string webSocketDebuggerUrl) : this(new Uri(webSocketDebuggerUrl)) { } + public DevtoolsWebsocket(Uri webSocketDebuggerUrl) + { + this.webSocketDebuggerUrl = webSocketDebuggerUrl; + } + + public Task OpenAsync() + { + // open remote devtools websocket connection + /** + * Note: For webgl compatibility you need to use the JavaScript plugin, see also https://docs.unity3d.com/Manual/webgl-networking.html#UsingWebSockets + */ + return ws.ConnectAsync(this.webSocketDebuggerUrl, cancellationTokenSource.Token); + } + + /// <summary> + /// Experimential! synchronisation with Unity coroutines is not jet implemented. Call ReadAsync from a coroutine instead. + /// </summary> + /// <returns></returns> + [Obsolete] + public async Task AsyncReadLoop() + { + var cancellationToken = cancellationTokenSource.Token; + do + { + var msg = await ReadAsync(); + receivedMessage?.Invoke(msg); + await Task.Yield(); + } while (!cancellationToken.IsCancellationRequested); + } + + public async Task<string> ReadAsync() + { + // create a MemoryStream to reconstruct the message + using (var ms = new MemoryStream()) + { + WebSocketReceiveResult result; + + /* A part of the message will be read into the message buffer and than be transfered + * to the MemoryStream. This will be repeated until the complete message has been received. + */ + do + { + var messageBuffer = WebSocket.CreateClientBuffer(1024, 16); + result = await ws.ReceiveAsync(messageBuffer, cancellationTokenSource.Token).ConfigureAwait(false); + + // write the messageBuffer to the MemoryStream + ms.Write(messageBuffer.Array, messageBuffer.Offset, result.Count); + } while (!result.EndOfMessage); + + switch (result.MessageType) + { + case WebSocketMessageType.Text: + // convert the message stream to string and call the receivedMessage event + return Encoding.UTF8.GetString(ms.ToArray()); + case WebSocketMessageType.Close: + throw new DevtoolsConnectionClosedException(); + default: + throw new UnexpectedWebSocketMessageTypeException(result.MessageType); + } + } + } + + /// <summary> + /// Sends a json serialized command over the devtools websocket + /// </summary> + /// <param name="command"></param> + /// <returns>Task that resoves then the command is send</returns> + public Task SendCommandAsync(string command) + { + // check that the websocket is open + if (ws.State != WebSocketState.Open) + { + throw new DevtoolsConnectionClosedException(); + } + Debug.Log($"ws send: '{command}'"); + return ws.SendAsync(Encoding.UTF8.GetBytes(command), WebSocketMessageType.Text, true, cancellationTokenSource.Token); + } + + public void Dispose() + { + cancellationTokenSource.Cancel(); + ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "tab closed", CancellationToken.None) + .ContinueWith(delegate + { + Debug.Log($"ws CloseAsync State:\n{ws.State}"); + ws.Dispose(); + cancellationTokenSource.Dispose(); + }); + } + } + + ///////////////////////////// + // Utility classes: + + public class UnexpectedWebSocketMessageTypeException : DevtoolsConnectionException + { + public readonly WebSocketMessageType messageType; + + public UnexpectedWebSocketMessageTypeException(WebSocketMessageType messageType) : base($"Unexpected websocket message type: {messageType}") + { + this.messageType = messageType; + } + } + +} \ No newline at end of file diff --git a/Runtime/ChromeDevtools/DevtoolsWebsocket.cs.meta b/Runtime/ChromeDevtools/DevtoolsWebsocket.cs.meta new file mode 100644 index 0000000000000000000000000000000000000000..3a21c93bf1664f6dbe6cd733ef3b46b943d486ef --- /dev/null +++ b/Runtime/ChromeDevtools/DevtoolsWebsocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c0bba16a21fb5e449aaf534ba8b217fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ChromeDevtools/Protocol/DevtoolsCommand.cs b/Runtime/ChromeDevtools/Protocol/DevtoolsCommand.cs index 1601a8537571c25b7896d95b20bc0cbfed770a67..9fe122d5ec7a312b7d5fb677d3679b05af6b8a91 100644 --- a/Runtime/ChromeDevtools/Protocol/DevtoolsCommand.cs +++ b/Runtime/ChromeDevtools/Protocol/DevtoolsCommand.cs @@ -24,6 +24,7 @@ namespace ChromeDevTools public T Params { get; set; } public DevtoolsCommandWrapper(T command) => Params = command; } - public interface IDevtoolsCommand {} + public interface IDevtoolsCommand { } + public interface IDevtoolsCommandWithResponse: IDevtoolsCommand { } } } \ No newline at end of file diff --git a/Runtime/ChromeDevtools/Protocol/DevtoolsProtocolHandler.cs b/Runtime/ChromeDevtools/Protocol/DevtoolsProtocolHandler.cs new file mode 100644 index 0000000000000000000000000000000000000000..22269c8439916f20d39d7228bd561935bb8baa3d --- /dev/null +++ b/Runtime/ChromeDevtools/Protocol/DevtoolsProtocolHandler.cs @@ -0,0 +1,227 @@ +using ChromeDevTools.Protocol; +using ChromeDevTools.Protocol.Page; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +namespace ChromeDevTools +{ + /// <summary> + /// Manages a chrome devtools websocket connection and parses the messages to C# objects + /// </summary> + public class DevtoolsProtocolHandler + { + private IDevtoolsConnection devtools; + private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + private ConcurrentDictionary<long, ResponseTypeAndCallback> commandResponseDict = new ConcurrentDictionary<long, ResponseTypeAndCallback>(); + + // devtools events + public event Action<screencastFrameEvent> screencastFrameEventHandler; + public event Action<screencastVisibilityChangedEvent> screencastVisibilityChangedEventHandler; + + public DevtoolsProtocolHandler(IDevtoolsConnection devtools) + { + this.devtools = devtools; + devtools.receivedMessage += ParseMessage; + //TODO: let devtools open async + devtools.OpenAsync().Wait(); + //devtools.AsyncReadLoop(); + } + + public IEnumerator readLoop() + { + while (true) + { + var task = devtools.ReadAsync(); + + + yield return new WaitUntil(() => task.IsCompleted); + + // exit the read loop if there was an unhandled exception (e.g. cancellation) + if (!task.IsCompletedSuccessfully) + { + break; + } + + var mesgStr = task.GetAwaiter().GetResult(); + Debug.Log($"ws reseived: '{mesgStr}'"); + ParseMessage(mesgStr); + } + } + + private void ParseMessage(string mesgStr) + { + // deserialize the devtools response wrapper + var message = JObject.Parse(mesgStr); + + if (message.ContainsKey("id")) + { + handleResponse( message.ToObject<DevtoolsResponseWrapper>() ); + } + else if (message.ContainsKey("method")) + { + handleEvent( message.ToObject<DevtoolsEventWrapper>() ); + } + else + { + throw new UnexpectedMessageException(mesgStr); + } + } + + /// <summary> + /// deserialize a json encoded command response from the devtools connection + /// </summary> + private void handleResponse(DevtoolsResponseWrapper response) + { + + // get the callback and the type for this response + ResponseTypeAndCallback responseTypeAndCallback; + if (!commandResponseDict.TryRemove(response.Id, out responseTypeAndCallback)) + { + Debug.Log($"There is no command waiting for the response '{response.Id}'"); + return; + } + + // deserialize the result + IDevtoolsResponse commandResponse = (IDevtoolsResponse) response.Result.ToObject(responseTypeAndCallback.responseType, Browser.serializer); + + // pass the response to the callback + responseTypeAndCallback.callback(commandResponse); + } + + /// <summary> + /// deserialize a json encoded event from the devtools connection and call its event handlers if at least one is registered + /// </summary> + private void handleEvent(DevtoolsEventWrapper ev) + { + switch (ev.Method) + { + case "Page.screencastFrame": + screencastFrameEventHandler?.Invoke( ev.Params.ToObject<screencastFrameEvent>(Browser.serializer) ); + break; + case "Page.screencastVisibilityChanged": + screencastVisibilityChangedEventHandler?.Invoke( ev.Params.ToObject<screencastVisibilityChangedEvent>(Browser.serializer) ); + break; + default: + throw new UnexpectedMessageException($"Event of type '{ev}' is not implemented"); + } + } + + + /// <summary> + /// Coroutine wrapper for SendCommandAsync + /// json serializes and sends a command over the devtools websocket + /// </summary> + /// <typeparam name="T">IDevtoolsCommand</typeparam> + /// <param name="command"></param> + /// <returns></returns> + public IEnumerator SendCommand<T>(T command) where T : IDevtoolsCommand + { + var sendTask = SendCommandAsync(command); + // wait until the command has been send + yield return new WaitUntil(() => sendTask.IsCompleted); + } + + + /// <summary> + /// Coroutine wrapper for SendCommandAsync + /// json serializes and sends a command over the devtools websocket + /// </summary> + /// <typeparam name="T">IDevtoolsCommand</typeparam> + /// <param name="command"></param> + /// <returns></returns> + public IEnumerator SendCommand<T>(T command, Action<IDevtoolsResponse> callback) where T : IDevtoolsCommandWithResponse + { + var sendTask = SendCommandAsync(command, callback); + // wait until the command has been send + yield return new WaitUntil(() => sendTask.IsCompleted); + } + + /// <summary> + /// json serializes and sends a command over the devtools websocket + /// </summary> + /// <typeparam name="T">IDevtoolsCommand</typeparam> + /// <param name="command"></param> + /// <returns>Task that resoves then the command is send</returns> + public async Task SendCommandAsync<T>(T command, Action<IDevtoolsResponse> callback) where T : IDevtoolsCommandWithResponse + { + // apply the message wrapper + var wrappedCommand = new DevtoolsCommandWrapper<T>(command); + + // get the response type from the commands attribute + CommandResponseAttribute cra = (CommandResponseAttribute)Attribute.GetCustomAttribute( + command.GetType(), + typeof(CommandResponseAttribute) + ); + if( cra == null ) + { + throw new NotImplementedException("command has no respnse attribute"); + } + // TODO: handle command without response + Type responseType = cra.responseType; + + // register the response callback for this command id + if (!commandResponseDict.TryAdd(wrappedCommand.Id, new ResponseTypeAndCallback(responseType, callback))) + { + throw new InvalidOperationException($"could not add response callback for command '{wrappedCommand.Id}' to commandResponseDict"); + } + + // json serialize the command and send it + var json = JsonConvert.SerializeObject(wrappedCommand, Browser.serializerSettings); + Debug.Log($"ws send: '{json}'"); + await devtools.SendCommandAsync(json); + } + + /// <summary> + /// json serializes and sends a command over the devtools websocket + /// </summary> + /// <typeparam name="T">IDevtoolsCommand</typeparam> + /// <returns>Task that resoves then the command is send</returns> + public async Task SendCommandAsync<T>(T command) where T : IDevtoolsCommand + { + // apply the message wrapper + var wrappedCommand = new DevtoolsCommandWrapper<T>(command); + + // json serialize the command and send it + var json = JsonConvert.SerializeObject(wrappedCommand, Browser.serializerSettings); + Debug.Log($"ws send: '{json}'"); + await devtools.SendCommandAsync(json); + } + + public void Dispose() + { + devtools.Dispose(); + } + } + + ///////////////////////////// + // Utility classes: + + + /// <summary> + /// Container type to store a response type together with its callback. + /// This is used in DevtoolsProtocolHandler to parse the response to the correct C# data type and call the callback with it as parameter. + /// </summary> + record ResponseTypeAndCallback + { + public ResponseTypeAndCallback(Type responseType, Action<IDevtoolsResponse> callback) + { + this.responseType = responseType; + this.callback = callback; + } + + public Type responseType { get; } + public Action<IDevtoolsResponse> callback { get; } + } + public class UnexpectedMessageException : DevtoolsConnectionException + { + public UnexpectedMessageException(string message) : base($"Unexpected message {message}") { } + } + +} \ No newline at end of file diff --git a/Runtime/ChromeDevtools/Protocol/DevtoolsProtocolHandler.cs.meta b/Runtime/ChromeDevtools/Protocol/DevtoolsProtocolHandler.cs.meta new file mode 100644 index 0000000000000000000000000000000000000000..f8c0a980837f5f7c8a5192b8ed679ca8253b0957 --- /dev/null +++ b/Runtime/ChromeDevtools/Protocol/DevtoolsProtocolHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3a6421e824ac19840bc5970b1a8f61b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ChromeDevtools/Protocol/IDevtoolsConnection.cs b/Runtime/ChromeDevtools/Protocol/IDevtoolsConnection.cs new file mode 100644 index 0000000000000000000000000000000000000000..5b9bea419ece2d20204af13d484e1b56ce2f9e26 --- /dev/null +++ b/Runtime/ChromeDevtools/Protocol/IDevtoolsConnection.cs @@ -0,0 +1,47 @@ +using ChromeDevTools.Protocol; +using System; +using System.Collections; +using System.Threading.Tasks; + +namespace ChromeDevTools +{ + namespace Protocol + { + public interface IDevtoolsConnection + { + public event Action<string> receivedMessage; + + /// <summary> + /// Opens the devtools connection. + /// </summary> + /// <returns>Returns a Task that is resolved when the connection is established.</returns> + public Task OpenAsync(); + + /// <summary> + /// Reads one message from the devtools connection. + /// </summary> + /// <returns>Returns a Task that is resolved with the message that has been read.</returns> + public Task<string> ReadAsync(); + + /// <summary> + /// experimential + /// </summary> + /// <returns></returns> + [Obsolete] + public Task AsyncReadLoop(); + + public Task SendCommandAsync(string command); + public void Dispose(); + } + + public class DevtoolsConnectionException : Exception + { + public DevtoolsConnectionException(string msg) : base(msg) { } + } + + public class DevtoolsConnectionClosedException : DevtoolsConnectionException + { + public DevtoolsConnectionClosedException() : base("Devtools connection closed") { } + } + } +} \ No newline at end of file diff --git a/Runtime/ChromeDevtools/Protocol/IDevtoolsConnection.cs.meta b/Runtime/ChromeDevtools/Protocol/IDevtoolsConnection.cs.meta new file mode 100644 index 0000000000000000000000000000000000000000..dfc7b340dc8a4856ec5fb95882886640a17e5be6 --- /dev/null +++ b/Runtime/ChromeDevtools/Protocol/IDevtoolsConnection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 190b0eaaa999f85448d43dd42b02189f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ChromeDevtools/Protocol/Page/CaptureScreenshot.cs b/Runtime/ChromeDevtools/Protocol/Page/CaptureScreenshot.cs index 84817ffce096899950fed143e54b6da044b28a0c..09e1b1af574b90bcb16c7b274ee19ce82e1d62ef 100644 --- a/Runtime/ChromeDevtools/Protocol/Page/CaptureScreenshot.cs +++ b/Runtime/ChromeDevtools/Protocol/Page/CaptureScreenshot.cs @@ -11,7 +11,7 @@ namespace ChromeDevTools /// Capture page screenshot. /// </summary> [CommandResponseAttribute(typeof(CaptureScreenshotCommandResponse))] - public class captureScreenshot : IDevtoolsCommand + public class captureScreenshot : IDevtoolsCommandWithResponse { /// <summary> /// Gets or sets Image compression format (defaults to png). diff --git a/Runtime/ChromeDevtools/Protocol/Page/Screencast.cs b/Runtime/ChromeDevtools/Protocol/Page/Screencast.cs index d3dac2e818521a7ce1bfb8a419650ac09575ed37..a8ccbd7f71ee246d4b1599f8bc2a7588450b2ecb 100644 --- a/Runtime/ChromeDevtools/Protocol/Page/Screencast.cs +++ b/Runtime/ChromeDevtools/Protocol/Page/Screencast.cs @@ -40,7 +40,7 @@ namespace ChromeDevTools /// Send every n-th frame. /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int everyNthFrame { get; set; } + public int? everyNthFrame { get; set; } } @@ -85,7 +85,7 @@ namespace ChromeDevTools /// <summary> /// Fired when the page with currently enabled screencast was shown or hidden `. /// </summary> - public class screencastVisibilityChanged : IDevtoolsEvent + public class screencastVisibilityChangedEvent : IDevtoolsEvent { /// <summary> /// True if the page is visible. diff --git a/Runtime/ChromeDevtools/Protocol/Target/CloseTarget.cs b/Runtime/ChromeDevtools/Protocol/Target/CloseTarget.cs index 4d12a90b2cf11e6470b31d3e077d144bf745dfb6..9a129828c8b0802a02ace71060293c644a40c42e 100644 --- a/Runtime/ChromeDevtools/Protocol/Target/CloseTarget.cs +++ b/Runtime/ChromeDevtools/Protocol/Target/CloseTarget.cs @@ -10,7 +10,7 @@ namespace ChromeDevTools /// Closes the target. If the target is a page that gets closed too. /// </summary> [CommandResponseAttribute(typeof(CloseTargetCommandResponse))] - public class closeTarget : IDevtoolsCommand + public class closeTarget : IDevtoolsCommandWithResponse { [JsonIgnore] public string Method { get; } = "Target.closeTarget";