From 06ac1d1b647f5ecd03303033983b9386fbbc5c47 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Thu, 13 Jun 2024 18:10:14 -0500 Subject: [PATCH] [PM-5189] Implementing a methodology for triggering subframe updates from layout-shift --- .../autofill/background/overlay.background.ts | 4 +++ .../autofill-overlay-content.service.ts | 1 + .../autofill-overlay-content.service.ts | 32 +++++++++++++------ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 5d1cf35453d..218bc1ea0b5 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -253,6 +253,9 @@ export class OverlayBackground implements OverlayBackgroundInterface { if (pageDetails.frameId !== 0 && pageDetails.details.fields.length) { void this.buildSubFrameOffsets(pageDetails.tab, pageDetails.frameId, pageDetails.details.url); + void BrowserApi.tabSendMessage(pageDetails.tab, { + command: "setupAutofillInlineMenuReflowObserver", + }); } const pageDetailsMap = this.pageDetailsForTab[sender.tab.id]; @@ -780,6 +783,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param isOpeningFullInlineMenu - Identifies whether the full inline menu should be forced open regardless of other states */ private async openInlineMenu(isFocusingFieldElement = false, isOpeningFullInlineMenu = false) { + this.clearDelayedInlineMenuClosure(); const currentTab = await BrowserApi.getTabFromCurrentWindowId(); await BrowserApi.tabSendMessage( diff --git a/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts index 750e532aaaf..1cb06a788a7 100644 --- a/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts @@ -28,6 +28,7 @@ export type AutofillOverlayContentExtensionMessageHandlers = { getSubFrameOffsets: ({ message }: AutofillExtensionMessageParam) => Promise; getSubFrameOffsetsFromWindowMessage: ({ message }: AutofillExtensionMessageParam) => void; checkMostRecentlyFocusedFieldHasValue: () => boolean; + setupAutofillInlineMenuReflowObserver: () => void; destroyAutofillInlineMenuListeners: () => void; }; diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index c1df9124e06..047f87f0c70 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -66,6 +66,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ getSubFrameOffsetsFromWindowMessage: ({ message }) => this.getSubFrameOffsetsFromWindowMessage(message), checkMostRecentlyFocusedFieldHasValue: () => this.mostRecentlyFocusedFieldHasValue(), + setupAutofillInlineMenuReflowObserver: () => this.setupPageReflowEventListeners(), destroyAutofillInlineMenuListeners: () => this.destroy(), }; @@ -480,7 +481,13 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ await this.sendExtensionMessage("updateIsFieldCurrentlyFocused", { isFieldCurrentlyFocused: true, }); - this.clearUserInteractionEventTimeout(); + if (this.userInteractionEventTimeout) { + this.clearUserInteractionEventTimeout(); + void this.toggleInlineMenuHidden(false, true); + void this.sendExtensionMessage("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + } const initiallyFocusedField = this.mostRecentlyFocusedField; await this.updateMostRecentlyFocusedField(formFieldElement); @@ -785,6 +792,10 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ } private setupPageReflowEventListeners() { + if (this.reflowPerformanceObserver || this.reflowMutationObserver) { + return; + } + if ("PerformanceObserver" in window && "LayoutShift" in window) { this.reflowPerformanceObserver = new PerformanceObserver( throttle(this.updateSubFrameOffsetsFromLayoutShiftEvent.bind(this), 10), @@ -795,7 +806,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ } this.reflowMutationObserver = new MutationObserver( - throttle(this.updateSubFrameOffsetsFromDomMutationEvent.bind(this), 10), + throttle(this.updateSubFrameForReflow.bind(this), 10), ); this.reflowMutationObserver.observe(globalThis.document.documentElement, { childList: true, @@ -809,17 +820,20 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ for (let index = 0; index < entries.length; index++) { const entry = entries[index]; if (entry.sources?.length) { - this.clearUserInteractionEventTimeout(); - this.clearRecalculateSubFrameOffsetsTimeout(); - void this.sendExtensionMessage("updateSubFrameOffsetsForReflowEvent"); + this.updateSubFrameForReflow(); return; } } }; - private updateSubFrameOffsetsFromDomMutationEvent = async () => { - this.clearUserInteractionEventTimeout(); - this.clearRecalculateSubFrameOffsetsTimeout(); + private updateSubFrameForReflow = () => { + if (this.userInteractionEventTimeout) { + this.clearUserInteractionEventTimeout(); + void this.toggleInlineMenuHidden(false, true); + void this.sendExtensionMessage("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + } void this.sendExtensionMessage("updateSubFrameOffsetsForReflowEvent"); }; @@ -920,6 +934,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ private clearUserInteractionEventTimeout() { if (this.userInteractionEventTimeout) { clearTimeout(this.userInteractionEventTimeout); + this.userInteractionEventTimeout = null; } } @@ -975,7 +990,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ globalThis.addEventListener(EVENTS.MESSAGE, this.handleWindowMessageEvent); globalThis.document.addEventListener(EVENTS.VISIBILITYCHANGE, this.handleVisibilityChangeEvent); globalThis.addEventListener(EVENTS.FOCUSOUT, this.handleFormFieldBlurEvent); - this.setupPageReflowEventListeners(); this.setOverlayRepositionEventListeners(); };