1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

[PM-5189] Separating the inline menu UI elements from the base AutofillOverlayContentService and setting up messaging to allow for propagation of those elements

This commit is contained in:
Cesar Gonzalez
2024-03-20 13:58:39 -05:00
parent 0af95bb2be
commit 94271201a2
9 changed files with 112 additions and 49 deletions

View File

@@ -48,6 +48,8 @@ type OverlayBackgroundExtensionMessage = {
forceCloseOverlay?: boolean; forceCloseOverlay?: boolean;
isOverlayHidden?: boolean; isOverlayHidden?: boolean;
data?: LockedVaultPendingNotificationsData; data?: LockedVaultPendingNotificationsData;
isFieldCurrentlyFocused?: boolean;
isCurrentlyFilling?: boolean;
} & OverlayAddNewItemMessage; } & OverlayAddNewItemMessage;
type OverlayPortMessage = { type OverlayPortMessage = {
@@ -102,6 +104,12 @@ type OverlayBackgroundExtensionMessageHandlers = {
unlockCompleted: ({ message }: BackgroundMessageParam) => void; unlockCompleted: ({ message }: BackgroundMessageParam) => void;
addEditCipherSubmitted: () => void; addEditCipherSubmitted: () => void;
deletedCipher: () => void; deletedCipher: () => void;
checkIsFieldCurrentlyFocused: () => boolean;
checkIsFieldCurrentlyFilling: () => boolean;
updateIsFieldCurrentlyFocused: ({ message }: BackgroundMessageParam) => void;
updateIsFieldCurrentlyFilling: ({ message }: BackgroundMessageParam) => void;
checkIsInlineMenuButtonVisible: ({ sender }: BackgroundSenderParam) => void;
checkIsInlineMenuListVisible: ({ sender }: BackgroundSenderParam) => void;
}; };
type PortMessageParam = { type PortMessageParam = {

View File

@@ -76,6 +76,14 @@ class OverlayBackground implements OverlayBackgroundInterface {
unlockCompleted: ({ message }) => this.unlockCompleted(message), unlockCompleted: ({ message }) => this.unlockCompleted(message),
addEditCipherSubmitted: () => this.updateOverlayCiphers(), addEditCipherSubmitted: () => this.updateOverlayCiphers(),
deletedCipher: () => this.updateOverlayCiphers(), deletedCipher: () => this.updateOverlayCiphers(),
checkIsFieldCurrentlyFocused: () => this.isFieldCurrentlyFocused,
checkIsFieldCurrentlyFilling: () => this.isCurrentlyFilling,
updateIsFieldCurrentlyFocused: ({ message }) =>
(this.isFieldCurrentlyFocused = message.isFieldCurrentlyFocused),
updateIsFieldCurrentlyFilling: ({ message }) =>
(this.isCurrentlyFilling = message.isFieldCurrentlyFilling),
checkIsInlineMenuButtonVisible: ({ sender }) => this.checkIsInlineMenuButtonVisible(sender),
checkIsInlineMenuListVisible: ({ sender }) => this.checkIsInlineMenuListVisible(sender),
}; };
private readonly overlayButtonPortMessageHandlers: OverlayButtonPortMessageHandlers = { private readonly overlayButtonPortMessageHandlers: OverlayButtonPortMessageHandlers = {
overlayButtonClicked: ({ port }) => this.handleOverlayButtonClicked(port), overlayButtonClicked: ({ port }) => this.handleOverlayButtonClicked(port),
@@ -112,6 +120,23 @@ class OverlayBackground implements OverlayBackgroundInterface {
this.iconsServerUrl = this.environmentService.getIconsUrl(); this.iconsServerUrl = this.environmentService.getIconsUrl();
} }
private async checkIsInlineMenuButtonVisible(sender: chrome.runtime.MessageSender) {
const value = await BrowserApi.tabSendMessage(
sender.tab,
{ command: "checkIsInlineMenuButtonVisible" },
{ frameId: 0 },
);
return value;
}
private async checkIsInlineMenuListVisible(sender: chrome.runtime.MessageSender) {
return await BrowserApi.tabSendMessage(
sender.tab,
{ command: "checkIsInlineMenuListVisible" },
{ frameId: 0 },
);
}
/** /**
* Removes cached page details for a tab * Removes cached page details for a tab
* based on the passed tabId. * based on the passed tabId.
@@ -414,7 +439,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
await BrowserApi.tabSendMessage( await BrowserApi.tabSendMessage(
sender.tab, sender.tab,
{ command: "updateInlineMenuElementsPosition" }, { command: "updateInlineMenuElementsPosition", overlayElement },
{ frameId: 0 }, { frameId: 0 },
); );
@@ -827,14 +852,14 @@ class OverlayBackground implements OverlayBackgroundInterface {
translations: this.getTranslations(), translations: this.getTranslations(),
ciphers: isOverlayListPort ? await this.getOverlayCipherData() : null, ciphers: isOverlayListPort ? await this.getOverlayCipherData() : null,
}); });
void this.updateOverlayPosition( // void this.updateOverlayPosition(
{ // {
overlayElement: isOverlayListPort // overlayElement: isOverlayListPort
? AutofillOverlayElement.List // ? AutofillOverlayElement.List
: AutofillOverlayElement.Button, // : AutofillOverlayElement.Button,
}, // },
port.sender, // port.sender,
); // );
}; };
/** /**

View File

@@ -13,6 +13,7 @@ export type AutofillExtensionMessage = {
pageDetailsUrl?: string; pageDetailsUrl?: string;
ciphers?: any; ciphers?: any;
isInlineMenuHidden?: boolean; isInlineMenuHidden?: boolean;
overlayElement?: string;
data?: { data?: {
authStatus?: AuthenticationStatus; authStatus?: AuthenticationStatus;
isFocusingFieldElement?: boolean; isFocusingFieldElement?: boolean;

View File

@@ -143,27 +143,18 @@ class AutofillInit implements AutofillInitInterface {
} }
this.blurAndRemoveOverlay(); this.blurAndRemoveOverlay();
this.updateOverlayIsCurrentlyFilling(true); await sendExtensionMessage("updateIsFieldCurrentlyFilling", { isFieldCurrentlyFilling: true });
await this.insertAutofillContentService.fillForm(fillScript); await this.insertAutofillContentService.fillForm(fillScript);
if (!this.autofillOverlayContentService) { if (!this.autofillOverlayContentService) {
return; return;
} }
setTimeout(() => this.updateOverlayIsCurrentlyFilling(false), 250); setTimeout(
} () =>
sendExtensionMessage("updateIsFieldCurrentlyFilling", { isFieldCurrentlyFilling: false }),
/** 250,
* Handles updating the overlay is currently filling value. );
*
* @param isCurrentlyFilling - Indicates if the overlay is currently filling
*/
private updateOverlayIsCurrentlyFilling(isCurrentlyFilling: boolean) {
if (!this.autofillOverlayContentService) {
return;
}
this.autofillOverlayContentService.isCurrentlyFilling = isCurrentlyFilling;
} }
/** /**

View File

@@ -3,8 +3,10 @@ import { AutofillExtensionMessageParam } from "../../content/abstractions/autofi
export type InlineMenuExtensionMessageHandlers = { export type InlineMenuExtensionMessageHandlers = {
[key: string]: CallableFunction; [key: string]: CallableFunction;
closeInlineMenu: ({ message }: AutofillExtensionMessageParam) => void; closeInlineMenu: ({ message }: AutofillExtensionMessageParam) => void;
updateInlineMenuElementsPosition: () => Promise<[void, void]>; updateInlineMenuElementsPosition: ({ message }: AutofillExtensionMessageParam) => Promise<void>;
toggleInlineMenuHidden: ({ message }: AutofillExtensionMessageParam) => void; toggleInlineMenuHidden: ({ message }: AutofillExtensionMessageParam) => void;
checkIsInlineMenuButtonVisible: () => boolean;
checkIsInlineMenuListVisible: () => boolean;
}; };
export interface InlineMenuElements { export interface InlineMenuElements {

View File

@@ -1,3 +1,4 @@
import { AutofillExtensionMessage } from "../../content/abstractions/autofill-init";
import { import {
sendExtensionMessage, sendExtensionMessage,
generateRandomCustomElementName, generateRandomCustomElementName,
@@ -35,9 +36,12 @@ export class InlineMenuElements implements InlineMenuElementsInterface {
}; };
private readonly _extensionMessageHandlers: InlineMenuExtensionMessageHandlers = { private readonly _extensionMessageHandlers: InlineMenuExtensionMessageHandlers = {
closeInlineMenu: ({ message }) => this.removeInlineMenu(), closeInlineMenu: ({ message }) => this.removeInlineMenu(),
updateInlineMenuElementsPosition: () => this.updateInlineMenuElementsPosition(), updateInlineMenuElementsPosition: ({ message }) =>
this.updateInlineMenuElementsPosition(message),
toggleInlineMenuHidden: ({ message }) => toggleInlineMenuHidden: ({ message }) =>
this.toggleInlineMenuHidden(message.isInlineMenuHidden), this.toggleInlineMenuHidden(message.isInlineMenuHidden),
checkIsInlineMenuButtonVisible: () => this.isButtonVisible,
checkIsInlineMenuListVisible: () => this.isListVisible,
}; };
constructor() { constructor() {
@@ -107,8 +111,12 @@ export class InlineMenuElements implements InlineMenuElementsInterface {
/** /**
* Updates the position of both the overlay button and overlay list. * Updates the position of both the overlay button and overlay list.
*/ */
private async updateInlineMenuElementsPosition() { private async updateInlineMenuElementsPosition({ overlayElement }: AutofillExtensionMessage) {
return Promise.all([this.updateButtonPosition(), this.updateListPosition()]); if (overlayElement === AutofillOverlayElement.Button) {
return this.updateButtonPosition();
}
return this.updateListPosition();
} }
/** /**

View File

@@ -245,7 +245,7 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf
} }
this.updateElementStyles(this.iframe, position); this.updateElementStyles(this.iframe, position);
setTimeout(() => this.updateElementStyles(this.iframe, { opacity: "1" }), 75); setTimeout(() => this.updateElementStyles(this.iframe, { opacity: "1" }), 0);
this.announceAriaAlert(); this.announceAriaAlert();
} }

View File

@@ -15,8 +15,8 @@ export type AutofillOverlayContentExtensionMessageHandlers = {
}; };
export interface AutofillOverlayContentService { export interface AutofillOverlayContentService {
isFieldCurrentlyFocused: boolean; // isFieldCurrentlyFocused: boolean;
isCurrentlyFilling: boolean; // isCurrentlyFilling: boolean;
isOverlayCiphersPopulated: boolean; isOverlayCiphersPopulated: boolean;
pageDetailsUpdateRequired: boolean; pageDetailsUpdateRequired: boolean;
autofillOverlayVisibility: number; autofillOverlayVisibility: number;

View File

@@ -26,8 +26,8 @@ import {
import { AutoFillConstants } from "./autofill-constants"; import { AutoFillConstants } from "./autofill-constants";
class AutofillOverlayContentService implements AutofillOverlayContentServiceInterface { class AutofillOverlayContentService implements AutofillOverlayContentServiceInterface {
isFieldCurrentlyFocused = false; // isFieldCurrentlyFocused = false;
isCurrentlyFilling = false; // isCurrentlyFilling = false;
isOverlayCiphersPopulated = false; isOverlayCiphersPopulated = false;
pageDetailsUpdateRequired = false; pageDetailsUpdateRequired = false;
autofillOverlayVisibility: number; autofillOverlayVisibility: number;
@@ -42,8 +42,8 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
private userFilledFields: Record<string, FillableFormFieldElement> = {}; private userFilledFields: Record<string, FillableFormFieldElement> = {};
private authStatus: AuthenticationStatus; private authStatus: AuthenticationStatus;
private focusableElements: FocusableElement[] = []; private focusableElements: FocusableElement[] = [];
private isOverlayButtonVisible = false; // private isOverlayButtonVisible = false;
private isOverlayListVisible = false; // private isOverlayListVisible = false;
// private overlayButtonElement: HTMLElement; // private overlayButtonElement: HTMLElement;
// private overlayListElement: HTMLElement; // private overlayListElement: HTMLElement;
private mostRecentlyFocusedField: ElementWithOpId<FormFieldElement>; private mostRecentlyFocusedField: ElementWithOpId<FormFieldElement>;
@@ -217,8 +217,9 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* Formats any found user filled fields for a login cipher and sends a message * Formats any found user filled fields for a login cipher and sends a message
* to the background script to add a new cipher. * to the background script to add a new cipher.
*/ */
addNewVaultItem() { async addNewVaultItem() {
if (!this.isOverlayListVisible) { // if (!this.isOverlayListVisible) {
if ((await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true) {
return; return;
} }
@@ -239,8 +240,12 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* *
* @param direction - The direction to redirect the focus. * @param direction - The direction to redirect the focus.
*/ */
redirectOverlayFocusOut(direction: string) { async redirectOverlayFocusOut(direction: string) {
if (!this.isOverlayListVisible || !this.mostRecentlyFocusedField) { // if (!this.isOverlayListVisible || !this.mostRecentlyFocusedField) {
if (
!this.mostRecentlyFocusedField ||
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true
) {
return; return;
} }
@@ -340,7 +345,10 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* is currently focused. * is currently focused.
*/ */
private handleFormFieldBlurEvent = () => { private handleFormFieldBlurEvent = () => {
this.isFieldCurrentlyFocused = false; // this.isFieldCurrentlyFocused = false;
void this.sendExtensionMessage("updateIsFieldCurrentlyFocused", {
isFieldCurrentlyFocused: false,
});
void this.sendExtensionMessage("checkAutofillOverlayFocused"); void this.sendExtensionMessage("checkAutofillOverlayFocused");
}; };
@@ -352,7 +360,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* *
* @param event - The keyup event. * @param event - The keyup event.
*/ */
private handleFormFieldKeyupEvent = (event: KeyboardEvent) => { private handleFormFieldKeyupEvent = async (event: KeyboardEvent) => {
const eventCode = event.code; const eventCode = event.code;
if (eventCode === "Escape") { if (eventCode === "Escape") {
// this.removeAutofillOverlay(); // this.removeAutofillOverlay();
@@ -360,8 +368,12 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
return; return;
} }
if (eventCode === "Enter" && !this.isCurrentlyFilling) { // if (eventCode === "Enter" && !this.isCurrentlyFilling) {
this.handleOverlayRepositionEvent(); if (
eventCode === "Enter" &&
!(await this.sendExtensionMessage("checkIsFieldCurrentlyFilling")) === true
) {
void this.handleOverlayRepositionEvent();
return; return;
} }
@@ -381,7 +393,11 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* that the overlay list is focused when the user presses the down arrow key. * that the overlay list is focused when the user presses the down arrow key.
*/ */
private async focusOverlayList() { private async focusOverlayList() {
if (!this.isOverlayListVisible && this.mostRecentlyFocusedField) { // if (!this.isOverlayListVisible && this.mostRecentlyFocusedField) {
if (
this.mostRecentlyFocusedField &&
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true
) {
await this.updateMostRecentlyFocusedField(this.mostRecentlyFocusedField); await this.updateMostRecentlyFocusedField(this.mostRecentlyFocusedField);
this.openAutofillOverlay({ isOpeningFullOverlay: true }); this.openAutofillOverlay({ isOpeningFullOverlay: true });
setTimeout(() => this.sendExtensionMessage("focusAutofillOverlayList"), 125); setTimeout(() => this.sendExtensionMessage("focusAutofillOverlayList"), 125);
@@ -470,7 +486,11 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* @param formFieldElement - The form field element that triggered the click event. * @param formFieldElement - The form field element that triggered the click event.
*/ */
private async triggerFormFieldClickedAction(formFieldElement: ElementWithOpId<FormFieldElement>) { private async triggerFormFieldClickedAction(formFieldElement: ElementWithOpId<FormFieldElement>) {
if (this.isOverlayButtonVisible || this.isOverlayListVisible) { // if (this.isOverlayButtonVisible || this.isOverlayListVisible) {
if (
(await this.sendExtensionMessage("checkIsInlineMenuButtonVisible")) === true ||
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) === true
) {
return; return;
} }
@@ -497,11 +517,15 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* @param formFieldElement - The form field element that triggered the focus event. * @param formFieldElement - The form field element that triggered the focus event.
*/ */
private async triggerFormFieldFocusedAction(formFieldElement: ElementWithOpId<FormFieldElement>) { private async triggerFormFieldFocusedAction(formFieldElement: ElementWithOpId<FormFieldElement>) {
if (this.isCurrentlyFilling) { // if (this.isCurrentlyFilling) {
if ((await this.sendExtensionMessage("checkIsFieldCurrentlyFilling")) === true) {
return; return;
} }
this.isFieldCurrentlyFocused = true; // this.isFieldCurrentlyFocused = true;
void this.sendExtensionMessage("updateIsFieldCurrentlyFocused", {
isFieldCurrentlyFocused: true,
});
this.clearUserInteractionEventTimeout(); this.clearUserInteractionEventTimeout();
const initiallyFocusedField = this.mostRecentlyFocusedField; const initiallyFocusedField = this.mostRecentlyFocusedField;
await this.updateMostRecentlyFocusedField(formFieldElement); await this.updateMostRecentlyFocusedField(formFieldElement);
@@ -848,8 +872,12 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* Handles the resize or scroll events that enact * Handles the resize or scroll events that enact
* repositioning of the overlay. * repositioning of the overlay.
*/ */
private handleOverlayRepositionEvent = () => { private handleOverlayRepositionEvent = async () => {
if (!this.isOverlayButtonVisible && !this.isOverlayListVisible) { // if (!this.isOverlayButtonVisible && !this.isOverlayListVisible) {
if (
(await this.sendExtensionMessage("checkIsInlineMenuButtonVisible")) !== true &&
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true
) {
return; return;
} }