fix(page): Page.goto should properly handle historyAPI in beforeunload (#3198)
Fixes #2764.
This commit is contained in:
parent
28d92116b7
commit
d54c7edeae
@ -51,16 +51,36 @@ class NavigatorWatcher {
|
|||||||
helper.addEventListener(this._frameManager, FrameManager.Events.FrameDetached, this._checkLifecycleComplete.bind(this))
|
helper.addEventListener(this._frameManager, FrameManager.Events.FrameDetached, this._checkLifecycleComplete.bind(this))
|
||||||
];
|
];
|
||||||
|
|
||||||
const lifecycleCompletePromise = new Promise(fulfill => {
|
this._sameDocumentNavigationPromise = new Promise(fulfill => {
|
||||||
this._lifecycleCompleteCallback = fulfill;
|
this._sameDocumentNavigationCompleteCallback = fulfill;
|
||||||
});
|
});
|
||||||
this._navigationPromise = Promise.race([
|
|
||||||
this._createTimeoutPromise(),
|
this._newDocumentNavigationPromise = new Promise(fulfill => {
|
||||||
lifecycleCompletePromise
|
this._newDocumentNavigationCompleteCallback = fulfill;
|
||||||
]).then(error => {
|
|
||||||
this._cleanup();
|
|
||||||
return error;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._timeoutPromise = this._createTimeoutPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Promise<?Error>}
|
||||||
|
*/
|
||||||
|
sameDocumentNavigationPromise() {
|
||||||
|
return this._sameDocumentNavigationPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Promise<?Error>}
|
||||||
|
*/
|
||||||
|
newDocumentNavigationPromise() {
|
||||||
|
return this._newDocumentNavigationPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Promise<?Error>}
|
||||||
|
*/
|
||||||
|
timeoutPromise() {
|
||||||
|
return this._timeoutPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,13 +94,6 @@ class NavigatorWatcher {
|
|||||||
.then(() => new TimeoutError(errorMessage));
|
.then(() => new TimeoutError(errorMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {!Promise<?Error>}
|
|
||||||
*/
|
|
||||||
async navigationPromise() {
|
|
||||||
return this._navigationPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Puppeteer.Frame} frame
|
* @param {!Puppeteer.Frame} frame
|
||||||
*/
|
*/
|
||||||
@ -97,7 +110,10 @@ class NavigatorWatcher {
|
|||||||
return;
|
return;
|
||||||
if (!checkLifecycle(this._frame, this._expectedLifecycle))
|
if (!checkLifecycle(this._frame, this._expectedLifecycle))
|
||||||
return;
|
return;
|
||||||
this._lifecycleCompleteCallback();
|
if (this._hasSameDocumentNavigation)
|
||||||
|
this._sameDocumentNavigationCompleteCallback();
|
||||||
|
if (this._frame._loaderId !== this._initialLoaderId)
|
||||||
|
this._newDocumentNavigationCompleteCallback();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Puppeteer.Frame} frame
|
* @param {!Puppeteer.Frame} frame
|
||||||
@ -117,13 +133,8 @@ class NavigatorWatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
dispose() {
|
||||||
this._cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
_cleanup() {
|
|
||||||
helper.removeEventListeners(this._eventListeners);
|
helper.removeEventListeners(this._eventListeners);
|
||||||
this._lifecycleCompleteCallback(new Error('Navigation failed'));
|
|
||||||
clearTimeout(this._maximumTimer);
|
clearTimeout(this._maximumTimer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
lib/Page.js
22
lib/Page.js
@ -591,14 +591,18 @@ class Page extends EventEmitter {
|
|||||||
const mainFrame = this._frameManager.mainFrame();
|
const mainFrame = this._frameManager.mainFrame();
|
||||||
const timeout = typeof options.timeout === 'number' ? options.timeout : this._defaultNavigationTimeout;
|
const timeout = typeof options.timeout === 'number' ? options.timeout : this._defaultNavigationTimeout;
|
||||||
const watcher = new NavigatorWatcher(this._frameManager, mainFrame, timeout, options);
|
const watcher = new NavigatorWatcher(this._frameManager, mainFrame, timeout, options);
|
||||||
const navigationPromise = watcher.navigationPromise();
|
let ensureNewDocumentNavigation = false;
|
||||||
let error = await Promise.race([
|
let error = await Promise.race([
|
||||||
navigate(this._client, url, referrer),
|
navigate(this._client, url, referrer),
|
||||||
navigationPromise,
|
watcher.timeoutPromise(),
|
||||||
]);
|
]);
|
||||||
if (!error)
|
if (!error) {
|
||||||
error = await navigationPromise;
|
error = await Promise.race([
|
||||||
watcher.cancel();
|
watcher.timeoutPromise(),
|
||||||
|
ensureNewDocumentNavigation ? watcher.newDocumentNavigationPromise() : watcher.sameDocumentNavigationPromise(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
watcher.dispose();
|
||||||
helper.removeEventListeners(eventListeners);
|
helper.removeEventListeners(eventListeners);
|
||||||
if (error)
|
if (error)
|
||||||
throw error;
|
throw error;
|
||||||
@ -614,6 +618,7 @@ class Page extends EventEmitter {
|
|||||||
async function navigate(client, url, referrer) {
|
async function navigate(client, url, referrer) {
|
||||||
try {
|
try {
|
||||||
const response = await client.send('Page.navigate', {url, referrer});
|
const response = await client.send('Page.navigate', {url, referrer});
|
||||||
|
ensureNewDocumentNavigation = !!response.loaderId;
|
||||||
return response.errorText ? new Error(`${response.errorText} at ${url}`) : null;
|
return response.errorText ? new Error(`${response.errorText} at ${url}`) : null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
@ -644,7 +649,12 @@ class Page extends EventEmitter {
|
|||||||
|
|
||||||
const responses = new Map();
|
const responses = new Map();
|
||||||
const listener = helper.addEventListener(this._networkManager, NetworkManager.Events.Response, response => responses.set(response.url(), response));
|
const listener = helper.addEventListener(this._networkManager, NetworkManager.Events.Response, response => responses.set(response.url(), response));
|
||||||
const error = await watcher.navigationPromise();
|
const error = await Promise.race([
|
||||||
|
watcher.timeoutPromise(),
|
||||||
|
watcher.sameDocumentNavigationPromise(),
|
||||||
|
watcher.newDocumentNavigationPromise()
|
||||||
|
]);
|
||||||
|
watcher.dispose();
|
||||||
helper.removeEventListeners([listener]);
|
helper.removeEventListeners([listener]);
|
||||||
if (error)
|
if (error)
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -552,7 +552,7 @@ module.exports.addTests = function({testRunner, expect, headless}) {
|
|||||||
expect(response.status()).toBe(200);
|
expect(response.status()).toBe(200);
|
||||||
expect(response.securityDetails()).toBe(null);
|
expect(response.securityDetails()).toBe(null);
|
||||||
});
|
});
|
||||||
xit('should work when page calls history API in beforeunload', async({page, server}) => {
|
it('should work when page calls history API in beforeunload', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false);
|
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false);
|
||||||
|
Loading…
Reference in New Issue
Block a user