chore: refactor goBack/goForward/reload (#3859)
These methods are the only users of waitForNavigation and waitForLoadState on the server side. This refactor lifts the Progress wrapper to the top-most goBack/goForward/reload call and leaves waitForNavigation/waitForLoadState as internal helpers. This way we get a single Progress for the actual api call.
This commit is contained in:
parent
2a66f8a066
commit
2f0d2029ca
|
|
@ -175,7 +175,7 @@ export abstract class BrowserContext extends EventEmitter {
|
|||
await waitForEvent.promise;
|
||||
}
|
||||
const pages = this.pages();
|
||||
await pages[0].mainFrame().waitForLoadState();
|
||||
await pages[0].mainFrame()._waitForLoadState(progress, 'load');
|
||||
if (pages.length !== 1 || pages[0].mainFrame().url() !== 'about:blank')
|
||||
throw new Error(`Arguments can not specify page to be opened (first url is ${pages[0].mainFrame().url()})`);
|
||||
if (this._options.isMobile || this._options.locale) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import * as types from '../types';
|
|||
import { launchProcess, waitForLine, envArrayToObject } from '../processLauncher';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import type {BrowserWindow} from 'electron';
|
||||
import { ProgressController } from '../progress';
|
||||
import { ProgressController, runAbortableTask } from '../progress';
|
||||
import { EventEmitter } from 'events';
|
||||
import { helper } from '../helper';
|
||||
import { BrowserProcess } from '../browser';
|
||||
|
|
@ -88,7 +88,7 @@ export class ElectronApplication extends EventEmitter {
|
|||
this._windows.delete(page);
|
||||
});
|
||||
this._windows.add(page);
|
||||
await page.mainFrame().waitForLoadState('domcontentloaded').catch(e => {}); // can happen after detach
|
||||
await runAbortableTask(progress => page.mainFrame()._waitForLoadState(progress, 'domcontentloaded'), page._timeoutSettings.navigationTimeout({})).catch(e => {}); // can happen after detach
|
||||
this.emit(ElectronApplication.Events.Window, page);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -424,8 +424,16 @@ export class Frame extends EventEmitter {
|
|||
this._subtreeLifecycleEvents = events;
|
||||
}
|
||||
|
||||
setupNavigationProgressController(controller: ProgressController) {
|
||||
this._page._disconnectedPromise.then(() => controller.abort(new Error('Navigation failed because page was closed!')));
|
||||
this._page._crashedPromise.then(() => controller.abort(new Error('Navigation failed because page crashed!')));
|
||||
this._detachedPromise.then(() => controller.abort(new Error('Navigating frame was detached!')));
|
||||
}
|
||||
|
||||
async goto(url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
|
||||
return runNavigationTask(this, options, async progress => {
|
||||
const controller = new ProgressController(this._page._timeoutSettings.navigationTimeout(options));
|
||||
this.setupNavigationProgressController(controller);
|
||||
return controller.run(async progress => {
|
||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||
progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
|
||||
const headers = this._page._state.extraHTTPHeaders || [];
|
||||
|
|
@ -471,31 +479,25 @@ export class Frame extends EventEmitter {
|
|||
});
|
||||
}
|
||||
|
||||
async waitForNavigation(options: types.NavigateOptions = {}): Promise<network.Response | null> {
|
||||
return runNavigationTask(this, options, async progress => {
|
||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||
progress.log(`waiting for navigation until "${waitUntil}"`);
|
||||
async _waitForNavigation(progress: Progress, options: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||
progress.log(`waiting for navigation until "${waitUntil}"`);
|
||||
|
||||
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this, Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
// Any failed navigation results in a rejection.
|
||||
if (event.error)
|
||||
return true;
|
||||
progress.log(` navigated to "${this._url}"`);
|
||||
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this, Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
// Any failed navigation results in a rejection.
|
||||
if (event.error)
|
||||
return true;
|
||||
}).promise;
|
||||
if (navigationEvent.error)
|
||||
throw navigationEvent.error;
|
||||
progress.log(` navigated to "${this._url}"`);
|
||||
return true;
|
||||
}).promise;
|
||||
if (navigationEvent.error)
|
||||
throw navigationEvent.error;
|
||||
|
||||
if (!this._subtreeLifecycleEvents.has(waitUntil))
|
||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
if (!this._subtreeLifecycleEvents.has(waitUntil))
|
||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
|
||||
const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : undefined;
|
||||
return request ? request._finalRequest().response() : null;
|
||||
});
|
||||
}
|
||||
|
||||
async waitForLoadState(state: types.LifecycleEvent = 'load', options: types.TimeoutOptions = {}): Promise<void> {
|
||||
return runNavigationTask(this, options, progress => this._waitForLoadState(progress, state));
|
||||
const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : undefined;
|
||||
return request ? request._finalRequest().response() : null;
|
||||
}
|
||||
|
||||
async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
|
||||
|
|
@ -606,7 +608,9 @@ export class Frame extends EventEmitter {
|
|||
}
|
||||
|
||||
async setContent(html: string, options: types.NavigateOptions = {}): Promise<void> {
|
||||
return runNavigationTask(this, options, async progress => {
|
||||
const controller = new ProgressController(this._page._timeoutSettings.navigationTimeout(options));
|
||||
this.setupNavigationProgressController(controller);
|
||||
return controller.run(async progress => {
|
||||
const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil;
|
||||
progress.log(`setting frame content, waiting until "${waitUntil}"`);
|
||||
const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
|
||||
|
|
@ -1087,15 +1091,6 @@ class SignalBarrier {
|
|||
}
|
||||
}
|
||||
|
||||
async function runNavigationTask<T>(frame: Frame, options: types.TimeoutOptions, task: (progress: Progress) => Promise<T>): Promise<T> {
|
||||
const page = frame._page;
|
||||
const controller = new ProgressController(page._timeoutSettings.navigationTimeout(options));
|
||||
page._disconnectedPromise.then(() => controller.abort(new Error('Navigation failed because page was closed!')));
|
||||
page._crashedPromise.then(() => controller.abort(new Error('Navigation failed because page crashed!')));
|
||||
frame._detachedPromise.then(() => controller.abort(new Error('Navigating frame was detached!')));
|
||||
return controller.run(task);
|
||||
}
|
||||
|
||||
function verifyLifecycle(name: string, waitUntil: types.LifecycleEvent): types.LifecycleEvent {
|
||||
if (waitUntil as unknown === 'networkidle0')
|
||||
waitUntil = 'networkidle';
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import { ConsoleMessage } from './console';
|
|||
import * as accessibility from './accessibility';
|
||||
import { EventEmitter } from 'events';
|
||||
import { FileChooser } from './fileChooser';
|
||||
import { runAbortableTask } from './progress';
|
||||
import { ProgressController, runAbortableTask } from './progress';
|
||||
import { assert, isError } from '../utils/utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { Selectors } from './selectors';
|
||||
|
|
@ -262,34 +262,46 @@ export class Page extends EventEmitter {
|
|||
this.emit(Page.Events.Console, message);
|
||||
}
|
||||
|
||||
async reload(options?: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const waitPromise = this.mainFrame().waitForNavigation(options);
|
||||
await this._delegate.reload();
|
||||
const response = await waitPromise;
|
||||
async reload(options: types.NavigateOptions = {}): Promise<network.Response | null> {
|
||||
const controller = new ProgressController(this._timeoutSettings.navigationTimeout(options));
|
||||
this.mainFrame().setupNavigationProgressController(controller);
|
||||
const response = await controller.run(async progress => {
|
||||
const waitPromise = this.mainFrame()._waitForNavigation(progress, options);
|
||||
await this._delegate.reload();
|
||||
return waitPromise;
|
||||
});
|
||||
await this._doSlowMo();
|
||||
return response;
|
||||
}
|
||||
|
||||
async goBack(options?: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const waitPromise = this.mainFrame().waitForNavigation(options);
|
||||
const result = await this._delegate.goBack();
|
||||
if (!result) {
|
||||
waitPromise.catch(() => {});
|
||||
return null;
|
||||
}
|
||||
const response = await waitPromise;
|
||||
async goBack(options: types.NavigateOptions = {}): Promise<network.Response | null> {
|
||||
const controller = new ProgressController(this._timeoutSettings.navigationTimeout(options));
|
||||
this.mainFrame().setupNavigationProgressController(controller);
|
||||
const response = await controller.run(async progress => {
|
||||
const waitPromise = this.mainFrame()._waitForNavigation(progress, options);
|
||||
const result = await this._delegate.goBack();
|
||||
if (!result) {
|
||||
waitPromise.catch(() => {});
|
||||
return null;
|
||||
}
|
||||
return waitPromise;
|
||||
});
|
||||
await this._doSlowMo();
|
||||
return response;
|
||||
}
|
||||
|
||||
async goForward(options?: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const waitPromise = this.mainFrame().waitForNavigation(options);
|
||||
const result = await this._delegate.goForward();
|
||||
if (!result) {
|
||||
waitPromise.catch(() => {});
|
||||
return null;
|
||||
}
|
||||
const response = await waitPromise;
|
||||
async goForward(options: types.NavigateOptions = {}): Promise<network.Response | null> {
|
||||
const controller = new ProgressController(this._timeoutSettings.navigationTimeout(options));
|
||||
this.mainFrame().setupNavigationProgressController(controller);
|
||||
const response = await controller.run(async progress => {
|
||||
const waitPromise = this.mainFrame()._waitForNavigation(progress, options);
|
||||
const result = await this._delegate.goForward();
|
||||
if (!result) {
|
||||
waitPromise.catch(() => {});
|
||||
return null;
|
||||
}
|
||||
return waitPromise;
|
||||
});
|
||||
await this._doSlowMo();
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue