Open Bug 2048223 Opened 22 days ago Updated 13 days ago

OS taskbar/dock "Close N windows" sends a per-window WM_CLOSE instead of a single quit signal, causing a race in the last-window close path

Categories

(Firefox :: Tabbed Browser, defect)

Desktop
Windows
defect

Tracking

()

People

(Reporter: jporter, Unassigned, NeedInfo)

References

(Blocks 1 open bug)

Details

Platforms Affected
Windows and Linux. macOS sends a single quit-application-requested signal and is unaffected.

Description
When a user invokes the OS-level "Close all windows" / "Quit N windows" action from the taskbar or dock, the window manager delivers a close signal to each window rather than issuing a single application-quit request. macOS is unaffected because it dispatches one quit-application-requested signal. Since each window is torn down independently, the per-window "last window closing" check does not always trigger. The specific race window is widened when the topmost window to close has a heavier page loaded and takes slightly longer to shut down, so the window(s) underneath will continue to detect it in their last-window check.

  • A window's .closed flag does not flip until after its close handler returns.
  • CanCloseWindow -> browser.permitUnload() performs synchronous IPC that spins the event loop, allowing the next window's window-close handler to dispatch and run to completion before the first window's handler returns.
  • As a result, multiple closing windows each observe the others as still open, none of them concludes it is the "last window," and the application-quit branch is skipped entirely.

This issue was discovered when working on Enterprise Firefox, so I'll be including some of that detail here, but this issue is not enterprise-specific. Rather it is another symptom of this underlying bug, surfaced because we implemented a custom signout/close dialog when the last browser window is closed or the application is quit. I found some related bugs that are the result of Windows/Linux closing the windows separately such as:

  • Bug 1818971: session restore loses windows
  • Bug 1858782: browser.tabs.warnOnClose prompts show on every window that is closed
  • Bug 2040447: Enterprise signout prompt is not shown since no window detects it's the last window closing

More detail and logs on the Enterprise-specific symptom and debugging can be viewed in bug 2040447.

Based on my debugging and the various related bugs, the proposed fix by :dao seems like a sound way of resolving this.

The bug describes a race condition when the OS (Windows taskbar "Close N windows", Linux/GNOME dock equivalent) sends individual close requests to each browser window instead of a unified quit signal (macOS is unaffected, as it sends a single quit-application-requested).

This breaks the "last window" detection logic:
• warnAboutClosingWindow() / browser-lastwindow-close-* observers (in browser/base/content/browser.js).
• CanCloseWindow() + browser.permitUnload() (synchronous IPC that spins the event loop).
• The .closed flag + otherWindowExists checks that decide between per-window close vs. "treat as quit" (session restore, prompts, Enterprise signout, etc.).
• Related effects: session restore losing windows (bug 1818971), warnOnClose prompting per-window (bug 1858782), and the Enterprise prompt not appearing (bug 2040447, which this blocks).

The core logic lives in the tabbed browser front-end:
• browser/base/content/browser.js (CanCloseWindow, WindowIsClosing, warnAboutClosingWindow, closeWindow).
• browser/components/BrowserGlue.sys.mjs (observers for browser-lastwindow-close-requested/granted and quit integration).
• browser/components/sessionstore/SessionStore.sys.mjs.
• Supporting pieces in browser/modules/BrowserDOMWindow.sys.mjs and xpfe/appshell/AppWindow.cpp (the platform RequestWindowClose path that feeds into this).

Platform widget code (e.g., widget/windows/nsWindow.cpp for WM_CLOSE, GTK equivalent) just delivers the per-window closes—the bug and the race are in how the browser layer handles them.

Component: General → Tabbed Browser

The severity field is not set for this bug.
:dao, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(dao+bmo)
You need to log in before you can comment on or make changes to this bug.