From 005e17aa9437738a18f9b000151a605c37c7e91c Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 14 Jul 2026 15:44:28 +0000 Subject: [PATCH] Fix browser shortcut frame regressions --- docs/architecture.md | 2 +- docs/browser-capture-harness.md | 4 +- packages/desktop/capture-harness/main.js | 85 ++++++++++++++++++- .../browser-keyboard/guest-preload.ts | 65 +++++++------- .../features/browser-keyboard/index.test.ts | 8 ++ .../src/features/browser-keyboard/index.ts | 39 ++++++++- packages/desktop/src/main.ts | 4 +- 7 files changed, 166 insertions(+), 41 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index aecedcb08..480ad596e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -140,7 +140,7 @@ Electron wrapper for macOS, Linux, and Windows. > > **In-app browser ownership.** Each registered guest records its owning host window. The active browser is keyed by `(host window, workspace)`, and application-menu Reload / Force Reload resolve only within the window Electron supplies to the menu callback. A non-null active update must name a browser owned by that host; a null update clears only that host/workspace. Browser automation continues to target explicit browser ids returned by `browser_new_tab` or `browser_list_tabs`. > -> **Browser keyboard boundary.** Guest pages receive renderer-published shortcuts first. `Cmd/Ctrl+T`, `Cmd/Ctrl+L`, and `Cmd/Ctrl+R` are explicit guest-shell reservations; ordinary Paseo shortcuts run only after the page declines them. Human guest input disables Electron's menu fallback for plain keys. Agent-generated keys use guest `sendInputEvent` with `skipIfUnhandled`, so an unhandled Enter stops at the guest instead of reaching the host composer. Main selects the sandboxed guest preload; it exposes no APIs to guest pages. +> **Browser keyboard boundary.** Guest pages receive renderer-published shortcuts first. `Cmd/Ctrl+L` and `Cmd/Ctrl+R` are explicit guest-shell reservations; ordinary Paseo shortcuts run only after the page declines them. The sandboxed guest preload runs in every frame so focused iframes use the same boundary, while Node integration remains disabled. Human guest input disables Electron's menu fallback for plain keys. Agent-generated keys use guest `sendInputEvent` with `skipIfUnhandled`, so an unhandled Enter stops at the guest instead of reaching the host composer. Main selects the preload; it exposes no APIs to guest pages. ```text Human key -> guest WebContents diff --git a/docs/browser-capture-harness.md b/docs/browser-capture-harness.md index bca4a79c8..9c6da70ee 100644 --- a/docs/browser-capture-harness.md +++ b/docs/browser-capture-harness.md @@ -19,7 +19,9 @@ It validates the compositor behavior that unit tests cannot see: - the automation group loads the compiled production keyboard boundary and guest preload, then proves that initial page window handlers get first refusal, unhandled shortcuts synchronously suppress editable browser defaults before crossing the host - boundary, digit wildcard shortcuts cross, and background automation stays in the guest. + boundary, handlers registered after preload still get first refusal, focused iframes + share the same boundary, digit wildcard shortcuts cross, and background automation stays + in the guest. Run it with the repo Electron: diff --git a/packages/desktop/capture-harness/main.js b/packages/desktop/capture-harness/main.js index 6a3c88286..a134a03b8 100644 --- a/packages/desktop/capture-harness/main.js +++ b/packages/desktop/capture-harness/main.js @@ -451,7 +451,7 @@ function installHarnessWebviewGuards(win, options = {}) { webPreferences.nodeIntegration = false; webPreferences.contextIsolation = true; if (options.preloadPath) { - webPreferences.nodeIntegrationInSubFrames = false; + webPreferences.nodeIntegrationInSubFrames = true; webPreferences.nodeIntegrationInWorker = false; webPreferences.sandbox = true; webPreferences.webSecurity = true; @@ -1177,6 +1177,27 @@ function automationFixtureUrl() {