Skip to content

Commit 20eb190

Browse files
committed
fix: restore suspended windows that macOS reports as occluded
1 parent 905dca7 commit 20eb190

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

‎electron/main/windows/window-service-mode.mjs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ export function createWindowServiceMode({
2828
})
2929
);
3030
};
31-
const visible = (target) => target.isVisible() && !target.isMinimized();
31+
// macOS isVisible() is also false while a window is occluded or on another
32+
// Space, so a restored window may never look "visible" when its events fire.
33+
// Any non-minimized window that is not an inactive tab (or hidden settings
34+
// window) is on screen from the user's point of view and needs its renderer.
35+
const visible = (target) => {
36+
if (target.isMinimized()) return false;
37+
if (target.isVisible()) return true;
38+
return !registry.stateFor(target)?.settingsOwner && activeFor(target) === target;
39+
};
3240
const restore = (target, state) => {
3341
if (state.serviceMode?.phase !== 'suspended') return;
3442
const saved = state.serviceMode;

‎tests/server/window-service-mode.test.mjs‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,36 @@ test('suspends all windows and restores a visible window once with its snapshot'
232232
assert.equal(state.serviceMode, null);
233233
});
234234

235+
test('restores an unminimized window even while macOS reports it occluded', async (t) => {
236+
const active = fakeWindow({ minimized: true, visible: false });
237+
const inactive = fakeWindow({ minimized: false, visible: false });
238+
let suspended = 0;
239+
const resumed = [];
240+
const { service } = harness(
241+
[
242+
[active, {}],
243+
[inactive, {}],
244+
],
245+
{
246+
activeFor: (target) => (target === inactive ? active : target),
247+
suspend: () => suspended++,
248+
resume: (window) => resumed.push(window),
249+
},
250+
);
251+
t.after(() => service.stop());
252+
253+
service.update();
254+
await waitFor(() => suspended === 2);
255+
256+
// Deminiaturized, but isVisible() is still false (occluded or another Space).
257+
active.minimized = false;
258+
service.update();
259+
await waitFor(() => resumed.length === 1);
260+
assert.deepEqual(resumed, [active]);
261+
// The hidden inactive tab keeps its suspension until it is selected.
262+
assert.equal(await observed(() => resumed.length > 1), false);
263+
});
264+
235265
test('stopping during prepare cancels the pending suspension', async (t) => {
236266
const target = fakeWindow({ minimized: true, visible: false });
237267
const state = {};

0 commit comments

Comments
 (0)