Skip to content

[RQ-799] fix: Add fix to resolve editors freezing on windows (Discard Changes dialog)#310

Open
ajinkya-browserstack wants to merge 1 commit intomasterfrom
RQ-799-editor-freeze-fix
Open

[RQ-799] fix: Add fix to resolve editors freezing on windows (Discard Changes dialog)#310
ajinkya-browserstack wants to merge 1 commit intomasterfrom
RQ-799-editor-freeze-fix

Conversation

@ajinkya-browserstack
Copy link
Copy Markdown
Contributor

@ajinkya-browserstack ajinkya-browserstack commented Mar 29, 2026

Approach:

Work around Electron Windows bug where built-in confirm/alert cause input
fields (including CodeMirror editors) to lose the visible caret and stop
accepting text until the window is re-focused.

See: electron/electron#20400

contextIsolation is enabled (default since Electron 12), so the preload's
window is isolated from the renderer's window. Assigning
window.confirm = ... in preload.js only affects the preload world, NOT the web page.

To override the renderer's window.confirm we:

    1. Expose a synchronous helper (__rqConfirmSync) via contextBridge that
  • uses ipcRenderer.sendSync to call the main process.
    1. In main.ts, after dom-ready, inject JS that replaces window.confirm with a call to window.__rqConfirmSync.

Built-in window.confirm on Windows causes input fields to lose their
caret / freeze. We replace it with dialog.showMessageBoxSync via IPC.
The preload exposes __rqConfirmSync (ipcRenderer.sendSync wrapper) into
the renderer world.
Here we:

  • a) Handle the synchronous IPC call with the native dialog.
  • b) Inject JS after dom-ready that wires window.confirm → __rqConfirmSync.

Task: https://browserstack.atlassian.net/browse/RQ-799

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 29, 2026

Walkthrough

These changes implement Windows-specific native dialog functionality for confirmation dialogs. The main process registers a synchronous IPC listener that displays a native dialog.showMessageBoxSync with OK/Cancel buttons and returns the user's selection as a boolean. The preload script exposes a __rqConfirmSync function via contextBridge that communicates with the main process using ipcRenderer.sendSync. JavaScript is injected into the window to override window.confirm to use this new synchronous dialog system on Windows platforms.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: implementing a Windows-specific fix for the 'Discard Changes dialog' freezing issue via a custom confirm dialog mechanism.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch RQ-799-editor-freeze-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/main.ts`:
- Around line 264-279: The IPC handler ipcMain.on("rq:show-confirm-dialog") is
currently registered inside createWindow() and parents dialogs to the mutable
webAppWindow, causing duplicate handlers and incorrect parenting; move this
registration out of createWindow() (or add a guard using
ipcMain.listenerCount("rq:show-confirm-dialog") to avoid multiple
registrations), resolve the parent window with
BrowserWindow.fromWebContents(event.sender) instead of webAppWindow, and wrap
the call to dialog.showMessageBoxSync(...) in a try/catch so that
event.returnValue is always set (true/false) even on error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e3b01281-70d3-49b0-9f9c-d88986088f5d

📥 Commits

Reviewing files that changed from the base of the PR and between 33adac2 and 79946fd.

📒 Files selected for processing (2)
  • src/main/main.ts
  • src/main/preload.js

Comment thread src/main/main.ts
Comment on lines +264 to +279
if (process.platform === "win32") {
ipcMain.on("rq:show-confirm-dialog", (event, message) => {
const buttonIdx = dialog.showMessageBoxSync(
webAppWindow as BrowserWindow,
{
type: "question",
buttons: ["OK", "Cancel"],
defaultId: 0,
cancelId: 1,
detail: String(message ?? ""),
message: "",
}
);
// sendSync expects event.returnValue to be set
event.returnValue = buttonIdx === 0;
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- confirm IPC block ---'
sed -n '257,290p' src/main/main.ts

printf '\n%s\n' '--- createWindow references ---'
rg -n -C2 '\bcreateWindow\s*\(' src/main/main.ts

printf '\n%s\n' '--- confirm channel references ---'
rg -n -C2 'rq:show-confirm-dialog' src/main/main.ts src/main/preload.js

Repository: requestly/requestly-desktop-app

Length of output: 2560


🏁 Script executed:

#!/bin/bash
# Find the createWindow function definition and its boundaries
rg -n 'async\s+function\s+createWindow|function\s+createWindow' src/main/main.ts

# Show the structure around createWindow definition with line numbers
sed -n '200,300p' src/main/main.ts | cat -n | head -50

Repository: requestly/requestly-desktop-app

Length of output: 1630


🏁 Script executed:

#!/bin/bash
# Show the IPC handler registration and the end of createWindow function
sed -n '260,330p' src/main/main.ts | cat -n

Repository: requestly/requestly-desktop-app

Length of output: 2722


🏁 Script executed:

#!/bin/bash
# Continue looking for the end of createWindow function
sed -n '320,380p' src/main/main.ts | cat -n

Repository: requestly/requestly-desktop-app

Length of output: 1986


🏁 Script executed:

#!/bin/bash
# Find where createWindow ends
sed -n '380,420p' src/main/main.ts | cat -n

Repository: requestly/requestly-desktop-app

Length of output: 1548


Hoist the confirm IPC handler outside createWindow() and use BrowserWindow.fromWebContents(event.sender) to resolve the parent window.

The ipcMain.on("rq:show-confirm-dialog") registration at lines 265–279 runs inside createWindow(), which is invoked at lines 477 and 525. This causes the handler to stack on each window creation, allowing a single confirm() call to trigger multiple native dialogs. Additionally, the handler parents the dialog to the mutable global webAppWindow rather than the actual sender window, and lacks error handling on the synchronous dialog.showMessageBoxSync() call—if it throws, event.returnValue is never set, blocking the renderer indefinitely.

Move this handler registration outside createWindow() (or guard it with ipcMain.listenerCount()), resolve the parent with BrowserWindow.fromWebContents(event.sender), and wrap the dialog call in try-catch to ensure event.returnValue is always set.

Suggested fix
   if (process.platform === "win32") {
-    ipcMain.on("rq:show-confirm-dialog", (event, message) => {
-      const buttonIdx = dialog.showMessageBoxSync(
-        webAppWindow as BrowserWindow,
-        {
-          type: "question",
-          buttons: ["OK", "Cancel"],
-          defaultId: 0,
-          cancelId: 1,
-          detail: String(message ?? ""),
-          message: "",
-        }
-      );
-      // sendSync expects event.returnValue to be set
-      event.returnValue = buttonIdx === 0;
-    });
+    if (!ipcMain.listenerCount("rq:show-confirm-dialog")) {
+      ipcMain.on("rq:show-confirm-dialog", (event, message) => {
+        const options = {
+          type: "question" as const,
+          buttons: ["OK", "Cancel"],
+          defaultId: 0,
+          cancelId: 1,
+          detail: String(message ?? ""),
+          message: "",
+        };
+        const parentWindow = BrowserWindow.fromWebContents(event.sender);
+
+        try {
+          const buttonIdx =
+            parentWindow && !parentWindow.isDestroyed()
+              ? dialog.showMessageBoxSync(parentWindow, options)
+              : dialog.showMessageBoxSync(options);
+
+          event.returnValue = buttonIdx === 0;
+        } catch (error) {
+          logger.warn("[Windows confirm] Failed to show confirm dialog", error);
+          event.returnValue = false;
+        }
+      });
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/main.ts` around lines 264 - 279, The IPC handler
ipcMain.on("rq:show-confirm-dialog") is currently registered inside
createWindow() and parents dialogs to the mutable webAppWindow, causing
duplicate handlers and incorrect parenting; move this registration out of
createWindow() (or add a guard using
ipcMain.listenerCount("rq:show-confirm-dialog") to avoid multiple
registrations), resolve the parent window with
BrowserWindow.fromWebContents(event.sender) instead of webAppWindow, and wrap
the call to dialog.showMessageBoxSync(...) in a try/catch so that
event.returnValue is always set (true/false) even on error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant