mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-11-01 16:15:24 +00:00
fix(electron): allow explicit quit with active connections (#1522)
This commit is contained in:
1 parent
4c2f54dac0
commit
5860921cbe
2 files changed
+103
No files matched your search
@@ -1546,6 +1546,12 @@ function createWindow() {
|
|||||||
console.log("Frontend loaded successfully");
|
console.log("Frontend loaded successfully");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mainWindow.webContents.on("will-prevent-unload", (event) => {
|
||||||
|
// Explicit Quit must not be vetoed by the renderer's connection guard.
|
||||||
|
// In this Electron event, preventDefault allows the unload to proceed.
|
||||||
|
if (isQuitting) event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
mainWindow.on("close", (event) => {
|
mainWindow.on("close", (event) => {
|
||||||
if (!isQuitting && tray && !tray.isDestroyed()) {
|
if (!isQuitting && tray && !tray.isDestroyed()) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { EventEmitter } from "node:events";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import vm from "node:vm";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const main = fs.readFileSync(
|
||||||
|
new URL("../../../../electron/main.cjs", import.meta.url),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
const shell = fs.readFileSync(
|
||||||
|
new URL("../../../ui/AppShell.tsx", import.meta.url),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
function harness(active = true, hasTray = true) {
|
||||||
|
const app = new EventEmitter();
|
||||||
|
const window = Object.assign(new EventEmitter(), {
|
||||||
|
webContents: new EventEmitter(),
|
||||||
|
hide: vi.fn(),
|
||||||
|
});
|
||||||
|
const context = vm.createContext({
|
||||||
|
isQuitting: false,
|
||||||
|
app,
|
||||||
|
mainWindow: window,
|
||||||
|
console,
|
||||||
|
tray: hasTray ? { isDestroyed: () => false } : null,
|
||||||
|
hasActiveConnection: () => active,
|
||||||
|
});
|
||||||
|
// Run the production registrations together, sharing the actual quit flag.
|
||||||
|
vm.runInContext(
|
||||||
|
main.slice(
|
||||||
|
main.indexOf(' mainWindow.webContents.on("did-finish-load"'),
|
||||||
|
main.indexOf(" mainWindow.webContents.setWindowOpenHandler"),
|
||||||
|
),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
vm.runInContext(
|
||||||
|
main.slice(
|
||||||
|
main.indexOf('app.on("before-quit"'),
|
||||||
|
main.indexOf('app.on("will-quit"'),
|
||||||
|
),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
const unload = shell.slice(
|
||||||
|
shell.indexOf(" const handleBeforeUnload ="),
|
||||||
|
shell.indexOf(' window.addEventListener("beforeunload"'),
|
||||||
|
);
|
||||||
|
vm.runInContext(
|
||||||
|
unload.replace("(event: BeforeUnloadEvent)", "(event)") +
|
||||||
|
"\nglobalThis.unload = handleBeforeUnload;",
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
const rendererUnload = () => {
|
||||||
|
const event = { preventDefault: vi.fn(), returnValue: undefined };
|
||||||
|
context.unload(event);
|
||||||
|
return event.preventDefault.mock.calls.length > 0;
|
||||||
|
};
|
||||||
|
function attemptQuit() {
|
||||||
|
app.emit("before-quit");
|
||||||
|
const close = { preventDefault: vi.fn() };
|
||||||
|
window.emit("close", close);
|
||||||
|
if (close.preventDefault.mock.calls.length) return false;
|
||||||
|
if (!rendererUnload()) return true;
|
||||||
|
const prevented = { preventDefault: vi.fn() };
|
||||||
|
window.webContents.emit("will-prevent-unload", prevented);
|
||||||
|
return prevented.preventDefault.mock.calls.length > 0;
|
||||||
|
}
|
||||||
|
return { window, rendererUnload, attemptQuit };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("desktop quit with active connections", () => {
|
||||||
|
it.each([true, false])(
|
||||||
|
"allows explicit quit when the renderer vetoes unloading (tray: %s)",
|
||||||
|
(hasTray) => {
|
||||||
|
const app = harness(true, hasTray);
|
||||||
|
expect(app.attemptQuit()).toBe(true);
|
||||||
|
expect(app.window.hide).not.toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
it("keeps the unload guard during ordinary navigation or reload", () => {
|
||||||
|
const app = harness();
|
||||||
|
expect(app.rendererUnload()).toBe(true);
|
||||||
|
const event = { preventDefault: vi.fn() };
|
||||||
|
app.window.webContents.emit("will-prevent-unload", event);
|
||||||
|
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it("continues hiding ordinary window closes in the tray", () => {
|
||||||
|
const app = harness();
|
||||||
|
const event = { preventDefault: vi.fn() };
|
||||||
|
app.window.emit("close", event);
|
||||||
|
expect(event.preventDefault).toHaveBeenCalledOnce();
|
||||||
|
expect(app.window.hide).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
it("quits normally when no connection blocks unloading", () => {
|
||||||
|
expect(harness(false).attemptQuit()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user