From 312d50f9252f010879e67b3031fbd7a3f7be9fdb Mon Sep 17 00:00:00 2001
From: Bjoern Esswein <692-bessw@users.noreply.gl.kwarc.info>
Date: Sat, 29 Mar 2025 14:08:43 +0100
Subject: [PATCH] Receive multiple messages per frame and fix some unhandled
 exceptions.

Runs AsyncReadLoop in a c# background thread (using Task.run) to be able to receive more then one message per Unity update() call.
---
 Runtime/ChromeDevtools/Browser.cs             | 12 +++++--
 Runtime/ChromeDevtools/BrowserTab.cs          |  5 +--
 .../ChromeDevtools/DevtoolsProtocolHandler.cs | 32 ++++++++++++++++---
 Runtime/ChromeDevtools/DevtoolsWebsocket.cs   |  1 -
 Runtime/ChromeDevtools/IDevtoolsConnection.cs |  1 -
 Runtime/ChromeDevtools/Protocol/DOM/DOM.cs    |  4 +--
 Runtime/WebViewComponent.cs                   |  4 +--
 7 files changed, 45 insertions(+), 14 deletions(-)

diff --git a/Runtime/ChromeDevtools/Browser.cs b/Runtime/ChromeDevtools/Browser.cs
index c39c762..aa8e384 100644
--- a/Runtime/ChromeDevtools/Browser.cs
+++ b/Runtime/ChromeDevtools/Browser.cs
@@ -17,7 +17,8 @@ namespace bessw.Unity.WebView.ChromeDevTools
         private Process browserProcess;
 
         /* browser settings */
-        public static string browserExecutablePath = "chrome";
+        public static string BrowserExecutablePath = "chrome";
+        public static string headlessBrowserExecutablePath = "chrome-headless-shell.exe";
         public static bool headless = true;
         private const int debugPort = 9222;
 
@@ -67,7 +68,14 @@ namespace bessw.Unity.WebView.ChromeDevTools
             if (browserProcess == null || browserProcess.HasExited)
             {
                 browserProcess = new Process();
-                browserProcess.StartInfo.FileName = browserExecutablePath;
+                if (headless)
+                {
+                    browserProcess.StartInfo.FileName = headlessBrowserExecutablePath;
+                }
+                else
+                {
+                    browserProcess.StartInfo.FileName = BrowserExecutablePath;
+                }
                 browserProcess.StartInfo.Arguments = String.Join(" ", new []{
                     $"--user-data-dir={Path.Join(Application.temporaryCachePath, "BrowserView")}",
                     $"--remote-debugging-port={debugPort}",
diff --git a/Runtime/ChromeDevtools/BrowserTab.cs b/Runtime/ChromeDevtools/BrowserTab.cs
index 853152e..e6ca33e 100644
--- a/Runtime/ChromeDevtools/BrowserTab.cs
+++ b/Runtime/ChromeDevtools/BrowserTab.cs
@@ -82,9 +82,10 @@ namespace bessw.Unity.WebView.ChromeDevTools
             _document = null;
         }
 
-        public IEnumerator Update()
+        public void Update()
         {
-            yield return devtools.readLoop();
+            //yield return devtools.readLoop();
+            devtools.handleMessages();
         }
 
         public async Task SetSizeAndScale(Vector2Int size, int pageScaleFactor)
diff --git a/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs b/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs
index 4f6fb74..57af61a 100644
--- a/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs
+++ b/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs
@@ -21,6 +21,7 @@ namespace bessw.Unity.WebView.ChromeDevTools
 
         private IDevtoolsConnection devtools;
 
+        private ConcurrentQueue<string> messageQueue = new();
         private ConcurrentDictionary<long, ResponseTypeAndCallback<IDevtoolsResponse>> commandResponseDict = new();
 
         // devtools events
@@ -44,12 +45,27 @@ namespace bessw.Unity.WebView.ChromeDevTools
         public DevtoolsProtocolHandler(IDevtoolsConnection devtools)
         {
             this.devtools = devtools;
-            devtools.receivedMessage += ParseMessage;
-            //TODO: let devtools open async
+            devtools.receivedMessage += enqueueMessage;
+            //TODO: let devtools open async (some other code currently expects it to be open once this method returns)
             devtools.OpenAsync().Wait();
-            //devtools.AsyncReadLoop();
+            // run AsyncReadLoop in a c# background thread to be able to receive more then one message per Unity update() call
+            Task.Run(devtools.AsyncReadLoop);
         }
 
+        private void enqueueMessage(string msg)
+        {
+            messageQueue.Enqueue(msg);
+        }
+
+        public void handleMessages()
+        {
+            while (messageQueue.TryDequeue(out string msgStr))
+            {
+                ParseMessage(msgStr);
+            }
+        }
+
+        [Obsolete]
         public IEnumerator readLoop()
         {
             while (true)
@@ -79,7 +95,15 @@ namespace bessw.Unity.WebView.ChromeDevTools
         private void ParseMessage(string msgStr)
         {
             // deserialize the devtools response wrapper
-            var message = JObject.Parse(msgStr);
+            JObject message;
+            try
+            {
+                message = JObject.Parse(msgStr);
+            } catch (Exception e)
+            {
+                Debug.LogError($"Could not parse message '{msgStr}' error '{e.Message}'");
+                return;
+            }
 
             if (message.ContainsKey("id"))
             {
diff --git a/Runtime/ChromeDevtools/DevtoolsWebsocket.cs b/Runtime/ChromeDevtools/DevtoolsWebsocket.cs
index b0acb78..a97c477 100644
--- a/Runtime/ChromeDevtools/DevtoolsWebsocket.cs
+++ b/Runtime/ChromeDevtools/DevtoolsWebsocket.cs
@@ -38,7 +38,6 @@ namespace bessw.Unity.WebView.ChromeDevTools
         /// Experimental! 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;
diff --git a/Runtime/ChromeDevtools/IDevtoolsConnection.cs b/Runtime/ChromeDevtools/IDevtoolsConnection.cs
index 122c569..7613461 100644
--- a/Runtime/ChromeDevtools/IDevtoolsConnection.cs
+++ b/Runtime/ChromeDevtools/IDevtoolsConnection.cs
@@ -23,7 +23,6 @@ namespace bessw.Unity.WebView.ChromeDevTools
         /// experimental
         /// </summary>
         /// <returns></returns>
-        [Obsolete]
         public Task AsyncReadLoop();
 
         public Task SendCommandAsync(string command);
diff --git a/Runtime/ChromeDevtools/Protocol/DOM/DOM.cs b/Runtime/ChromeDevtools/Protocol/DOM/DOM.cs
index 08c6e70..ef01970 100644
--- a/Runtime/ChromeDevtools/Protocol/DOM/DOM.cs
+++ b/Runtime/ChromeDevtools/Protocol/DOM/DOM.cs
@@ -922,12 +922,12 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
         /// <summary>
         /// Child nodes of this node when requested with children.
         /// </summary>
-        public List<Node> children { get; set; }
+        public List<Node> children { get; set; } = new List<Node>();
         /// <summary>
         /// Attributes of the `Element` node in the form of flat array `[name1, value1, name2, value2]`.
         /// </summary>
         [JsonConverter(typeof(JsonInterleavedArrayConverter<string, string>))]
-        public Dictionary<string,string> attributes { get; set; }
+        public Dictionary<string,string> attributes { get; set; } = new Dictionary<string, string>();
         /// <summary>
         /// Document URL that `Document` or `FrameOwner` node points to.
         /// </summary>
diff --git a/Runtime/WebViewComponent.cs b/Runtime/WebViewComponent.cs
index 438a664..ef951ff 100644
--- a/Runtime/WebViewComponent.cs
+++ b/Runtime/WebViewComponent.cs
@@ -80,7 +80,7 @@ namespace bessw.Unity.WebView
                 tab = bt;
                 _ = tab.SetSizeAndScale(new Vector2Int((int)rectTransform.rect.width, (int)rectTransform.rect.height) / PageScaleFactor, PageScaleFactor);
 
-                StartCoroutine(tab.Update());
+                //StartCoroutine(tab.Update());
                 //StartCoroutine(createScreenshots());
                 StartCoroutine(tab.StartStream(tab.Size.x, tab.Size.y, (frame) =>
                 {
@@ -120,7 +120,7 @@ namespace bessw.Unity.WebView
         // Update is called once per frame
         private void Update()
         {
-
+            tab?.Update();
         }
 
         #region pointer event handlers
-- 
GitLab