Target messages to only the interested ports (#7301)

This commit is contained in:
Matt Gibson 2023-12-21 14:01:48 -05:00 committed by GitHub
parent 062a3ce2d2
commit bbdf704763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 37 deletions

View File

@ -25,20 +25,23 @@ export class BackgroundMemoryStorageService extends MemoryStorageService {
});
port.onMessage.addListener(listenerCallback);
// Initialize the new memory storage service with existing data
this.sendMessage({
this.sendMessageTo(port, {
action: "initialization",
data: Array.from(this.store.keys()),
});
});
this.updates$.subscribe((update) => {
this.sendMessage({
this.broadcastMessage({
action: "subject_update",
data: update,
});
});
}
private async onMessageFromForeground(message: MemoryStoragePortMessage) {
private async onMessageFromForeground(
message: MemoryStoragePortMessage,
port: chrome.runtime.Port,
) {
if (message.originator === "background") {
return;
}
@ -60,19 +63,26 @@ export class BackgroundMemoryStorageService extends MemoryStorageService {
break;
}
this.sendMessage({
this.sendMessageTo(port, {
id: message.id,
key: message.key,
data: JSON.stringify(result),
});
}
private async sendMessage(data: Omit<MemoryStoragePortMessage, "originator">) {
private broadcastMessage(data: Omit<MemoryStoragePortMessage, "originator">) {
this._ports.forEach((port) => {
port.postMessage({
...data,
originator: "background",
});
this.sendMessageTo(port, data);
});
}
private sendMessageTo(
port: chrome.runtime.Port,
data: Omit<MemoryStoragePortMessage, "originator">,
) {
port.postMessage({
...data,
originator: "background",
});
}
}

View File

@ -7,15 +7,14 @@ import { trackEmissions } from "@bitwarden/common/../spec/utils";
import { BackgroundMemoryStorageService } from "./background-memory-storage.service";
import { ForegroundMemoryStorageService } from "./foreground-memory-storage.service";
import { mockPort } from "./mock-port.spec-util";
import { portName } from "./port-name";
import { mockPorts } from "./mock-ports.spec-util";
describe("foreground background memory storage interaction", () => {
let foreground: ForegroundMemoryStorageService;
let background: BackgroundMemoryStorageService;
beforeEach(() => {
mockPort(portName(chrome.storage.session));
mockPorts();
background = new BackgroundMemoryStorageService();
foreground = new ForegroundMemoryStorageService();
@ -63,4 +62,16 @@ describe("foreground background memory storage interaction", () => {
expect(emissions).toEqual([{ key, updateType }]);
});
test("background should message only the requesting foreground", async () => {
const secondForeground = new ForegroundMemoryStorageService();
const secondPort = secondForeground["_port"];
const secondPost = secondPort.postMessage as jest.Mock;
secondPost.mockClear();
const key = "key";
await foreground.get(key);
expect(secondPost).not.toHaveBeenCalled();
});
});

View File

@ -1,25 +0,0 @@
import { mockDeep } from "jest-mock-extended";
/**
* Mocks a chrome.runtime.Port set up to send messages through `postMessage` to `onMessage.addListener` callbacks.
* @returns a mock chrome.runtime.Port
*/
export function mockPort(name: string) {
const port = mockDeep<chrome.runtime.Port>();
// notify listeners of a new port
(chrome.runtime.connect as jest.Mock).mockImplementation((portInfo) => {
port.name = portInfo.name;
(chrome.runtime.onConnect.addListener as jest.Mock).mock.calls.forEach(([callbackFn]) => {
callbackFn(port);
});
return port;
});
// set message broadcast
(port.postMessage as jest.Mock).mockImplementation((message) => {
(port.onMessage.addListener as jest.Mock).mock.calls.forEach(([callbackFn]) => {
callbackFn(message);
});
});
return port;
}

View File

@ -0,0 +1,29 @@
import { mockDeep } from "jest-mock-extended";
/**
* Mocks a chrome.runtime.Port set up to send messages through `postMessage` to `onMessage.addListener` callbacks.
* @param name - The name of the port.
* @param immediateOnConnectExecution - Whether to immediately execute the onConnect callbacks against the new port.
* Defaults to false. If true, the creator of the port will not have had a chance to set up listeners yet.
* @returns a mock chrome.runtime.Port
*/
export function mockPorts() {
// notify listeners of a new port
(chrome.runtime.connect as jest.Mock).mockImplementation((portInfo) => {
const port = mockDeep<chrome.runtime.Port>();
port.name = portInfo.name;
// set message broadcast
(port.postMessage as jest.Mock).mockImplementation((message) => {
(port.onMessage.addListener as jest.Mock).mock.calls.forEach(([callbackFn]) => {
callbackFn(message, port);
});
});
(chrome.runtime.onConnect.addListener as jest.Mock).mock.calls.forEach(([callbackFn]) => {
callbackFn(port);
});
return port;
});
}