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:
parent
6fdfca6e49
commit
9f47c9a051
8 changed files with 259 additions and 10 deletions
|
@ -699,12 +699,14 @@ describe('webContents module', () => {
|
|||
describe('navigationHistory.getEntryAtIndex(index) API ', () => {
|
||||
it('should fetch default navigation entry when no urls are loaded', async () => {
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: '', title: '' });
|
||||
expect(result.url).to.equal('');
|
||||
expect(result.title).to.equal('');
|
||||
});
|
||||
it('should fetch navigation entry given a valid index', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
const result = w.webContents.navigationHistory.getEntryAtIndex(0);
|
||||
expect(result).to.deep.equal({ url: urlPage1, title: 'Page 1' });
|
||||
expect(result.url).to.equal(urlPage1);
|
||||
expect(result.title).to.equal('Page 1');
|
||||
});
|
||||
it('should return null given an invalid index larger than history length', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
|
@ -763,7 +765,10 @@ describe('webContents module', () => {
|
|||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
const entries = w.webContents.navigationHistory.getAllEntries();
|
||||
const entries = w.webContents.navigationHistory.getAllEntries().map(entry => ({
|
||||
url: entry.url,
|
||||
title: entry.title
|
||||
}));
|
||||
expect(entries.length).to.equal(3);
|
||||
expect(entries[0]).to.deep.equal({ url: urlPage1, title: 'Page 1' });
|
||||
expect(entries[1]).to.deep.equal({ url: urlPage2, title: 'Page 2' });
|
||||
|
@ -774,6 +779,92 @@ describe('webContents module', () => {
|
|||
const entries = w.webContents.navigationHistory.getAllEntries();
|
||||
expect(entries.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should create a NavigationEntry with PageState that can be serialized/deserialized with JSON', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
const entries = w.webContents.navigationHistory.getAllEntries();
|
||||
const serialized = JSON.stringify(entries);
|
||||
const deserialized = JSON.parse(serialized);
|
||||
expect(deserialized).to.deep.equal(entries);
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigationHistory.restore({ index, entries }) API', () => {
|
||||
let server: http.Server;
|
||||
let serverUrl: string;
|
||||
|
||||
before(async () => {
|
||||
server = http.createServer((req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end('<html><head><title>Form</title></head><body><form><input type="text" value="value" /></form></body></html>');
|
||||
});
|
||||
serverUrl = (await listen(server)).url;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
if (server) await new Promise(resolve => server.close(resolve));
|
||||
server = null as any;
|
||||
});
|
||||
|
||||
it('should restore navigation history with PageState', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(serverUrl);
|
||||
|
||||
// Fill out the form on the page
|
||||
await w.webContents.executeJavaScript('document.querySelector("input").value = "Hi!";');
|
||||
|
||||
// PageState is committed:
|
||||
// 1) When the page receives an unload event
|
||||
// 2) During periodic serialization of page state
|
||||
// To not wait randomly for the second option, we'll trigger another load
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
// Save the navigation state
|
||||
const entries = w.webContents.navigationHistory.getAllEntries();
|
||||
|
||||
// Close the window, make a new one
|
||||
w.close();
|
||||
w = new BrowserWindow();
|
||||
|
||||
const formValue = await new Promise<string>(resolve => {
|
||||
w.webContents.once('dom-ready', () => resolve(w.webContents.executeJavaScript('document.querySelector("input").value')));
|
||||
|
||||
// Restore the navigation history
|
||||
return w.webContents.navigationHistory.restore({ index: 2, entries });
|
||||
});
|
||||
|
||||
expect(formValue).to.equal('Hi!');
|
||||
});
|
||||
|
||||
it('should handle invalid base64 pageState', async () => {
|
||||
await w.loadURL(urlPage1);
|
||||
await w.loadURL(urlPage2);
|
||||
await w.loadURL(urlPage3);
|
||||
|
||||
const brokenEntries = w.webContents.navigationHistory.getAllEntries().map(entry => ({
|
||||
...entry,
|
||||
pageState: 'invalid base64'
|
||||
}));
|
||||
|
||||
// Close the window, make a new one
|
||||
w.close();
|
||||
w = new BrowserWindow();
|
||||
await w.webContents.navigationHistory.restore({ index: 2, entries: brokenEntries });
|
||||
|
||||
const entries = w.webContents.navigationHistory.getAllEntries();
|
||||
|
||||
// Check that we used the original url and titles but threw away the broken
|
||||
// pageState
|
||||
entries.forEach((entry, index) => {
|
||||
expect(entry.url).to.equal(brokenEntries[index].url);
|
||||
expect(entry.title).to.equal(brokenEntries[index].title);
|
||||
expect(entry.pageState?.length).to.be.greaterThanOrEqual(100);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue