1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[PM-5189] Implementing a methodology for triggering subframe updates from layout-shift

This commit is contained in:
Cesar Gonzalez
2024-06-13 18:10:14 -05:00
parent 2329445d45
commit 06ac1d1b64
3 changed files with 28 additions and 9 deletions

View File

@@ -253,6 +253,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
if (pageDetails.frameId !== 0 && pageDetails.details.fields.length) { if (pageDetails.frameId !== 0 && pageDetails.details.fields.length) {
void this.buildSubFrameOffsets(pageDetails.tab, pageDetails.frameId, pageDetails.details.url); void this.buildSubFrameOffsets(pageDetails.tab, pageDetails.frameId, pageDetails.details.url);
void BrowserApi.tabSendMessage(pageDetails.tab, {
command: "setupAutofillInlineMenuReflowObserver",
});
} }
const pageDetailsMap = this.pageDetailsForTab[sender.tab.id]; 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 * @param isOpeningFullInlineMenu - Identifies whether the full inline menu should be forced open regardless of other states
*/ */
private async openInlineMenu(isFocusingFieldElement = false, isOpeningFullInlineMenu = false) { private async openInlineMenu(isFocusingFieldElement = false, isOpeningFullInlineMenu = false) {
this.clearDelayedInlineMenuClosure();
const currentTab = await BrowserApi.getTabFromCurrentWindowId(); const currentTab = await BrowserApi.getTabFromCurrentWindowId();
await BrowserApi.tabSendMessage( await BrowserApi.tabSendMessage(

View File

@@ -28,6 +28,7 @@ export type AutofillOverlayContentExtensionMessageHandlers = {
getSubFrameOffsets: ({ message }: AutofillExtensionMessageParam) => Promise<SubFrameOffsetData>; getSubFrameOffsets: ({ message }: AutofillExtensionMessageParam) => Promise<SubFrameOffsetData>;
getSubFrameOffsetsFromWindowMessage: ({ message }: AutofillExtensionMessageParam) => void; getSubFrameOffsetsFromWindowMessage: ({ message }: AutofillExtensionMessageParam) => void;
checkMostRecentlyFocusedFieldHasValue: () => boolean; checkMostRecentlyFocusedFieldHasValue: () => boolean;
setupAutofillInlineMenuReflowObserver: () => void;
destroyAutofillInlineMenuListeners: () => void; destroyAutofillInlineMenuListeners: () => void;
}; };

View File

@@ -66,6 +66,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
getSubFrameOffsetsFromWindowMessage: ({ message }) => getSubFrameOffsetsFromWindowMessage: ({ message }) =>
this.getSubFrameOffsetsFromWindowMessage(message), this.getSubFrameOffsetsFromWindowMessage(message),
checkMostRecentlyFocusedFieldHasValue: () => this.mostRecentlyFocusedFieldHasValue(), checkMostRecentlyFocusedFieldHasValue: () => this.mostRecentlyFocusedFieldHasValue(),
setupAutofillInlineMenuReflowObserver: () => this.setupPageReflowEventListeners(),
destroyAutofillInlineMenuListeners: () => this.destroy(), destroyAutofillInlineMenuListeners: () => this.destroy(),
}; };
@@ -480,7 +481,13 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
await this.sendExtensionMessage("updateIsFieldCurrentlyFocused", { await this.sendExtensionMessage("updateIsFieldCurrentlyFocused", {
isFieldCurrentlyFocused: true, isFieldCurrentlyFocused: true,
}); });
this.clearUserInteractionEventTimeout(); if (this.userInteractionEventTimeout) {
this.clearUserInteractionEventTimeout();
void this.toggleInlineMenuHidden(false, true);
void this.sendExtensionMessage("closeAutofillInlineMenu", {
forceCloseInlineMenu: true,
});
}
const initiallyFocusedField = this.mostRecentlyFocusedField; const initiallyFocusedField = this.mostRecentlyFocusedField;
await this.updateMostRecentlyFocusedField(formFieldElement); await this.updateMostRecentlyFocusedField(formFieldElement);
@@ -785,6 +792,10 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
} }
private setupPageReflowEventListeners() { private setupPageReflowEventListeners() {
if (this.reflowPerformanceObserver || this.reflowMutationObserver) {
return;
}
if ("PerformanceObserver" in window && "LayoutShift" in window) { if ("PerformanceObserver" in window && "LayoutShift" in window) {
this.reflowPerformanceObserver = new PerformanceObserver( this.reflowPerformanceObserver = new PerformanceObserver(
throttle(this.updateSubFrameOffsetsFromLayoutShiftEvent.bind(this), 10), throttle(this.updateSubFrameOffsetsFromLayoutShiftEvent.bind(this), 10),
@@ -795,7 +806,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
} }
this.reflowMutationObserver = new MutationObserver( this.reflowMutationObserver = new MutationObserver(
throttle(this.updateSubFrameOffsetsFromDomMutationEvent.bind(this), 10), throttle(this.updateSubFrameForReflow.bind(this), 10),
); );
this.reflowMutationObserver.observe(globalThis.document.documentElement, { this.reflowMutationObserver.observe(globalThis.document.documentElement, {
childList: true, childList: true,
@@ -809,17 +820,20 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
for (let index = 0; index < entries.length; index++) { for (let index = 0; index < entries.length; index++) {
const entry = entries[index]; const entry = entries[index];
if (entry.sources?.length) { if (entry.sources?.length) {
this.clearUserInteractionEventTimeout(); this.updateSubFrameForReflow();
this.clearRecalculateSubFrameOffsetsTimeout();
void this.sendExtensionMessage("updateSubFrameOffsetsForReflowEvent");
return; return;
} }
} }
}; };
private updateSubFrameOffsetsFromDomMutationEvent = async () => { private updateSubFrameForReflow = () => {
this.clearUserInteractionEventTimeout(); if (this.userInteractionEventTimeout) {
this.clearRecalculateSubFrameOffsetsTimeout(); this.clearUserInteractionEventTimeout();
void this.toggleInlineMenuHidden(false, true);
void this.sendExtensionMessage("closeAutofillInlineMenu", {
forceCloseInlineMenu: true,
});
}
void this.sendExtensionMessage("updateSubFrameOffsetsForReflowEvent"); void this.sendExtensionMessage("updateSubFrameOffsetsForReflowEvent");
}; };
@@ -920,6 +934,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
private clearUserInteractionEventTimeout() { private clearUserInteractionEventTimeout() {
if (this.userInteractionEventTimeout) { if (this.userInteractionEventTimeout) {
clearTimeout(this.userInteractionEventTimeout); clearTimeout(this.userInteractionEventTimeout);
this.userInteractionEventTimeout = null;
} }
} }
@@ -975,7 +990,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
globalThis.addEventListener(EVENTS.MESSAGE, this.handleWindowMessageEvent); globalThis.addEventListener(EVENTS.MESSAGE, this.handleWindowMessageEvent);
globalThis.document.addEventListener(EVENTS.VISIBILITYCHANGE, this.handleVisibilityChangeEvent); globalThis.document.addEventListener(EVENTS.VISIBILITYCHANGE, this.handleVisibilityChangeEvent);
globalThis.addEventListener(EVENTS.FOCUSOUT, this.handleFormFieldBlurEvent); globalThis.addEventListener(EVENTS.FOCUSOUT, this.handleFormFieldBlurEvent);
this.setupPageReflowEventListeners();
this.setOverlayRepositionEventListeners(); this.setOverlayRepositionEventListeners();
}; };