diff --git a/src/server/electron/electron.ts b/src/server/electron/electron.ts index 5d0b86c2b2..de20ac69ef 100644 --- a/src/server/electron/electron.ts +++ b/src/server/electron/electron.ts @@ -100,19 +100,13 @@ export class ElectronApplication extends SdkObject { } async close() { - const closed = this._waitForEvent(ElectronApplication.Events.Close); + const progressController = new ProgressController(internalCallMetadata(), this); + const closed = progressController.run(progress => helper.waitForEvent(progress, this, ElectronApplication.Events.Close).promise, this._timeoutSettings.timeout({})); await this._nodeElectronHandle!.evaluate(({ app }) => app.quit()); this._nodeConnection.close(); await closed; } - private async _waitForEvent(event: string, predicate?: Function): Promise { - const progressController = new ProgressController(internalCallMetadata(), this); - if (event !== ElectronApplication.Events.Close) - this._browserContext._closePromise.then(error => progressController.abort(error)); - return progressController.run(progress => helper.waitForEvent(progress, this, event, predicate).promise, this._timeoutSettings.timeout({})); - } - async _init() { this._nodeSession.on('Runtime.executionContextCreated', (event: any) => { if (event.context.auxData && event.context.auxData.isDefault) diff --git a/src/server/frames.ts b/src/server/frames.ts index 006916588c..ffe6443083 100644 --- a/src/server/frames.ts +++ b/src/server/frames.ts @@ -483,17 +483,22 @@ export class Frame extends SdkObject { this._subtreeLifecycleEvents = events; } - setupNavigationProgressController(metadata: CallMetadata): ProgressController { - const controller = new ProgressController(metadata, this); - 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!'))); - return controller; + async raceNavigationAction(action: () => Promise): Promise { + return Promise.race([ + this._page._disconnectedPromise.then(() => { throw new Error('Navigation failed because page was closed!'); }), + this._page._crashedPromise.then(() => { throw new Error('Navigation failed because page crashed!'); }), + this._detachedPromise.then(() => { throw new Error('Navigating frame was detached!'); }), + action(), + ]); } async goto(metadata: CallMetadata, url: string, options: types.GotoOptions = {}): Promise { - const controller = this.setupNavigationProgressController(metadata); - return controller.run(async progress => { + const controller = new ProgressController(metadata, this); + return controller.run(progress => this._goto(progress, url, options), this._page._timeoutSettings.navigationTimeout(options)); + } + + private async _goto(progress: Progress, url: string, options: types.GotoOptions): Promise { + return this.raceNavigationAction(async () => { const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); progress.log(`navigating to "${url}", waiting until "${waitUntil}"`); const headers = this._page._state.extraHTTPHeaders || []; @@ -536,7 +541,7 @@ export class Frame extends SdkObject { const response = request ? request._finalRequest().response() : null; await this._page._doSlowMo(); return response; - }, this._page._timeoutSettings.navigationTimeout(options)); + }); } async _waitForNavigation(progress: Progress, options: types.NavigateOptions): Promise { @@ -672,8 +677,8 @@ export class Frame extends SdkObject { } async setContent(metadata: CallMetadata, html: string, options: types.NavigateOptions = {}): Promise { - const controller = this.setupNavigationProgressController(metadata); - return controller.run(async progress => { + const controller = new ProgressController(metadata, this); + return controller.run(progress => this.raceNavigationAction(async () => { 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}--`; @@ -694,7 +699,7 @@ export class Frame extends SdkObject { }, { html, tag }); await Promise.all([contentPromise, lifecyclePromise]); await this._page._doSlowMo(); - }, this._page._timeoutSettings.navigationTimeout(options)); + }), this._page._timeoutSettings.navigationTimeout(options)); } name(): string { diff --git a/src/server/page.ts b/src/server/page.ts index fe205978a7..5ec18f95e1 100644 --- a/src/server/page.ts +++ b/src/server/page.ts @@ -299,23 +299,22 @@ export class Page extends SdkObject { } async reload(metadata: CallMetadata, options: types.NavigateOptions): Promise { - const controller = this.mainFrame().setupNavigationProgressController(metadata); - const response = await controller.run(async progress => { + const controller = new ProgressController(metadata, this); + return controller.run(progress => this.mainFrame().raceNavigationAction(async () => { // Note: waitForNavigation may fail before we get response to reload(), // so we should await it immediately. const [response] = await Promise.all([ this.mainFrame()._waitForNavigation(progress, options), this._delegate.reload(), ]); + await this._doSlowMo(); return response; - }, this._timeoutSettings.navigationTimeout(options)); - await this._doSlowMo(); - return response; + }), this._timeoutSettings.navigationTimeout(options)); } async goBack(metadata: CallMetadata, options: types.NavigateOptions): Promise { - const controller = this.mainFrame().setupNavigationProgressController(metadata); - const response = await controller.run(async progress => { + const controller = new ProgressController(metadata, this); + return controller.run(progress => this.mainFrame().raceNavigationAction(async () => { // Note: waitForNavigation may fail before we get response to goBack, // so we should catch it immediately. let error: Error | undefined; @@ -329,15 +328,14 @@ export class Page extends SdkObject { const response = await waitPromise; if (error) throw error; + await this._doSlowMo(); return response; - }, this._timeoutSettings.navigationTimeout(options)); - await this._doSlowMo(); - return response; + }), this._timeoutSettings.navigationTimeout(options)); } async goForward(metadata: CallMetadata, options: types.NavigateOptions): Promise { - const controller = this.mainFrame().setupNavigationProgressController(metadata); - const response = await controller.run(async progress => { + const controller = new ProgressController(metadata, this); + return controller.run(progress => this.mainFrame().raceNavigationAction(async () => { // Note: waitForNavigation may fail before we get response to goForward, // so we should catch it immediately. let error: Error | undefined; @@ -351,10 +349,9 @@ export class Page extends SdkObject { const response = await waitPromise; if (error) throw error; + await this._doSlowMo(); return response; - }, this._timeoutSettings.navigationTimeout(options)); - await this._doSlowMo(); - return response; + }), this._timeoutSettings.navigationTimeout(options)); } async emulateMedia(options: { media?: types.MediaType | null, colorScheme?: types.ColorScheme | null }) { diff --git a/src/server/progress.ts b/src/server/progress.ts index c16d92b5f7..efc5cad610 100644 --- a/src/server/progress.ts +++ b/src/server/progress.ts @@ -111,10 +111,6 @@ export class ProgressController { clearTimeout(timer); } } - - abort(error: Error) { - this._forceAbort(error); - } } async function runCleanup(cleanup: () => any) {