Skip to content

Comments

Added logic for keeping background process runing on closing of debug…#299

Open
rohitneharabrowserstack wants to merge 4 commits intomasterfrom
ENGG-5314_background_debug
Open

Added logic for keeping background process runing on closing of debug…#299
rohitneharabrowserstack wants to merge 4 commits intomasterfrom
ENGG-5314_background_debug

Conversation

@rohitneharabrowserstack
Copy link
Contributor

@rohitneharabrowserstack rohitneharabrowserstack commented Feb 20, 2026

In startBackgroundProcess.js, the background window is now shielded from accidental destruction the close event is intercepted to hide instead of close; destroy is overridden so it only destroys when a new quit flag (global.allowBackgroundWindowDestruction) is set; removeAllListeners is wrapped so the close handler can’t be removed; and a backgroundProcessStarted flag is recorded. Initial behavior: the window could be closed/destroyed normally, so accidental teardown was possible.
In cleanup.js, quitting now sets allowBackgroundWindowDestruction, waits for shutdown-success, restores the original destroy when quitting, and adds a 2s fallback timer to force-destroy if the shutdown signal never arrives. Initial behavior: it just listened for shutdown-success and immediately closed/destroyed the window without guarding against missed events.

Summary by CodeRabbit

  • New Features

    • Background window now hides instead of closing and reopens developer tools whenever it loads or is shown.
    • Tracks background process state to manage reopen and visibility behavior.
  • Bug Fixes

    • More reliable shutdown with a 2s timed fallback to enforce cleanup on quit.
    • Prevents accidental background-window destruction except during intentional quit, reducing unexpected terminations.

@linear
Copy link

linear bot commented Feb 20, 2026

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2026

Walkthrough

cleanup.js now sets global.allowBackgroundWindowDestruction = true during quit, replaces direct close() calls with a destruction path that prefers a preserved _originalDestroy() when present, and enforces destruction with a 2-second timeout that force-destroys via _originalDestroy() or destroy() before resolving. startBackgroundProcess.js changes the dom-ready listener to reopen DevTools on load/show, installs a close handler that hides the background window instead of destroying it, preserves and overrides destroy (using _originalDestroy for real destruction only when allowed), and patches removeAllListeners to reattach the close-prevention handler. Global flags track background process state.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nsrCodes
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is partially related to the changeset and refers to a real aspect of the change, but it's incomplete and contains a typo ('runing' instead of 'running'), making it unclear. Revise the title to be more complete and precise, such as 'Protect background process from destruction when debug window closes' and fix the typo.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ENGG-5314_background_debug

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

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/actions/cleanup.js (1)

17-49: ⚠️ Potential issue | 🟡 Minor

ipcMain.once("shutdown-success") listener is never removed on the timeout path.

If the timeout fires first (because the background process never sends "shutdown-success"), the promise resolves but the ipcMain.once("shutdown-success") listener remains registered. If a stale "shutdown-success" message arrives later (e.g., from a new background window in a subsequent session), it will invoke the old callback on a now-destroyed window reference.

Remove the listener when the timeout fires.

Sketch
+      const onShutdownSuccess = () => {
+        clearTimeout(shutdownTimeout);
+        // ... destroy + resolve
+      };
+      ipcMain.once("shutdown-success", onShutdownSuccess);
+
       const shutdownTimeout = setTimeout(() => {
+        ipcMain.removeListener("shutdown-success", onShutdownSuccess);
         // ... destroy + resolve
       }, 2000);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/cleanup.js` around lines 17 - 49, The
ipcMain.once("shutdown-success") handler registered in getReadyToQuitApp can
remain after the timeout fires; change the logic to capture the listener
callback in a variable (the function passed to ipcMain.once) and, inside the
timeout fallback before resolving, remove that listener (using
ipcMain.removeListener or ipcMain.off with the same event name and captured
callback) so the stale callback cannot run later against
global.backgroundWindow; keep the existing destroy/_originalDestroy calls and
resolve behavior intact.
🧹 Nitpick comments (3)
src/main/actions/startBackgroundProcess.js (3)

72-120: Consider extracting lifecycle protection into a helper function.

The close handler, destroy override, and removeAllListeners override collectively form a single "protect window from destruction" concern spread across ~50 lines in the middle of startBackgroundProcess. Extracting this into a named helper (e.g., protectWindowFromDestruction(window)) would improve readability and make it testable independently.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 72 - 120, Extract
the window-lifecycle protection logic into a helper named
protectWindowFromDestruction(window): move the closeHandler, its attachment, the
_preventCloseHandler assignment, the overridden destroy (using originalDestroy
and _originalDestroy), and the overridden removeAllListeners (using
originalRemoveAllListeners) into that function; have startBackgroundProcess call
protectWindowFromDestruction(backgroundWindow) before
setupIPCForwardingToBackground and before setting the global flags so behavior
is unchanged but the lifecycle-protection concern is isolated and testable.

72-80: event.returnValue = false is unnecessary in a close event handler.

In Electron, event.preventDefault() on the close event is sufficient to prevent the window from closing. event.returnValue is only meaningful in synchronous IPC or beforeunload handlers. Line 75 can be removed for clarity.

Proposed fix
     const closeHandler = (event) => {
       event.preventDefault();
-      event.returnValue = false;
       if (!backgroundWindow.isDestroyed()) {
         backgroundWindow.hide();
       }
-      return false;
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 72 - 80, The close
handler sets event.returnValue which is unnecessary for the Electron 'close'
event; update the closeHandler (the function assigned where backgroundWindow is
handled) to remove the line setting event.returnValue and rely solely on
event.preventDefault() and backgroundWindow.hide(); keep the checks for
backgroundWindow.isDestroyed() and the final return false if desired, but delete
the redundant event.returnValue assignment to simplify the handler.

103-113: Consider a less invasive approach to protecting the close handler.

Monkey-patching removeAllListeners is fragile and brittle across Electron upgrades. While removeListener is not currently patched or called in the codebase, this approach relies on incomplete protections that could be bypassed.

Instead of patching core EventEmitter methods, document that the close handler must not be removed, or refactor to guard the removal at the point where it happens (e.g., skip removing 'close' listeners in any code that calls removeAllListeners).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 103 - 113, You
currently monkey-patch backgroundWindow.removeAllListeners to protect the
closeHandler, which is fragile; instead revert that override and either (A) add
a small safe utility or guard at call sites that remove listeners so they skip
the 'close' event (e.g., replace calls to
backgroundWindow.removeAllListeners(...) with a safeRemoveAllListeners(target,
eventName) helper that delegates except when eventName is 'close' or undefined),
or (B) centralize listener cleanup in a single function that knows not to remove
the 'close' listener; update/remove the override of removeAllListeners and
ensure places that previously cleared listeners call the new safe helper or the
centralized cleanup, referencing backgroundWindow, closeHandler,
removeAllListeners, and removeListener as needed.
🤖 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/actions/cleanup.js`:
- Around line 25-45: The shutdown handler for ipcMain.once("shutdown-success")
can race with the timeout and call destroy/resolve twice; store the timeout
handle returned by setTimeout, then in the "shutdown-success" callback first
clearTimeout(timeoutHandle) and check global.backgroundWindow &&
!global.backgroundWindow.isDestroyed() before calling either
global.backgroundWindow._originalDestroy() or global.backgroundWindow.destroy();
likewise keep the existing isDestroyed() guard in the timeout path and only call
resolve() once per path after performing the guarded destroy. Ensure you
reference ipcMain.once("shutdown-success"), the timeout handle from
setTimeout(...), global.backgroundWindow, _originalDestroy, isDestroyed(), and
resolve() when applying the fix.

In `@src/main/actions/startBackgroundProcess.js`:
- Around line 87-98: The overridden destroy implementation calls
originalDestroy() unconditionally when global.allowBackgroundWindowDestruction
is true which can throw if the window is already destroyed; update the
backgroundWindow.destroy override (the function that binds originalDestroy and
replaces backgroundWindow.destroy) to check backgroundWindow.isDestroyed()
before calling originalDestroy(), mirroring the other guard: only call
originalDestroy() when global.allowBackgroundWindowDestruction is true AND
!backgroundWindow.isDestroyed(), otherwise fall back to hiding when not
destroyed.

---

Outside diff comments:
In `@src/main/actions/cleanup.js`:
- Around line 17-49: The ipcMain.once("shutdown-success") handler registered in
getReadyToQuitApp can remain after the timeout fires; change the logic to
capture the listener callback in a variable (the function passed to
ipcMain.once) and, inside the timeout fallback before resolving, remove that
listener (using ipcMain.removeListener or ipcMain.off with the same event name
and captured callback) so the stale callback cannot run later against
global.backgroundWindow; keep the existing destroy/_originalDestroy calls and
resolve behavior intact.

---

Nitpick comments:
In `@src/main/actions/startBackgroundProcess.js`:
- Around line 72-120: Extract the window-lifecycle protection logic into a
helper named protectWindowFromDestruction(window): move the closeHandler, its
attachment, the _preventCloseHandler assignment, the overridden destroy (using
originalDestroy and _originalDestroy), and the overridden removeAllListeners
(using originalRemoveAllListeners) into that function; have
startBackgroundProcess call protectWindowFromDestruction(backgroundWindow)
before setupIPCForwardingToBackground and before setting the global flags so
behavior is unchanged but the lifecycle-protection concern is isolated and
testable.
- Around line 72-80: The close handler sets event.returnValue which is
unnecessary for the Electron 'close' event; update the closeHandler (the
function assigned where backgroundWindow is handled) to remove the line setting
event.returnValue and rely solely on event.preventDefault() and
backgroundWindow.hide(); keep the checks for backgroundWindow.isDestroyed() and
the final return false if desired, but delete the redundant event.returnValue
assignment to simplify the handler.
- Around line 103-113: You currently monkey-patch
backgroundWindow.removeAllListeners to protect the closeHandler, which is
fragile; instead revert that override and either (A) add a small safe utility or
guard at call sites that remove listeners so they skip the 'close' event (e.g.,
replace calls to backgroundWindow.removeAllListeners(...) with a
safeRemoveAllListeners(target, eventName) helper that delegates except when
eventName is 'close' or undefined), or (B) centralize listener cleanup in a
single function that knows not to remove the 'close' listener; update/remove the
override of removeAllListeners and ensure places that previously cleared
listeners call the new safe helper or the centralized cleanup, referencing
backgroundWindow, closeHandler, removeAllListeners, and removeListener as
needed.

Copy link

@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.

🧹 Nitpick comments (3)
src/main/actions/startBackgroundProcess.js (3)

73-80: event.returnValue = false and return false are ineffective on BrowserWindow's close event.

event.preventDefault() on line 74 is the correct and sufficient way to cancel a BrowserWindow close event. event.returnValue is meaningful for webContents' beforeunload events, not BrowserWindow lifecycle events, and return false has no effect in Node EventEmitter handlers. Both can be removed to avoid misleading future readers.

Suggested cleanup
     const closeHandler = (event) => {
       event.preventDefault();
-      event.returnValue = false;
       if (!backgroundWindow.isDestroyed()) {
         backgroundWindow.hide();
       }
-      return false;
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 73 - 80, The
closeHandler currently sets event.returnValue = false and returns false which
are ineffective for a BrowserWindow 'close' event; update closeHandler to rely
solely on event.preventDefault() to cancel the close and then hide the
backgroundWindow if it's not destroyed (keep the existing
backgroundWindow.hide() call guarded by backgroundWindow.isDestroyed()); remove
the lines setting event.returnValue and the final return false so the handler
only calls event.preventDefault() and performs the hide action.

103-113: removeListener/off are not overridden, leaving a gap in close-handler protection.

If the intent is to prevent external code from stripping the close handler, note that removeListener('close', handler) and off('close', handler) can still remove it. Whether this matters depends on your threat model — if only removeAllListeners is called by the code paths you're guarding against, this is fine. Otherwise, consider wrapping off/removeListener similarly for the 'close' event.

Also, originalRemoveAllListeners is already .bind(backgroundWindow) on line 104, so the .call(backgroundWindow, ...) on lines 108 and 112 is redundant — you can call originalRemoveAllListeners(eventName) directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 103 - 113, The
override only protects removeAllListeners; to fully guard the close handler on
backgroundWindow, also wrap/remove override removeListener and off so attempts
to remove the 'close' handler are ignored or re-attached: intercept
backgroundWindow.removeListener and backgroundWindow.off, check if eventName ===
'close' (and optionally match handler === closeHandler) then no-op or re-attach
closeHandler, otherwise delegate to the original bound functions; also simplify
calls to originalRemoveAllListeners by invoking
originalRemoveAllListeners(eventName) directly (remove the redundant
.call(backgroundWindow, ...)) and keep references to the originals
(originalRemoveAllListeners, originalRemoveListener, originalOff) when adding
these overrides.

119-120: Consider documenting the distinction between the two global flags.

global.isBackgroundProcessActive (line 119) and global.backgroundProcessStarted (line 120) convey subtly different semantics (current state vs. ever-started). A brief inline comment clarifying each flag's purpose would help future maintainers avoid misuse.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 119 - 120, Add
concise inline comments where global.isBackgroundProcessActive and
global.backgroundProcessStarted are set (in the startBackgroundProcess flow)
explaining that isBackgroundProcessActive denotes the current run-time state
(true while the background loop/process is running) and backgroundProcessStarted
denotes an "ever-started" flag (true once the process has been initiated at
least once), so maintainers know one is transient/current-state and the other is
a one-time marker used for startup logic and metrics. Ensure comments are placed
immediately above the two assignments and mention intended usage (e.g., toggled
on stop vs. never reset) to prevent misuse.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/main/actions/startBackgroundProcess.js`:
- Around line 73-80: The closeHandler currently sets event.returnValue = false
and returns false which are ineffective for a BrowserWindow 'close' event;
update closeHandler to rely solely on event.preventDefault() to cancel the close
and then hide the backgroundWindow if it's not destroyed (keep the existing
backgroundWindow.hide() call guarded by backgroundWindow.isDestroyed()); remove
the lines setting event.returnValue and the final return false so the handler
only calls event.preventDefault() and performs the hide action.
- Around line 103-113: The override only protects removeAllListeners; to fully
guard the close handler on backgroundWindow, also wrap/remove override
removeListener and off so attempts to remove the 'close' handler are ignored or
re-attached: intercept backgroundWindow.removeListener and backgroundWindow.off,
check if eventName === 'close' (and optionally match handler === closeHandler)
then no-op or re-attach closeHandler, otherwise delegate to the original bound
functions; also simplify calls to originalRemoveAllListeners by invoking
originalRemoveAllListeners(eventName) directly (remove the redundant
.call(backgroundWindow, ...)) and keep references to the originals
(originalRemoveAllListeners, originalRemoveListener, originalOff) when adding
these overrides.
- Around line 119-120: Add concise inline comments where
global.isBackgroundProcessActive and global.backgroundProcessStarted are set (in
the startBackgroundProcess flow) explaining that isBackgroundProcessActive
denotes the current run-time state (true while the background loop/process is
running) and backgroundProcessStarted denotes an "ever-started" flag (true once
the process has been initiated at least once), so maintainers know one is
transient/current-state and the other is a one-time marker used for startup
logic and metrics. Ensure comments are placed immediately above the two
assignments and mention intended usage (e.g., toggled on stop vs. never reset)
to prevent misuse.

Copy link

@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.

🧹 Nitpick comments (3)
src/main/actions/startBackgroundProcess.js (3)

81-89: Dead statements: event.returnValue = false and return false have no effect here.

In Electron's BrowserWindow close event, only event.preventDefault() cancels the close. event.returnValue is a DOM beforeunload pattern, and Electron ignores handler return values. Both are no-ops that could mislead future readers.

Suggested cleanup
     const closeHandler = (event) => {
       event.preventDefault();
-      event.returnValue = false;
       if (!backgroundWindow.isDestroyed()) {
         backgroundWindow.hide();
       }
-      return false;
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 81 - 89, The
closeHandler contains dead statements—remove the no-op lines `event.returnValue
= false` and `return false` and rely solely on `event.preventDefault()` to
cancel the BrowserWindow close; keep the existing check `if
(!backgroundWindow.isDestroyed()) { backgroundWindow.hide(); }` so that
closeHandler only calls event.preventDefault() and hides the backgroundWindow
when appropriate.

128-129: Two seemingly overlapping global flags: isBackgroundProcessActive and backgroundProcessStarted.

Both are set to true at the same point. If they have different semantics (e.g., one is never reset during the app lifecycle), a brief inline comment clarifying the distinction would help future readers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 128 - 129, Two
globals — isBackgroundProcessActive and backgroundProcessStarted — are set to
true together and their semantics are unclear; decide whether they represent
distinct states (e.g., "active" vs "everStarted") or are redundant. If distinct,
add a concise inline comment at the assignment in startBackgroundProcess (and
update related functions such as stopBackgroundProcess/cleanup handlers)
clarifying which flag is reset when the process stops and where each is toggled;
if redundant, remove one and update all references to use the remaining flag
(and update tests/usages accordingly). Ensure any reset logic (e.g., in
stopBackgroundProcess, onError handlers, or process exit hooks) updates only the
intended flag(s) per your clarified semantics.

112-122: removeListener/off are not similarly guarded — close handler can still be removed individually.

The override catches removeAllListeners('close') and removeAllListeners(), but a call like backgroundWindow.off('close', handler) or backgroundWindow.removeListener('close', handler) will bypass this protection. If the only concern is framework/library code calling removeAllListeners, this is fine as-is; just noting the gap.

Also, .call(backgroundWindow, ...) on originalRemoveAllListeners (which is already .bind(backgroundWindow)) is redundant — the bound this takes precedence. Consistent with line 97 which only uses .bind().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 112 - 122, The
current override only protects backgroundWindow.removeAllListeners but not
backgroundWindow.removeListener or backgroundWindow.off, so individual removal
of the 'close' handler can still occur; update the patch to also wrap/override
backgroundWindow.removeListener and backgroundWindow.off (or alias off to
removeListener if needed) to check for eventName === 'close' (and undefined if
applicable), call the original bound function to perform the removal, then
immediately re-attach closeHandler (using backgroundWindow.on("close",
closeHandler)); also remove the redundant .call(...) when invoking the
already-bound originalRemoveAllListeners/originalRemoveListener to keep usage
consistent with the earlier .bind() pattern and reference the symbols
backgroundWindow.removeAllListeners, backgroundWindow.removeListener,
backgroundWindow.off, originalRemoveAllListeners, and closeHandler when applying
the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/main/actions/startBackgroundProcess.js`:
- Around line 81-89: The closeHandler contains dead statements—remove the no-op
lines `event.returnValue = false` and `return false` and rely solely on
`event.preventDefault()` to cancel the BrowserWindow close; keep the existing
check `if (!backgroundWindow.isDestroyed()) { backgroundWindow.hide(); }` so
that closeHandler only calls event.preventDefault() and hides the
backgroundWindow when appropriate.
- Around line 128-129: Two globals — isBackgroundProcessActive and
backgroundProcessStarted — are set to true together and their semantics are
unclear; decide whether they represent distinct states (e.g., "active" vs
"everStarted") or are redundant. If distinct, add a concise inline comment at
the assignment in startBackgroundProcess (and update related functions such as
stopBackgroundProcess/cleanup handlers) clarifying which flag is reset when the
process stops and where each is toggled; if redundant, remove one and update all
references to use the remaining flag (and update tests/usages accordingly).
Ensure any reset logic (e.g., in stopBackgroundProcess, onError handlers, or
process exit hooks) updates only the intended flag(s) per your clarified
semantics.
- Around line 112-122: The current override only protects
backgroundWindow.removeAllListeners but not backgroundWindow.removeListener or
backgroundWindow.off, so individual removal of the 'close' handler can still
occur; update the patch to also wrap/override backgroundWindow.removeListener
and backgroundWindow.off (or alias off to removeListener if needed) to check for
eventName === 'close' (and undefined if applicable), call the original bound
function to perform the removal, then immediately re-attach closeHandler (using
backgroundWindow.on("close", closeHandler)); also remove the redundant
.call(...) when invoking the already-bound
originalRemoveAllListeners/originalRemoveListener to keep usage consistent with
the earlier .bind() pattern and reference the symbols
backgroundWindow.removeAllListeners, backgroundWindow.removeListener,
backgroundWindow.off, originalRemoveAllListeners, and closeHandler when applying
the changes.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ff5611 and 80d06d2.

📒 Files selected for processing (1)
  • src/main/actions/startBackgroundProcess.js

Aarushsr12
Aarushsr12 previously approved these changes Feb 23, 2026
// Override removeAllListeners to prevent removal of close handler
const originalRemoveAllListeners = backgroundWindow.removeAllListeners.bind(backgroundWindow);
backgroundWindow.removeAllListeners = (eventName) => {
if (eventName === 'close' || eventName === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why eventName === undefined?

@Aarushsr12 Aarushsr12 self-requested a review February 23, 2026 20:44
@Aarushsr12 Aarushsr12 dismissed their stale review February 23, 2026 20:44

First iteration review

resolve();
});

// Timeout fallback in case shutdown-success never comes
Copy link
Contributor

Choose a reason for hiding this comment

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

can we remove comments?
add the functioning of the code part in PR description itself

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/actions/cleanup.js (1)

3-13: ⚠️ Potential issue | 🟡 Minor

cleanup() may call app.quit() while getReadyToQuitApp still proceeds to set up IPC listeners.

When backgroundProcessStarted is falsy (or backgroundWindow is null), cleanup() calls app.quit() synchronously. However, app.quit() is async (fires events); execution then continues in getReadyToQuitApp past line 19. If the caller relies on the promise to know when it's safe to quit, the resolve() at line 52 may or may not fire before the quit completes, depending on event ordering.

This is probably fine in practice (if backgroundProcessStarted is false then backgroundWindow is also likely null → line 52 resolves immediately), but the coupling between cleanup() calling app.quit() and getReadyToQuitApp returning a promise is fragile if any future change makes those conditions diverge.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/cleanup.js` around lines 3 - 13, cleanup currently calls
app.quit() synchronously which races with the promise returned by
getReadyToQuitApp; modify cleanup (and its callers) so it does not directly call
app.quit() synchronously: make cleanup return a Promise (or accept a callback)
that either resolves after sending the "shutdown" IPC or after listening for the
app 'will-quit'/'quit' event if no backgroundWindow exists, and update
getReadyToQuitApp to await that Promise before resolving; refer to the functions
and symbols cleanup, getReadyToQuitApp, global.backgroundProcessStarted,
global.backgroundWindow, and app.quit so you locate and change the behavior
consistently.
🧹 Nitpick comments (4)
src/main/actions/startBackgroundProcess.js (3)

104-114: Redundant .call(backgroundWindow, …) on an already-bound function.

originalRemoveAllListeners is already .bind(backgroundWindow) on line 104, so the .call(backgroundWindow, ...) on lines 109 and 113 is redundant. Either use .bind() at capture time or .call() at invocation time — not both.

♻️ Simplify to just use the bound function
     backgroundWindow.removeAllListeners = (eventName) => {
       if (eventName === 'close' || eventName === undefined) {
-        originalRemoveAllListeners.call(backgroundWindow, eventName);
+        originalRemoveAllListeners(eventName);
         backgroundWindow.on("close", closeHandler);
         return backgroundWindow;
       }
-      return originalRemoveAllListeners.call(backgroundWindow, eventName);
+      return originalRemoveAllListeners(eventName);
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 104 - 114, The
wrapper around backgroundWindow.removeAllListeners binds
originalRemoveAllListeners to backgroundWindow but then redundantly calls it
with .call(backgroundWindow, ...); update the wrapper to either stop binding at
capture time or (preferably) keep the bind and invoke the bound function
directly: replace originalRemoveAllListeners.call(backgroundWindow, eventName)
with originalRemoveAllListeners(eventName) in both places inside the
backgroundWindow.removeAllListeners override, ensuring the closeHandler
reattachment logic (backgroundWindow.on("close", closeHandler)) remains
unchanged.

67-69: Switching dom-ready from once to on re-opens DevTools on every navigation.

If the background page ever reloads (e.g., due to a crash or HMR in dev), openDevTools() will fire again. This is probably intentional for dev/debug, but worth confirming — if the user manually closes DevTools, a page reload will force them open again.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 67 - 69, The change
from backgroundWindow.webContents.once('dom-ready', ...) to .on(...) causes
openDevTools() to run on every navigation/reload; revert to using
backgroundWindow.webContents.once('dom-ready', ...) so DevTools opens only the
first time the background page is ready, or alternatively gate the call with a
dev-only check (e.g., isDev/process.env.NODE_ENV) and/or a boolean flag (e.g.,
devtoolsOpened) in startBackgroundProcess to avoid re-opening after manual
close; update the handler around backgroundWindow.webContents and the
openDevTools call accordingly.

78-89: Remove dead code from closeHandler: event.returnValue = false and return false have no effect in Electron main-process BrowserWindow close events.

Only event.preventDefault() is needed to prevent the window from closing. Setting event.returnValue is a renderer-side beforeunload pattern and doesn't apply to main-process close handlers. Event handler return values are also ignored by Electron's BrowserWindow.

♻️ Proposed cleanup
     const closeHandler = (event) => {
       event.preventDefault();
-      event.returnValue = false;
       if (!backgroundWindow.isDestroyed()) {
         backgroundWindow.hide();
       }
-      return false;
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/startBackgroundProcess.js` around lines 78 - 89, The
closeHandler contains dead code: remove the renderer-style lines
event.returnValue = false and the trailing return false from the closeHandler
function and keep only event.preventDefault() and the existing
backgroundWindow.isDestroyed() check/hide logic; update the registration at
backgroundWindow.on("close", closeHandler) and the stored reference
backgroundWindow._preventCloseHandler as-is (no other behavior changes).
src/main/actions/cleanup.js (1)

26-50: Consider extracting the repeated destroy-or-fallback block into a helper.

The same 7-line conditional destruction pattern (_originalDestroy check → destroy fallback) is duplicated in the shutdown-success handler and the timeout handler.

♻️ Proposed refactor to reduce duplication
+    const destroyBackgroundWindow = () => {
+      if (global.backgroundWindow && !global.backgroundWindow.isDestroyed()) {
+        if (global.backgroundWindow._originalDestroy) {
+          global.backgroundWindow._originalDestroy();
+        } else {
+          global.backgroundWindow.destroy();
+        }
+      }
+    };
+
     ipcMain.once("shutdown-success", () => {
       clearTimeout(timeoutHandle);
-      if (
-        global.backgroundWindow &&
-        !global.backgroundWindow.isDestroyed()
-      ) {
-        if (global.backgroundWindow._originalDestroy) {
-          global.backgroundWindow._originalDestroy();
-        } else {
-          global.backgroundWindow.destroy();
-        }
-      }
+      destroyBackgroundWindow();
       resolve();
     });
      
     timeoutHandle = setTimeout(() => {
-      if (global.backgroundWindow && !global.backgroundWindow.isDestroyed()) {
-        if (global.backgroundWindow._originalDestroy) {
-          global.backgroundWindow._originalDestroy();
-        } else {
-          global.backgroundWindow.destroy();
-        }
-      }
+      destroyBackgroundWindow();
       resolve();
     }, 2000);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/actions/cleanup.js` around lines 26 - 50, The repeated conditional
block that destroys the background window should be extracted into a small
helper (e.g., destroyBackgroundWindow) and called from both the
ipcMain.once("shutdown-success", ...) handler and the timeout callback;
implement destroyBackgroundWindow to check global.backgroundWindow and
!global.backgroundWindow.isDestroyed(), then call
global.backgroundWindow._originalDestroy() if present otherwise
global.backgroundWindow.destroy(), and replace the duplicated 7-line blocks in
the ipcMain.once handler and the setTimeout callback with a call to that helper
(keep timeoutHandle and resolve logic unchanged).
🤖 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/actions/startBackgroundProcess.js`:
- Around line 104-114: The override only hardens
backgroundWindow.removeAllListeners, but backgroundWindow.removeListener and
backgroundWindow.off can still remove the closeHandler; patch both by saving
their originals (e.g., originalRemoveListener, originalOff) and wrapping them to
ignore attempts to remove the 'close' event or the specific closeHandler
(backgroundWindow.off/removeListener when eventName === 'close' or handler ===
closeHandler should be no-ops), while delegating other calls to the originals;
ensure you bind originals to backgroundWindow like originalRemoveAllListeners
and preserve return values and chaining behavior.

---

Outside diff comments:
In `@src/main/actions/cleanup.js`:
- Around line 3-13: cleanup currently calls app.quit() synchronously which races
with the promise returned by getReadyToQuitApp; modify cleanup (and its callers)
so it does not directly call app.quit() synchronously: make cleanup return a
Promise (or accept a callback) that either resolves after sending the "shutdown"
IPC or after listening for the app 'will-quit'/'quit' event if no
backgroundWindow exists, and update getReadyToQuitApp to await that Promise
before resolving; refer to the functions and symbols cleanup, getReadyToQuitApp,
global.backgroundProcessStarted, global.backgroundWindow, and app.quit so you
locate and change the behavior consistently.

---

Nitpick comments:
In `@src/main/actions/cleanup.js`:
- Around line 26-50: The repeated conditional block that destroys the background
window should be extracted into a small helper (e.g., destroyBackgroundWindow)
and called from both the ipcMain.once("shutdown-success", ...) handler and the
timeout callback; implement destroyBackgroundWindow to check
global.backgroundWindow and !global.backgroundWindow.isDestroyed(), then call
global.backgroundWindow._originalDestroy() if present otherwise
global.backgroundWindow.destroy(), and replace the duplicated 7-line blocks in
the ipcMain.once handler and the setTimeout callback with a call to that helper
(keep timeoutHandle and resolve logic unchanged).

In `@src/main/actions/startBackgroundProcess.js`:
- Around line 104-114: The wrapper around backgroundWindow.removeAllListeners
binds originalRemoveAllListeners to backgroundWindow but then redundantly calls
it with .call(backgroundWindow, ...); update the wrapper to either stop binding
at capture time or (preferably) keep the bind and invoke the bound function
directly: replace originalRemoveAllListeners.call(backgroundWindow, eventName)
with originalRemoveAllListeners(eventName) in both places inside the
backgroundWindow.removeAllListeners override, ensuring the closeHandler
reattachment logic (backgroundWindow.on("close", closeHandler)) remains
unchanged.
- Around line 67-69: The change from
backgroundWindow.webContents.once('dom-ready', ...) to .on(...) causes
openDevTools() to run on every navigation/reload; revert to using
backgroundWindow.webContents.once('dom-ready', ...) so DevTools opens only the
first time the background page is ready, or alternatively gate the call with a
dev-only check (e.g., isDev/process.env.NODE_ENV) and/or a boolean flag (e.g.,
devtoolsOpened) in startBackgroundProcess to avoid re-opening after manual
close; update the handler around backgroundWindow.webContents and the
openDevTools call accordingly.
- Around line 78-89: The closeHandler contains dead code: remove the
renderer-style lines event.returnValue = false and the trailing return false
from the closeHandler function and keep only event.preventDefault() and the
existing backgroundWindow.isDestroyed() check/hide logic; update the
registration at backgroundWindow.on("close", closeHandler) and the stored
reference backgroundWindow._preventCloseHandler as-is (no other behavior
changes).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80d06d2 and 6d7d664.

📒 Files selected for processing (2)
  • src/main/actions/cleanup.js
  • src/main/actions/startBackgroundProcess.js

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.

2 participants