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:
@@ -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,
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user