feat: Restore webContents navigation history and page state (#45433)

* feat: Working navigationHistory.restore with just title/url

* feat: Restore page state, too

* chore: Docs, lint, tests

* Implement feedback

* More magic

* Make _awaitNextLoad truly private

* Implement API group feedback

* One more round of feedback
This commit is contained in:
Felix Rieseberg 2025-02-11 15:09:38 -08:00 committed by GitHub
parent 6fdfca6e49
commit 9f47c9a051
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 259 additions and 10 deletions

View file

@ -8,7 +8,7 @@ import * as deprecate from '@electron/internal/common/deprecate';
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
import { app, ipcMain, session, webFrameMain, dialog } from 'electron/main';
import type { BrowserWindowConstructorOptions, MessageBoxOptions } from 'electron/main';
import type { BrowserWindowConstructorOptions, MessageBoxOptions, NavigationEntry } from 'electron/main';
import * as path from 'path';
import * as url from 'url';
@ -343,8 +343,8 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
type LoadError = { errorCode: number, errorDescription: string, url: string };
WebContents.prototype.loadURL = function (url, options) {
const p = new Promise<void>((resolve, reject) => {
function _awaitNextLoad (this: Electron.WebContents, navigationUrl: string) {
return new Promise<void>((resolve, reject) => {
const resolveAndCleanup = () => {
removeListeners();
resolve();
@ -402,7 +402,7 @@ WebContents.prototype.loadURL = function (url, options) {
// the only one is with a bad scheme, perhaps ERR_INVALID_ARGUMENT
// would be more appropriate.
if (!error) {
error = { errorCode: -2, errorDescription: 'ERR_FAILED', url };
error = { errorCode: -2, errorDescription: 'ERR_FAILED', url: navigationUrl };
}
finishListener();
};
@ -426,6 +426,10 @@ WebContents.prototype.loadURL = function (url, options) {
this.on('did-stop-loading', stopLoadingListener);
this.on('destroyed', stopLoadingListener);
});
};
WebContents.prototype.loadURL = function (url, options) {
const p = _awaitNextLoad.call(this, url);
// Add a no-op rejection handler to silence the unhandled rejection error.
p.catch(() => {});
this._loadURL(url, options ?? {});
@ -611,7 +615,27 @@ WebContents.prototype._init = function () {
length: this._historyLength.bind(this),
getEntryAtIndex: this._getNavigationEntryAtIndex.bind(this),
removeEntryAtIndex: this._removeNavigationEntryAtIndex.bind(this),
getAllEntries: this._getHistory.bind(this)
getAllEntries: this._getHistory.bind(this),
restore: ({ index, entries }: { index?: number, entries: NavigationEntry[] }) => {
if (index === undefined) {
index = entries.length - 1;
}
if (index < 0 || !entries[index]) {
throw new Error('Invalid index. Index must be a positive integer and within the bounds of the entries length.');
}
const p = _awaitNextLoad.call(this, entries[index].url);
p.catch(() => {});
try {
this._restoreHistory(index, entries);
} catch (error) {
return Promise.reject(error);
}
return p;
}
},
writable: false,
enumerable: true