From 52561448e121eecd804e24df00e7d5eaaa74d2e5 Mon Sep 17 00:00:00 2001 From: Adam Raine Date: Tue, 20 Dec 2022 17:31:21 -0500 Subject: [PATCH] [Lighthouse] Dismiss dialogs during navigation mode Required for bfcache testing in Lighthouse to work: https://github.com/GoogleChrome/lighthouse/pull/14465 Bug: None Change-Id: I4faf54e5dee9acdf8ffde4ee8762c21ed7fb50b5 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/4117673 Commit-Queue: Adam Raine Reviewed-by: Benedikt Meurer Reviewed-by: Paul Irish --- front_end/core/sdk/ResourceTreeModel.ts | 7 +++++-- .../lighthouse/LighthouseProtocolService.ts | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/front_end/core/sdk/ResourceTreeModel.ts b/front_end/core/sdk/ResourceTreeModel.ts index a7da8f08b5..426c481fcd 100644 --- a/front_end/core/sdk/ResourceTreeModel.ts +++ b/front_end/core/sdk/ResourceTreeModel.ts @@ -655,6 +655,7 @@ export enum Events { BackForwardCacheDetailsUpdated = 'BackForwardCacheDetailsUpdated', PrerenderingStatusUpdated = 'PrerenderingStatusUpdated', PrerenderAttemptCompleted = 'PrerenderAttemptCompleted', + JavaScriptDialogOpening = 'JavaScriptDialogOpening', } export type EventTypes = { @@ -677,6 +678,7 @@ export type EventTypes = { [Events.BackForwardCacheDetailsUpdated]: ResourceTreeFrame, [Events.PrerenderingStatusUpdated]: ResourceTreeFrame, [Events.PrerenderAttemptCompleted]: Protocol.Page.PrerenderAttemptCompletedEvent, + [Events.JavaScriptDialogOpening]: Protocol.Page.JavascriptDialogOpeningEvent, }; export class ResourceTreeFrame { @@ -1154,8 +1156,9 @@ export class PageDispatcher implements ProtocolProxyApi.PageDispatcher { this.#resourceTreeModel.dispatchEventToListeners(Events.FrameResized); } - javascriptDialogOpening({hasBrowserHandler}: Protocol.Page.JavascriptDialogOpeningEvent): void { - if (!hasBrowserHandler) { + javascriptDialogOpening(event: Protocol.Page.JavascriptDialogOpeningEvent): void { + this.#resourceTreeModel.dispatchEventToListeners(Events.JavaScriptDialogOpening, event); + if (!event.hasBrowserHandler) { void this.#resourceTreeModel.agent.invoke_handleJavaScriptDialog({accept: false}); } } diff --git a/front_end/panels/lighthouse/LighthouseProtocolService.ts b/front_end/panels/lighthouse/LighthouseProtocolService.ts index cdc7eb118d..f877c8486b 100644 --- a/front_end/panels/lighthouse/LighthouseProtocolService.ts +++ b/front_end/panels/lighthouse/LighthouseProtocolService.ts @@ -64,6 +64,7 @@ export class ProtocolService { private parallelConnection?: ProtocolClient.InspectorBackend.Connection; private lighthouseWorkerPromise?: Promise; private lighthouseMessageUpdateCallback?: ((arg0: string) => void); + private removeDialogHandler?: () => void; private configForTesting?: Object; async attach(): Promise { @@ -92,6 +93,23 @@ export class ProtocolService { this.dispatchProtocolMessage(message); }); + // Lighthouse implements its own dialog handler like this, however its lifecycle ends when + // the internal Lighthouse session is disposed. + // + // If the page is reloaded near the end of the run (e.g. bfcache testing), the Lighthouse + // internal session can be disposed before a dialog message appears. This allows the dialog + // to block important Lighthouse teardown operations in LighthouseProtocolService. + // + // To ensure the teardown operations can proceed, we need a dialog handler which lasts until + // the LighthouseProtocolService detaches. + const dialogHandler = (): void => { + void mainTarget.pageAgent().invoke_handleJavaScriptDialog({accept: true}); + }; + + resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.JavaScriptDialogOpening, dialogHandler); + this.removeDialogHandler = (): void => + resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.JavaScriptDialogOpening, dialogHandler); + this.parallelConnection = connection; this.targetInfos = childTargetManager.targetInfos(); this.mainFrameId = mainFrame.id; @@ -163,6 +181,7 @@ export class ProtocolService { await oldParallelConnection.disconnect(); } await SDK.TargetManager.TargetManager.instance().resumeAllTargets(); + this.removeDialogHandler?.(); } registerStatusCallback(callback: (arg0: string) => void): void {