mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-21 16:18:28 +01:00
[PM-5189] Working through content script port improvement
This commit is contained in:
parent
6802cc8957
commit
da357f46b3
@ -73,13 +73,6 @@ export type OverlayBackgroundExtensionMessage = {
|
||||
CloseInlineMenuMessage &
|
||||
ToggleInlineMenuHiddenMessage;
|
||||
|
||||
export type OverlayPortMessage = {
|
||||
[key: string]: any;
|
||||
command: string;
|
||||
direction?: string;
|
||||
inlineMenuCipherId?: string;
|
||||
};
|
||||
|
||||
export type InlineMenuCipherData = {
|
||||
id: string;
|
||||
name: string;
|
||||
@ -101,7 +94,7 @@ export type BackgroundOnMessageHandlerParams = BackgroundMessageParam & Backgrou
|
||||
|
||||
export type OverlayBackgroundExtensionMessageHandlers = {
|
||||
[key: string]: CallableFunction;
|
||||
autofillOverlayElementClosed: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
|
||||
|
||||
autofillOverlayAddNewVaultItem: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
|
||||
triggerAutofillOverlayReposition: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
|
||||
checkIsInlineMenuCiphersPopulated: ({ sender }: BackgroundSenderParam) => void;
|
||||
@ -137,6 +130,11 @@ export type OverlayBackgroundExtensionMessageHandlers = {
|
||||
deletedCipher: () => void;
|
||||
};
|
||||
|
||||
export type OverlayPortMessage = OverlayBackgroundExtensionMessage & {
|
||||
direction?: string;
|
||||
inlineMenuCipherId?: string;
|
||||
};
|
||||
|
||||
export type PortMessageParam = {
|
||||
message: OverlayPortMessage;
|
||||
};
|
||||
@ -145,6 +143,11 @@ export type PortConnectionParam = {
|
||||
};
|
||||
export type PortOnMessageHandlerParams = PortMessageParam & PortConnectionParam;
|
||||
|
||||
export type OverlayContentScriptPortMessageHandlers = {
|
||||
[key: string]: CallableFunction;
|
||||
autofillOverlayElementClosed: ({ message, port }: PortOnMessageHandlerParams) => void;
|
||||
};
|
||||
|
||||
export type InlineMenuButtonPortMessageHandlers = {
|
||||
[key: string]: CallableFunction;
|
||||
triggerDelayedAutofillInlineMenuClosure: ({ port }: PortConnectionParam) => void;
|
||||
|
@ -50,6 +50,7 @@ import {
|
||||
SubFrameOffsetsForTab,
|
||||
CloseInlineMenuMessage,
|
||||
ToggleInlineMenuHiddenMessage,
|
||||
OverlayContentScriptPortMessageHandlers,
|
||||
} from "./abstractions/overlay.background";
|
||||
|
||||
export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
@ -75,8 +76,6 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
private isFieldCurrentlyFilling: boolean = false;
|
||||
private iconsServerUrl: string;
|
||||
private readonly extensionMessageHandlers: OverlayBackgroundExtensionMessageHandlers = {
|
||||
autofillOverlayElementClosed: ({ message, sender }) =>
|
||||
this.overlayElementClosed(message, sender),
|
||||
autofillOverlayAddNewVaultItem: ({ message, sender }) => this.addNewVaultItem(message, sender),
|
||||
triggerAutofillOverlayReposition: ({ sender }) => this.triggerOverlayReposition(sender),
|
||||
checkIsInlineMenuCiphersPopulated: ({ sender }) =>
|
||||
@ -110,6 +109,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
editedCipher: () => this.updateInlineMenuCiphers(),
|
||||
deletedCipher: () => this.updateInlineMenuCiphers(),
|
||||
};
|
||||
private readonly contentScriptPortMessageHandlers: OverlayContentScriptPortMessageHandlers = {
|
||||
autofillOverlayElementClosed: ({ message, port }) => this.overlayElementClosed(message, port),
|
||||
};
|
||||
private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = {
|
||||
triggerDelayedAutofillInlineMenuClosure: ({ port }) => this.triggerDelayedInlineMenuClosure(),
|
||||
autofillInlineMenuButtonClicked: ({ port }) => this.handleInlineMenuButtonClicked(port),
|
||||
@ -612,13 +614,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
* the list and button ports and sets them to null.
|
||||
*
|
||||
* @param overlayElement - The overlay element that was closed, either the list or button
|
||||
* @param sender - The sender of the port message
|
||||
* @param port - The port that sent the message
|
||||
*/
|
||||
private overlayElementClosed(
|
||||
{ overlayElement }: OverlayBackgroundExtensionMessage,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
port: chrome.runtime.Port,
|
||||
) {
|
||||
if (sender.tab.id !== this.focusedFieldData?.tabId) {
|
||||
if (port.sender.tab.id !== this.focusedFieldData?.tabId) {
|
||||
this.expiredPorts.forEach((port) => port.disconnect());
|
||||
this.expiredPorts = [];
|
||||
return;
|
||||
@ -1217,6 +1219,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
* @param port - The port that connected to the extension background
|
||||
*/
|
||||
private handlePortOnConnect = async (port: chrome.runtime.Port) => {
|
||||
if (port.name === AutofillOverlayPort.ContentScript) {
|
||||
port.onMessage.addListener(this.handleContentScriptPortMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const isInlineMenuListMessageConnector = port.name === AutofillOverlayPort.ListMessageConnector;
|
||||
const isInlineMenuButtonMessageConnector =
|
||||
port.name === AutofillOverlayPort.ButtonMessageConnector;
|
||||
@ -1293,6 +1300,21 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private handleContentScriptPortMessage = (
|
||||
message: OverlayPortMessage,
|
||||
port: chrome.runtime.Port,
|
||||
) => {
|
||||
if (port.name !== AutofillOverlayPort.ContentScript) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handler: CallableFunction | undefined =
|
||||
this.contentScriptPortMessageHandlers[message.command];
|
||||
if (handler) {
|
||||
handler({ message, port });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles messages sent to the overlay list or button ports.
|
||||
*
|
||||
@ -1300,7 +1322,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
* @param port - The port that sent the message
|
||||
*/
|
||||
private handleOverlayElementPortMessage = (
|
||||
message: OverlayBackgroundExtensionMessage,
|
||||
message: OverlayPortMessage,
|
||||
port: chrome.runtime.Port,
|
||||
) => {
|
||||
const tabPortKey = this.portKeyForTab[port.sender.tab.id];
|
||||
|
@ -33,14 +33,14 @@ class AutofillInit implements AutofillInitInterface {
|
||||
* CollectAutofillContentService and InsertAutofillContentService classes.
|
||||
*
|
||||
* @param autofillOverlayContentService - The autofill overlay content service, potentially undefined.
|
||||
* @param inlineMenuElements - The inline menu elements, potentially undefined.
|
||||
* @param autofillInlineMenuContentService - The inline menu elements, potentially undefined.
|
||||
*/
|
||||
constructor(
|
||||
autofillOverlayContentService?: AutofillOverlayContentService,
|
||||
inlineMenuElements?: AutofillInlineMenuContentService,
|
||||
autofillInlineMenuContentService?: AutofillInlineMenuContentService,
|
||||
) {
|
||||
this.autofillOverlayContentService = autofillOverlayContentService;
|
||||
this.autofillInlineMenuContentService = inlineMenuElements;
|
||||
this.autofillInlineMenuContentService = autofillInlineMenuContentService;
|
||||
this.domElementVisibilityService = new DomElementVisibilityService(
|
||||
this.autofillInlineMenuContentService,
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { AutofillOverlayPort } from "../enums/autofill-overlay.enum";
|
||||
import { AutofillInlineMenuContentService } from "../overlay/inline-menu/content/autofill-inline-menu-content.service";
|
||||
import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service";
|
||||
import { InlineMenuFieldQualificationService } from "../services/inline-menu-field-qualification.service";
|
||||
@ -7,17 +8,19 @@ import AutofillInit from "./autofill-init";
|
||||
|
||||
(function (windowContext) {
|
||||
if (!windowContext.bitwardenAutofillInit) {
|
||||
const overlayPort = chrome.runtime.connect({ name: AutofillOverlayPort.ContentScript });
|
||||
const inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
overlayPort,
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
let inlineMenuElements: AutofillInlineMenuContentService;
|
||||
let autofillInlineMenuContentService: AutofillInlineMenuContentService;
|
||||
if (globalThis.self === globalThis.top) {
|
||||
inlineMenuElements = new AutofillInlineMenuContentService();
|
||||
autofillInlineMenuContentService = new AutofillInlineMenuContentService(overlayPort);
|
||||
}
|
||||
windowContext.bitwardenAutofillInit = new AutofillInit(
|
||||
autofillOverlayContentService,
|
||||
inlineMenuElements,
|
||||
autofillInlineMenuContentService,
|
||||
);
|
||||
setupAutofillInitDisconnectAction(windowContext);
|
||||
|
||||
|
@ -4,6 +4,7 @@ export const AutofillOverlayElement = {
|
||||
} as const;
|
||||
|
||||
export const AutofillOverlayPort = {
|
||||
ContentScript: "autofill-overlay-content-script-port",
|
||||
Button: "autofill-inline-menu-button-port",
|
||||
ButtonMessageConnector: "autofill-inline-menu-button-message-connector",
|
||||
List: "autofill-inline-menu-list-port",
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import AutofillInit from "../../../content/autofill-init";
|
||||
import { AutofillOverlayElement } from "../../../enums/autofill-overlay.enum";
|
||||
import { createMutationRecordMock } from "../../../spec/autofill-mocks";
|
||||
import { AutofillOverlayElement, AutofillOverlayPort } from "../../../enums/autofill-overlay.enum";
|
||||
import { createMutationRecordMock, createPortSpyMock } from "../../../spec/autofill-mocks";
|
||||
import { flushPromises, sendMockExtensionMessage } from "../../../spec/testing-utils";
|
||||
import { ElementWithOpId } from "../../../types";
|
||||
|
||||
import { AutofillInlineMenuContentService } from "./autofill-inline-menu-content.service";
|
||||
|
||||
describe("AutofillInlineMenuContentService", () => {
|
||||
let overlayPort: chrome.runtime.Port;
|
||||
let autofillInlineMenuContentService: AutofillInlineMenuContentService;
|
||||
let autofillInit: AutofillInit;
|
||||
let sendExtensionMessageSpy: jest.SpyInstance;
|
||||
@ -17,7 +18,8 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.document.body.innerHTML = "";
|
||||
autofillInlineMenuContentService = new AutofillInlineMenuContentService();
|
||||
overlayPort = createPortSpyMock(AutofillOverlayPort.ContentScript);
|
||||
autofillInlineMenuContentService = new AutofillInlineMenuContentService(overlayPort);
|
||||
autofillInit = new AutofillInit(null, autofillInlineMenuContentService);
|
||||
autofillInit.init();
|
||||
observeBodyMutationsSpy = jest.spyOn(
|
||||
|
@ -41,7 +41,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
checkIsAutofillInlineMenuListVisible: () => this.isInlineMenuListVisible(),
|
||||
};
|
||||
|
||||
constructor() {
|
||||
constructor(private port: chrome.runtime.Port) {
|
||||
this.setupMutationObserver();
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
if (this.buttonElement) {
|
||||
this.buttonElement.remove();
|
||||
this.isButtonVisible = false;
|
||||
void this.sendExtensionMessage("autofillOverlayElementClosed", {
|
||||
this.sendPortMessage("autofillOverlayElementClosed", {
|
||||
overlayElement: AutofillOverlayElement.Button,
|
||||
});
|
||||
}
|
||||
@ -128,7 +128,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
if (this.listElement) {
|
||||
this.listElement.remove();
|
||||
this.isListVisible = false;
|
||||
void this.sendExtensionMessage("autofillOverlayElementClosed", {
|
||||
this.sendPortMessage("autofillOverlayElementClosed", {
|
||||
overlayElement: AutofillOverlayElement.List,
|
||||
});
|
||||
}
|
||||
@ -421,6 +421,16 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message through the port to the background script.
|
||||
*
|
||||
* @param command - The command to send through the port.
|
||||
* @param message - The message to send through the port.
|
||||
*/
|
||||
private sendPortMessage(command: string, message: Omit<AutofillExtensionMessage, "command">) {
|
||||
this.port.postMessage({ command, ...message });
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the mutation observers and removes the inline menu elements from the DOM.
|
||||
*/
|
||||
|
@ -6,13 +6,14 @@ import { AutofillOverlayVisibility, EVENTS } from "@bitwarden/common/autofill/co
|
||||
import AutofillInit from "../content/autofill-init";
|
||||
import {
|
||||
AutofillOverlayElement,
|
||||
AutofillOverlayPort,
|
||||
MAX_SUB_FRAME_DEPTH,
|
||||
RedirectFocusDirection,
|
||||
} from "../enums/autofill-overlay.enum";
|
||||
import AutofillField from "../models/autofill-field";
|
||||
import AutofillForm from "../models/autofill-form";
|
||||
import AutofillPageDetails from "../models/autofill-page-details";
|
||||
import { createAutofillFieldMock } from "../spec/autofill-mocks";
|
||||
import { createAutofillFieldMock, createPortSpyMock } from "../spec/autofill-mocks";
|
||||
import { flushPromises, postWindowMessage, sendMockExtensionMessage } from "../spec/testing-utils";
|
||||
import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types";
|
||||
|
||||
@ -25,6 +26,7 @@ const defaultDocumentVisibilityState = document.visibilityState;
|
||||
describe("AutofillOverlayContentService", () => {
|
||||
let autofillInit: AutofillInit;
|
||||
let inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
|
||||
let overlayPort: chrome.runtime.Port;
|
||||
let autofillOverlayContentService: AutofillOverlayContentService;
|
||||
let sendExtensionMessageSpy: jest.SpyInstance;
|
||||
const sendResponseSpy = jest.fn();
|
||||
@ -39,8 +41,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
overlayPort = createPortSpyMock(AutofillOverlayPort.ContentScript);
|
||||
inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
||||
autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
overlayPort,
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
autofillInit = new AutofillInit(autofillOverlayContentService);
|
||||
|
@ -79,7 +79,10 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
destroyAutofillInlineMenuListeners: () => this.destroy(),
|
||||
};
|
||||
|
||||
constructor(private inlineMenuFieldQualificationService: InlineMenuFieldQualificationService) {}
|
||||
constructor(
|
||||
private port: chrome.runtime.Port,
|
||||
private inlineMenuFieldQualificationService: InlineMenuFieldQualificationService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initializes the autofill overlay content service by setting up the mutation observers.
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { AutofillOverlayPort } from "../enums/autofill-overlay.enum";
|
||||
import AutofillField from "../models/autofill-field";
|
||||
import AutofillForm from "../models/autofill-form";
|
||||
import { createAutofillFieldMock, createAutofillFormMock } from "../spec/autofill-mocks";
|
||||
import {
|
||||
createAutofillFieldMock,
|
||||
createAutofillFormMock,
|
||||
createPortSpyMock,
|
||||
} from "../spec/autofill-mocks";
|
||||
import { mockQuerySelectorAllDefinedCall } from "../spec/testing-utils";
|
||||
import {
|
||||
ElementWithOpId,
|
||||
@ -28,9 +33,11 @@ const mockLoginForm = `
|
||||
const waitForIdleCallback = () => new Promise((resolve) => globalThis.requestIdleCallback(resolve));
|
||||
|
||||
describe("CollectAutofillContentService", () => {
|
||||
const overlayPort = createPortSpyMock(AutofillOverlayPort.ContentScript);
|
||||
const domElementVisibilityService = new DomElementVisibilityService();
|
||||
const inlineMenuFieldQualificationService = mock<InlineMenuFieldQualificationService>();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
overlayPort,
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
let collectAutofillContentService: CollectAutofillContentService;
|
||||
|
@ -2,7 +2,9 @@ import { mock } from "jest-mock-extended";
|
||||
|
||||
import { EVENTS } from "@bitwarden/common/autofill/constants";
|
||||
|
||||
import { AutofillOverlayPort } from "../enums/autofill-overlay.enum";
|
||||
import AutofillScript, { FillScript, FillScriptActions } from "../models/autofill-script";
|
||||
import { createPortSpyMock } from "../spec/autofill-mocks";
|
||||
import { mockQuerySelectorAllDefinedCall } from "../spec/testing-utils";
|
||||
import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types";
|
||||
|
||||
@ -67,9 +69,11 @@ function setMockWindowLocation({
|
||||
}
|
||||
|
||||
describe("InsertAutofillContentService", () => {
|
||||
const overlayPort = createPortSpyMock(AutofillOverlayPort.ContentScript);
|
||||
const inlineMenuFieldQualificationService = mock<InlineMenuFieldQualificationService>();
|
||||
const domElementVisibilityService = new DomElementVisibilityService();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
overlayPort,
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
const collectAutofillContentService = new CollectAutofillContentService(
|
||||
|
Loading…
Reference in New Issue
Block a user