Skip to content

Commit 34211b8

Browse files
committed
Fix startup document restore order and focus
1 parent 786ffec commit 34211b8

4 files changed

Lines changed: 65 additions & 26 deletions

File tree

‎electron/document-session.mjs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ export async function createDocumentSession(path) {
4141
return writes;
4242
}
4343
return {
44-
restore: () =>
45-
structuredClone(saved.documents.length ? saved.documents : saved.last ? [saved.last] : []),
44+
restore: () => structuredClone(saved.documents),
4645
open(key, value) {
4746
const document = entry(value);
4847
if (!document) throw Error('Invalid session document');

‎electron/main.mjs‎

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -628,31 +628,34 @@ else {
628628
return focusDocumentWindow(target);
629629
}
630630
const openingDocuments = new Map();
631-
async function openDocumentWindow(document, preferredWindow, restoreView) {
631+
async function openDocumentWindow(document, preferredWindow, restoreView, activate = true) {
632632
const started = performance.now();
633633
try {
634-
return await deliverDocumentWindow(document, preferredWindow, restoreView);
634+
return await deliverDocumentWindow(document, preferredWindow, restoreView, activate);
635635
} finally {
636636
developerOperationMetrics.fileOpen(performance.now() - started);
637637
}
638638
}
639-
async function deliverDocumentWindow(document, preferredWindow, restoreView) {
639+
async function deliverDocumentWindow(document, preferredWindow, restoreView, activate) {
640640
const identity = typeof document === 'string' ? documentIdentity(document) : null;
641641
const existing = findDocumentWindow(identity);
642-
if (existing) return focusDocumentWindow(existing);
643-
if (identity && openingDocuments.has(identity))
644-
return focusDocumentWindow(await openingDocuments.get(identity));
642+
if (existing) return activate ? focusDocumentWindow(existing) : existing;
643+
if (identity && openingDocuments.has(identity)) {
644+
const target = await openingDocuments.get(identity);
645+
return activate ? focusDocumentWindow(target) : target;
646+
}
645647
const operation = (async () => {
646648
const candidates = [preferredWindow, focusedWindow(), ...windows.keys()];
647649
const target = candidates.find(isBlankStartPage);
648-
if (!target) return createWindow(document, restoreView);
650+
if (!target) return createWindow(document, restoreView, null, 'general', activate);
649651
const state = windows.get(target);
650652
state.documentIdentity = identity;
653+
state.restoreView = restoreView;
651654
state.documents.push(document);
652655
state.unkeyedAnnotationSource = annotationSourceForDocument(document);
653656
state.performance.hasDocument = true;
654657
target.webContents.send('documents:available');
655-
return focusDocumentWindow(target);
658+
return activate ? focusDocumentWindow(target) : target;
656659
})();
657660
if (identity) openingDocuments.set(identity, operation);
658661
try {
@@ -666,6 +669,7 @@ else {
666669
restoreView,
667670
settingsOwner = null,
668671
settingsSection = 'general',
672+
activate = true,
669673
) {
670674
let window;
671675
const backend = settingsOwner
@@ -912,7 +916,7 @@ else {
912916
const showLoadedWindow = () => {
913917
hideNativeMenuBar(window);
914918
if (!backgroundRenderSmoke && !window.isVisible()) {
915-
if (process.argv.includes('--background')) window.showInactive();
919+
if (!activate || process.argv.includes('--background')) window.showInactive();
916920
else window.show();
917921
}
918922
updatePerformanceSampling(window);
@@ -2337,22 +2341,32 @@ else {
23372341
trustedWindow(event);
23382342
await createWindow();
23392343
});
2344+
// Drain external launch requests before restoring the previous session. Requests
2345+
// arriving while a backend starts also take priority over the next restore.
2346+
let externalLaunch = false;
2347+
const openLaunchFiles = async () => {
2348+
if (ciLaunchCheck || !pendingFiles.length) return;
2349+
externalLaunch = true;
2350+
await deliverPendingFiles();
2351+
};
2352+
await openLaunchFiles();
23402353
if (!smoke && !ciLaunchCheck && preferences.load().restoreDocuments) {
23412354
for (const document of documentSession.restore()) {
2355+
await openLaunchFiles();
23422356
try {
23432357
await validateSystemPDF(document.path);
23442358
} catch {
23452359
continue;
23462360
}
2347-
if (!pendingFiles.includes(document.path))
2348-
await openDocumentWindow(document.path, null, document.view);
2361+
await openLaunchFiles();
2362+
const firstRestore = windows.size === 0;
2363+
const restored = await openDocumentWindow(document.path, null, document.view, false);
2364+
await openLaunchFiles();
2365+
if (firstRestore && !externalLaunch) focusDocumentWindow(restored);
23492366
}
23502367
}
2351-
window =
2352-
[...windows.keys()][0] ||
2353-
(!ciLaunchCheck && pendingFiles.length
2354-
? await openDocumentWindow(pendingFiles.shift())
2355-
: await createWindow());
2368+
await openLaunchFiles();
2369+
window = [...windows.keys()][0] || (await createWindow());
23562370
backend = windows.get(window).backend;
23572371
documentsReady = true;
23582372
if (!ciLaunchCheck) await deliverPendingFiles();

‎electron/session-smoke.mjs‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,20 @@ export async function verifySession(first, windows, createWindow) {
3434
sidebar: false,
3535
showTranslations: false,
3636
};
37-
const second = await createWindow(a, view),
38-
third = await createWindow(b);
37+
const second = await createWindow(a, view);
3938
await wait(second, 'window.previewReady');
39+
second.show();
40+
second.focus();
41+
for (let i = 0; i < 100 && !second.isFocused(); i++)
42+
await new Promise((resolve) => setTimeout(resolve, 50));
43+
assert.equal(second.isFocused(), true);
44+
const third = await createWindow(b, undefined, null, 'general', false);
4045
await wait(third, 'window.previewReady');
46+
for (let i = 0; i < 100 && !third.isVisible(); i++)
47+
await new Promise((resolve) => setTimeout(resolve, 50));
48+
assert.equal(third.isVisible(), true);
49+
assert.equal(third.isFocused(), false);
50+
assert.equal(second.isFocused(), true);
4151
await second.webContents.executeJavaScript('window.previewSaveReadingView()');
4252
await third.webContents.executeJavaScript('window.previewSaveReadingView()');
4353
const restored = (await createDocumentSession(store)).restore();
@@ -51,9 +61,7 @@ export async function verifySession(first, windows, createWindow) {
5161
);
5262
third.webContents.send('reader:action', 'close-document');
5363
await wait(third, 'window.previewRenderDiagnostics().totalPages===0');
54-
const fallback = (await createDocumentSession(store)).restore();
55-
assert.equal(fallback.length, 1);
56-
assert.equal(fallback[0].path, b);
64+
assert.deepEqual((await createDocumentSession(store)).restore(), []);
5765
await first.webContents.executeJavaScript(
5866
'window.previewPreferences.save({restoreDocuments:false})',
5967
);
@@ -67,8 +75,9 @@ export async function verifySession(first, windows, createWindow) {
6775
JSON.stringify({
6876
passed: true,
6977
multipleDocuments: true,
78+
backgroundRestorePreservesFocus: true,
7079
restoredPosition: true,
71-
lastClosedFallback: true,
80+
allDocumentsClosed: true,
7281
sharedOptOut: true,
7382
}),
7483
);

‎server/document-session.test.mjs‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const view = {
1616
showTranslations: true,
1717
};
1818

19-
test('session persists multiple documents and positions; closing all restores only the last closed document', async () => {
19+
test('session persists multiple documents and positions; closing all restores nothing', async () => {
2020
const dir = await mkdtemp(join(tmpdir(), 'reader-session-'));
2121
try {
2222
const path = join(dir, 'session.json'),
@@ -36,9 +36,26 @@ test('session persists multiple documents and positions; closing all restores on
3636
assert.deepEqual((await createDocumentSession(path)).restore(), [{ path: '/tmp/a.pdf', view }]);
3737
await session.close(1);
3838
await session.close(1);
39-
assert.deepEqual((await createDocumentSession(path)).restore(), [{ path: '/tmp/a.pdf', view }]);
39+
assert.deepEqual((await createDocumentSession(path)).restore(), []);
4040
assert.equal(JSON.parse(await readFile(path)).documents.length, 0);
4141
} finally {
4242
await rm(dir, { recursive: true, force: true });
4343
}
4444
});
45+
46+
test('empty documents do not restore a legacy last closed document', async () => {
47+
const dir = await mkdtemp(join(tmpdir(), 'reader-session-'));
48+
try {
49+
const path = join(dir, 'session.json');
50+
await writeFile(
51+
path,
52+
JSON.stringify({
53+
documents: [],
54+
last: { path: '/tmp/legacy-closed.pdf' },
55+
}),
56+
);
57+
assert.deepEqual((await createDocumentSession(path)).restore(), []);
58+
} finally {
59+
await rm(dir, { recursive: true, force: true });
60+
}
61+
});

0 commit comments

Comments
 (0)