1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-13 14:53:33 +00:00

[PM-5189] Reworking how we handle updating the inline menu position

This commit is contained in:
Cesar Gonzalez
2024-06-18 10:45:09 -05:00
parent 09babb5587
commit 4295014e39
6 changed files with 102 additions and 111 deletions

View File

@@ -52,14 +52,17 @@ export type CloseInlineMenuMessage = {
overlayElement?: string; overlayElement?: string;
}; };
export type ToggleInlineMenuHiddenMessage = {
isInlineMenuHidden?: boolean;
setTransparentInlineMenu?: boolean;
};
export type OverlayBackgroundExtensionMessage = { export type OverlayBackgroundExtensionMessage = {
command: string; command: string;
portKey?: string; portKey?: string;
tab?: chrome.tabs.Tab; tab?: chrome.tabs.Tab;
sender?: string; sender?: string;
details?: AutofillPageDetails; details?: AutofillPageDetails;
isInlineMenuHidden?: boolean;
setTransparentInlineMenu?: boolean;
isFieldCurrentlyFocused?: boolean; isFieldCurrentlyFocused?: boolean;
isFieldCurrentlyFilling?: boolean; isFieldCurrentlyFilling?: boolean;
subFrameData?: SubFrameOffsetData; subFrameData?: SubFrameOffsetData;
@@ -67,7 +70,8 @@ export type OverlayBackgroundExtensionMessage = {
styles?: Partial<CSSStyleDeclaration>; styles?: Partial<CSSStyleDeclaration>;
data?: LockedVaultPendingNotificationsData; data?: LockedVaultPendingNotificationsData;
} & OverlayAddNewItemMessage & } & OverlayAddNewItemMessage &
CloseInlineMenuMessage; CloseInlineMenuMessage &
ToggleInlineMenuHiddenMessage;
export type OverlayPortMessage = { export type OverlayPortMessage = {
[key: string]: any; [key: string]: any;
@@ -99,6 +103,7 @@ export type OverlayBackgroundExtensionMessageHandlers = {
[key: string]: CallableFunction; [key: string]: CallableFunction;
autofillOverlayElementClosed: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; autofillOverlayElementClosed: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
autofillOverlayAddNewVaultItem: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; autofillOverlayAddNewVaultItem: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
triggerAutofillOverlayReposition: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
checkIsInlineMenuCiphersPopulated: ({ sender }: BackgroundSenderParam) => void; checkIsInlineMenuCiphersPopulated: ({ sender }: BackgroundSenderParam) => void;
updateFocusedFieldData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; updateFocusedFieldData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
updateIsFieldCurrentlyFocused: ({ message }: BackgroundMessageParam) => void; updateIsFieldCurrentlyFocused: ({ message }: BackgroundMessageParam) => void;
@@ -117,11 +122,9 @@ export type OverlayBackgroundExtensionMessageHandlers = {
toggleAutofillInlineMenuHidden: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; toggleAutofillInlineMenuHidden: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
checkIsAutofillInlineMenuButtonVisible: ({ sender }: BackgroundSenderParam) => void; checkIsAutofillInlineMenuButtonVisible: ({ sender }: BackgroundSenderParam) => void;
checkIsAutofillInlineMenuListVisible: ({ sender }: BackgroundSenderParam) => void; checkIsAutofillInlineMenuListVisible: ({ sender }: BackgroundSenderParam) => void;
checkShouldRepositionInlineMenu: ({ sender }: BackgroundSenderParam) => boolean;
getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number; getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number;
updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
rebuildSubFrameOffsets: ({ sender }: BackgroundSenderParam) => void; triggerSubFrameFocusInRebuild: ({ sender }: BackgroundSenderParam) => void;
repositionAutofillInlineMenuForSubFrame: ({ sender }: BackgroundSenderParam) => void;
destroyAutofillInlineMenuListeners: ({ destroyAutofillInlineMenuListeners: ({
message, message,
sender, sender,

View File

@@ -396,7 +396,7 @@ describe("OverlayBackground", () => {
jest.useFakeTimers(); jest.useFakeTimers();
overlayBackground["delayedUpdateInlineMenuPositionTimeout"] = setTimeout(jest.fn, 650); overlayBackground["delayedUpdateInlineMenuPositionTimeout"] = setTimeout(jest.fn, 650);
const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: middleFrameId }); const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: middleFrameId });
jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterSubFrameRebuild"); jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterRepositionEvent");
sendMockExtensionMessage( sendMockExtensionMessage(
{ {
@@ -409,12 +409,12 @@ describe("OverlayBackground", () => {
jest.advanceTimersByTime(650); jest.advanceTimersByTime(650);
expect( expect(
overlayBackground["updateInlineMenuPositionAfterSubFrameRebuild"], overlayBackground["updateInlineMenuPositionAfterRepositionEvent"],
).toHaveBeenCalled(); ).toHaveBeenCalled();
}); });
}); });
describe("updateInlineMenuPositionAfterSubFrameRebuild", () => { describe("updateInlineMenuPositionAfterRepositionEvent", () => {
let sender: chrome.runtime.MessageSender; let sender: chrome.runtime.MessageSender;
async function flushInlineMenuUpdatePromises() { async function flushInlineMenuUpdatePromises() {

View File

@@ -1,4 +1,5 @@
import { firstValueFrom } from "rxjs"; import { firstValueFrom, Subject } from "rxjs";
import { debounceTime, switchMap } from "rxjs/operators";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
@@ -48,6 +49,7 @@ import {
SubFrameOffsetData, SubFrameOffsetData,
SubFrameOffsetsForTab, SubFrameOffsetsForTab,
CloseInlineMenuMessage, CloseInlineMenuMessage,
ToggleInlineMenuHiddenMessage,
} from "./abstractions/overlay.background"; } from "./abstractions/overlay.background";
export class OverlayBackground implements OverlayBackgroundInterface { export class OverlayBackground implements OverlayBackgroundInterface {
@@ -65,6 +67,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private inlineMenuFadeInTimeout: number | NodeJS.Timeout; private inlineMenuFadeInTimeout: number | NodeJS.Timeout;
private delayedUpdateInlineMenuPositionTimeout: number | NodeJS.Timeout; private delayedUpdateInlineMenuPositionTimeout: number | NodeJS.Timeout;
private delayedCloseTimeout: number | NodeJS.Timeout; private delayedCloseTimeout: number | NodeJS.Timeout;
private repositionInlineMenu$ = new Subject<chrome.runtime.MessageSender>();
private rebuildSubFrameOffsets$ = new Subject<chrome.runtime.MessageSender>();
private focusedFieldData: FocusedFieldData; private focusedFieldData: FocusedFieldData;
private isFieldCurrentlyFocused: boolean = false; private isFieldCurrentlyFocused: boolean = false;
private isFieldCurrentlyFilling: boolean = false; private isFieldCurrentlyFilling: boolean = false;
@@ -73,6 +77,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
autofillOverlayElementClosed: ({ message, sender }) => autofillOverlayElementClosed: ({ message, sender }) =>
this.overlayElementClosed(message, sender), this.overlayElementClosed(message, sender),
autofillOverlayAddNewVaultItem: ({ message, sender }) => this.addNewVaultItem(message, sender), autofillOverlayAddNewVaultItem: ({ message, sender }) => this.addNewVaultItem(message, sender),
triggerAutofillOverlayReposition: ({ sender }) => this.triggerOverlayReposition(sender),
checkIsInlineMenuCiphersPopulated: ({ sender }) => checkIsInlineMenuCiphersPopulated: ({ sender }) =>
this.checkIsInlineMenuCiphersPopulated(sender), this.checkIsInlineMenuCiphersPopulated(sender),
updateFocusedFieldData: ({ message, sender }) => this.setFocusedFieldData(message, sender), updateFocusedFieldData: ({ message, sender }) => this.setFocusedFieldData(message, sender),
@@ -88,16 +93,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
updateAutofillInlineMenuPosition: ({ message, sender }) => updateAutofillInlineMenuPosition: ({ message, sender }) =>
this.updateInlineMenuPosition(message, sender), this.updateInlineMenuPosition(message, sender),
toggleAutofillInlineMenuHidden: ({ message, sender }) => toggleAutofillInlineMenuHidden: ({ message, sender }) =>
this.updateInlineMenuHidden(message, sender), this.toggleInlineMenuHidden(message, sender),
checkIsAutofillInlineMenuButtonVisible: ({ sender }) => checkIsAutofillInlineMenuButtonVisible: ({ sender }) =>
this.checkIsInlineMenuButtonVisible(sender), this.checkIsInlineMenuButtonVisible(sender),
checkIsAutofillInlineMenuListVisible: ({ sender }) => this.checkIsInlineMenuListVisible(sender), checkIsAutofillInlineMenuListVisible: ({ sender }) => this.checkIsInlineMenuListVisible(sender),
checkShouldRepositionInlineMenu: ({ sender }) => this.checkShouldRepositionInlineMenu(sender),
getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender), getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender),
updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender), updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender),
rebuildSubFrameOffsets: ({ sender }) => this.rebuildSubFrameOffsets(sender), triggerSubFrameFocusInRebuild: ({ sender }) => this.triggerSubFrameFocusInRebuild(sender),
repositionAutofillInlineMenuForSubFrame: ({ sender }) =>
this.repositionInlineMenuForSubFrame(sender),
destroyAutofillInlineMenuListeners: ({ message, sender }) => destroyAutofillInlineMenuListeners: ({ message, sender }) =>
this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId), this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId),
collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender), collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender),
@@ -138,7 +140,20 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private i18nService: I18nService, private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private themeStateService: ThemeStateService, private themeStateService: ThemeStateService,
) {} ) {
this.repositionInlineMenu$
.pipe(
debounceTime(500),
switchMap((sender) => this.repositionInlineMenu(sender)),
)
.subscribe();
this.rebuildSubFrameOffsets$
.pipe(
debounceTime(200),
switchMap((sender) => this.rebuildSubFrameOffsets(sender)),
)
.subscribe();
}
/** /**
* Sets up the extension message listeners and gets the settings for the * Sets up the extension message listeners and gets the settings for the
@@ -411,23 +426,6 @@ export class OverlayBackground implements OverlayBackgroundInterface {
} }
} }
/**
* Handles rebuilding the sub frame offsets when the tab is repositioned or scrolled.
* Will trigger a re-positioning of the inline menu list and button. Note that we
* do not trigger an update to sub frame data if the sender is the frame that has
* the field currently focused. We trigger a re-calculation of the field's position
* and as a result, the sub frame offsets of that frame will be updated.
*
* @param sender - The sender of the message
*/
private async repositionInlineMenuForSubFrame(sender: chrome.runtime.MessageSender) {
if (sender.frameId === this.focusedFieldData?.frameId) {
return;
}
await this.rebuildSubFrameOffsets(sender);
}
/** /**
* Triggers a delayed repositioning of the inline menu. Used in cases where the page in some way * Triggers a delayed repositioning of the inline menu. Used in cases where the page in some way
* is resized, scrolled, or when a sub frame is interacted with. * is resized, scrolled, or when a sub frame is interacted with.
@@ -438,7 +436,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.clearDelayedUpdateInlineMenuPositionTimeout(); this.clearDelayedUpdateInlineMenuPositionTimeout();
this.delayedUpdateInlineMenuPositionTimeout = globalThis.setTimeout(async () => { this.delayedUpdateInlineMenuPositionTimeout = globalThis.setTimeout(async () => {
this.clearDelayedUpdateInlineMenuPositionTimeout(); this.clearDelayedUpdateInlineMenuPositionTimeout();
await this.updateInlineMenuPositionAfterSubFrameRebuild(sender); await this.updateInlineMenuPositionAfterRepositionEvent(sender);
}, 650); }, 650);
} }
@@ -449,7 +447,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* *
* @param sender - The sender of the message * @param sender - The sender of the message
*/ */
private async updateInlineMenuPositionAfterSubFrameRebuild(sender: chrome.runtime.MessageSender) { private async updateInlineMenuPositionAfterRepositionEvent(sender: chrome.runtime.MessageSender) {
if (!this.isFieldCurrentlyFocused) { if (!this.isFieldCurrentlyFocused) {
return; return;
} }
@@ -791,8 +789,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* @param display - The display property of the inline menu, either "block" or "none" * @param display - The display property of the inline menu, either "block" or "none"
* @param sender - The sender of the extension message * @param sender - The sender of the extension message
*/ */
private async updateInlineMenuHidden( private async toggleInlineMenuHidden(
{ isInlineMenuHidden, setTransparentInlineMenu }: OverlayBackgroundExtensionMessage, { isInlineMenuHidden, setTransparentInlineMenu }: ToggleInlineMenuHiddenMessage,
sender: chrome.runtime.MessageSender, sender: chrome.runtime.MessageSender,
) { ) {
this.clearInlineMenuFadeInTimeout(); this.clearInlineMenuFadeInTimeout();
@@ -1122,13 +1120,15 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* *
* @param sender - The sender of the message * @param sender - The sender of the message
*/ */
private checkShouldRepositionInlineMenu(sender: chrome.runtime.MessageSender): boolean { private async checkShouldRepositionInlineMenu(
sender: chrome.runtime.MessageSender,
): Promise<boolean> {
if (!this.focusedFieldData || sender.tab.id !== this.focusedFieldData.tabId) { if (!this.focusedFieldData || sender.tab.id !== this.focusedFieldData.tabId) {
return false; return false;
} }
if (this.focusedFieldData.frameId === sender.frameId) { if (this.focusedFieldData.frameId === sender.frameId) {
return true; return await this.checkIsInlineMenuButtonVisible(sender);
} }
const subFrameOffsetsForTab = this.subFrameOffsetsForTab[sender.tab.id]; const subFrameOffsetsForTab = this.subFrameOffsetsForTab[sender.tab.id];
@@ -1347,4 +1347,47 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.inlineMenuButtonPort = null; this.inlineMenuButtonPort = null;
} }
}; };
private async triggerOverlayReposition(sender: chrome.runtime.MessageSender) {
if (await this.checkShouldRepositionInlineMenu(sender)) {
await this.toggleInlineMenuHidden({ isInlineMenuHidden: true }, sender);
this.repositionInlineMenu$.next(sender);
}
}
private repositionInlineMenu = async (sender: chrome.runtime.MessageSender) => {
if (!this.isFieldCurrentlyFocused) {
await this.closeInlineMenuAfterReposition(sender);
return;
}
const isFieldWithinViewport = await BrowserApi.tabSendMessage(
sender.tab,
{ command: "checkIsMostRecentlyFocusedFieldWithinViewport" },
{ frameId: this.focusedFieldData.frameId },
);
if (!isFieldWithinViewport) {
await this.closeInlineMenuAfterReposition(sender);
return;
}
if (this.focusedFieldData.frameId > 0 && sender.frameId !== this.focusedFieldData.frameId) {
this.rebuildSubFrameOffsets$.next(sender);
return;
}
await this.updateInlineMenuPositionAfterRepositionEvent(sender);
};
private async closeInlineMenuAfterReposition(sender: chrome.runtime.MessageSender) {
await this.toggleInlineMenuHidden(
{ isInlineMenuHidden: false, setTransparentInlineMenu: true },
sender,
);
this.closeInlineMenu(sender, { forceCloseInlineMenu: true });
}
private async triggerSubFrameFocusInRebuild(sender: chrome.runtime.MessageSender) {
this.rebuildSubFrameOffsets$.next(sender);
}
} }

View File

@@ -211,7 +211,10 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement {
if (this.cipherListScrollDebounceTimeout) { if (this.cipherListScrollDebounceTimeout) {
clearTimeout(this.cipherListScrollDebounceTimeout); clearTimeout(this.cipherListScrollDebounceTimeout);
} }
this.cipherListScrollDebounceTimeout = setTimeout(this.handleDebouncedScrollEvent, 300); this.cipherListScrollDebounceTimeout = globalThis.setTimeout(
this.handleDebouncedScrollEvent,
300,
);
}; };
/** /**

View File

@@ -22,6 +22,7 @@ export type AutofillOverlayContentExtensionMessageHandlers = {
addNewVaultItemFromOverlay: () => void; addNewVaultItemFromOverlay: () => void;
blurMostRecentlyFocusedField: () => void; blurMostRecentlyFocusedField: () => void;
unsetMostRecentlyFocusedField: () => void; unsetMostRecentlyFocusedField: () => void;
checkIsMostRecentlyFocusedFieldWithinViewport: () => Promise<boolean>;
bgUnlockPopoutOpened: () => void; bgUnlockPopoutOpened: () => void;
bgVaultItemRepromptPopoutOpened: () => void; bgVaultItemRepromptPopoutOpened: () => void;
redirectAutofillInlineMenuFocusOut: ({ message }: AutofillExtensionMessageParam) => void; redirectAutofillInlineMenuFocusOut: ({ message }: AutofillExtensionMessageParam) => void;

View File

@@ -64,6 +64,8 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
addNewVaultItemFromOverlay: () => this.addNewVaultItem(), addNewVaultItemFromOverlay: () => this.addNewVaultItem(),
blurMostRecentlyFocusedField: () => this.blurMostRecentlyFocusedField(), blurMostRecentlyFocusedField: () => this.blurMostRecentlyFocusedField(),
unsetMostRecentlyFocusedField: () => this.unsetMostRecentlyFocusedField(), unsetMostRecentlyFocusedField: () => this.unsetMostRecentlyFocusedField(),
checkIsMostRecentlyFocusedFieldWithinViewport: () =>
this.checkIsMostRecentlyFocusedFieldWithinViewport(),
bgUnlockPopoutOpened: () => this.blurMostRecentlyFocusedField(true), bgUnlockPopoutOpened: () => this.blurMostRecentlyFocusedField(true),
bgVaultItemRepromptPopoutOpened: () => this.blurMostRecentlyFocusedField(true), bgVaultItemRepromptPopoutOpened: () => this.blurMostRecentlyFocusedField(true),
redirectAutofillInlineMenuFocusOut: ({ message }) => redirectAutofillInlineMenuFocusOut: ({ message }) =>
@@ -615,7 +617,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
focusedFieldRects: { width, height, top, left }, focusedFieldRects: { width, height, top, left },
}; };
void this.sendExtensionMessage("updateFocusedFieldData", { await this.sendExtensionMessage("updateFocusedFieldData", {
focusedFieldData: this.focusedFieldData, focusedFieldData: this.focusedFieldData,
}); });
} }
@@ -1024,7 +1026,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
globalThis.addEventListener( globalThis.addEventListener(
EVENTS.SCROLL, EVENTS.SCROLL,
this.useEventHandlersMemo( this.useEventHandlersMemo(
throttle(this.handleOverlayRepositionEvent, 150), throttle(this.handleOverlayRepositionEvent, 200),
AUTOFILL_OVERLAY_ON_SCROLL, AUTOFILL_OVERLAY_ON_SCROLL,
), ),
{ {
@@ -1034,7 +1036,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
globalThis.addEventListener( globalThis.addEventListener(
EVENTS.RESIZE, EVENTS.RESIZE,
this.useEventHandlersMemo( this.useEventHandlersMemo(
throttle(this.handleOverlayRepositionEvent, 150), throttle(this.handleOverlayRepositionEvent, 200),
AUTOFILL_OVERLAY_ON_RESIZE, AUTOFILL_OVERLAY_ON_RESIZE,
), ),
); );
@@ -1066,68 +1068,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
* repositioning of existing overlay elements. * repositioning of existing overlay elements.
*/ */
private handleOverlayRepositionEvent = async () => { private handleOverlayRepositionEvent = async () => {
if (!(await this.checkShouldRepositionInlineMenu())) { await this.sendExtensionMessage("triggerAutofillOverlayReposition");
return;
}
this.repositionInlineMenuForSubFrame();
this.toggleInlineMenuHidden(true);
this.clearUserInteractionEventTimeout();
this.userInteractionEventTimeout = globalThis.setTimeout(
this.triggerOverlayRepositionUpdates,
750,
);
};
/**
* Triggers a rebuild of a sub frame's offsets within the tab.
*/
private repositionInlineMenuForSubFrame() {
this.clearRecalculateSubFrameOffsetsTimeout();
this.recalculateSubFrameOffsetsTimeout = globalThis.setTimeout(
() => void this.sendExtensionMessage("repositionAutofillInlineMenuForSubFrame"),
150,
);
}
/**
* Triggers the overlay reposition updates. This method ensures that the overlay elements
* are correctly positioned when the viewport scrolls or repositions.
*/
private triggerOverlayRepositionUpdates = async () => {
if (!this.recentlyFocusedFieldIsCurrentlyFocused()) {
this.toggleInlineMenuHidden(false, true);
void this.sendExtensionMessage("closeAutofillInlineMenu", {
forceCloseInlineMenu: true,
});
return;
}
await this.updateMostRecentlyFocusedField(this.mostRecentlyFocusedField);
this.updateInlineMenuElementsPosition();
this.clearCloseInlineMenuOnFilledFieldTimeout();
this.closeInlineMenuOnFilledFieldTimeout = globalThis.setTimeout(async () => {
this.toggleInlineMenuHidden(false, true);
if (
await this.hideInlineMenuListOnFilledField(
this.mostRecentlyFocusedField as FillableFormFieldElement,
)
) {
void this.sendExtensionMessage("closeAutofillInlineMenu", {
overlayElement: AutofillOverlayElement.List,
forceCloseInlineMenu: true,
});
}
}, 50);
this.clearUserInteractionEventTimeout();
if (this.isFocusedFieldWithinViewportBounds()) {
return;
}
void this.sendExtensionMessage("closeAutofillInlineMenu", {
forceCloseInlineMenu: true,
});
}; };
private setupRebuildSubFrameOffsetsListeners = () => { private setupRebuildSubFrameOffsetsListeners = () => {
@@ -1140,7 +1081,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
}; };
private handleSubFrameFocusInEvent = () => { private handleSubFrameFocusInEvent = () => {
this.rebuildSubFrameOffsets(); void this.sendExtensionMessage("triggerSubFrameFocusInRebuild");
globalThis.removeEventListener(EVENTS.FOCUS, this.handleSubFrameFocusInEvent); globalThis.removeEventListener(EVENTS.FOCUS, this.handleSubFrameFocusInEvent);
globalThis.document.body.removeEventListener( globalThis.document.body.removeEventListener(
@@ -1154,11 +1095,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
); );
}; };
private rebuildSubFrameOffsets = () => { private async checkIsMostRecentlyFocusedFieldWithinViewport() {
this.clearUserInteractionEventTimeout(); await this.updateMostRecentlyFocusedField(this.mostRecentlyFocusedField);
this.clearRecalculateSubFrameOffsetsTimeout();
void this.sendExtensionMessage("rebuildSubFrameOffsets"); return this.isFocusedFieldWithinViewportBounds();
}; }
/** /**
* Checks if the focused field is present within the bounds of the viewport. * Checks if the focused field is present within the bounds of the viewport.