[PM-6921] Optimize methodology for storing page details within inline menu background processes (#8385)

* [PM-6921] Optimize methodology for storing page details within inline menu background processes

* [PM-6921] Incorporating method for ensuring that we clear the Map datastructure when the page details are being removed

* [PM-6921] Adjusting method to ensure that page details always remain up to date for when processed
This commit is contained in:
Cesar Gonzalez 2024-03-21 12:41:26 -05:00 committed by GitHub
parent 600cc080b8
commit b9f9ad029f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 76 additions and 23 deletions

View File

@ -125,7 +125,8 @@ describe("OverlayBackground", () => {
describe("removePageDetails", () => {
it("removes the page details for a specific tab from the pageDetailsForTab object", () => {
const tabId = 1;
overlayBackground["pageDetailsForTab"][tabId] = [createPageDetailMock()];
const frameId = 2;
overlayBackground["pageDetailsForTab"][tabId] = new Map([[frameId, createPageDetailMock()]]);
overlayBackground.removePageDetails(tabId);
expect(overlayBackground["pageDetailsForTab"][tabId]).toBeUndefined();
@ -864,29 +865,40 @@ describe("OverlayBackground", () => {
sender,
);
expect(overlayBackground["pageDetailsForTab"][sender.tab.id]).toStrictEqual([
{ frameId: sender.frameId, tab: sender.tab, details: pageDetails1 },
]);
expect(overlayBackground["pageDetailsForTab"][sender.tab.id]).toStrictEqual(
new Map([
[sender.frameId, { frameId: sender.frameId, tab: sender.tab, details: pageDetails1 }],
]),
);
});
it("updates the page details for a tab that already has a set of page details stored ", () => {
overlayBackground["pageDetailsForTab"][sender.tab.id] = [
{
frameId: sender.frameId,
tab: sender.tab,
details: pageDetails1,
},
];
const secondFrameSender = mock<chrome.runtime.MessageSender>({
tab: { id: 1 },
frameId: 3,
});
overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([
[sender.frameId, { frameId: sender.frameId, tab: sender.tab, details: pageDetails1 }],
]);
sendExtensionRuntimeMessage(
{ command: "collectPageDetailsResponse", details: pageDetails2 },
sender,
secondFrameSender,
);
expect(overlayBackground["pageDetailsForTab"][sender.tab.id]).toStrictEqual([
{ frameId: sender.frameId, tab: sender.tab, details: pageDetails1 },
{ frameId: sender.frameId, tab: sender.tab, details: pageDetails2 },
]);
expect(overlayBackground["pageDetailsForTab"][sender.tab.id]).toStrictEqual(
new Map([
[sender.frameId, { frameId: sender.frameId, tab: sender.tab, details: pageDetails1 }],
[
secondFrameSender.frameId,
{
frameId: secondFrameSender.frameId,
tab: secondFrameSender.tab,
details: pageDetails2,
},
],
]),
);
});
});
@ -1196,6 +1208,10 @@ describe("OverlayBackground", () => {
let getLoginCiphersSpy: jest.SpyInstance;
let isPasswordRepromptRequiredSpy: jest.SpyInstance;
let doAutoFillSpy: jest.SpyInstance;
let sender: chrome.runtime.MessageSender;
const pageDetails = createAutofillPageDetailsMock({
login: { username: "username1", password: "password1" },
});
beforeEach(() => {
getLoginCiphersSpy = jest.spyOn(overlayBackground["overlayLoginCiphers"], "get");
@ -1204,6 +1220,7 @@ describe("OverlayBackground", () => {
"isPasswordRepromptRequired",
);
doAutoFillSpy = jest.spyOn(overlayBackground["autofillService"], "doAutoFill");
sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
});
it("ignores the fill request if the overlay cipher id is not provided", async () => {
@ -1215,12 +1232,27 @@ describe("OverlayBackground", () => {
expect(doAutoFillSpy).not.toHaveBeenCalled();
});
it("ignores the fill request if the tab does not contain any identified page details", async () => {
sendPortMessage(listPortSpy, {
command: "fillSelectedListItem",
overlayCipherId: "overlay-cipher-1",
});
await flushPromises();
expect(getLoginCiphersSpy).not.toHaveBeenCalled();
expect(isPasswordRepromptRequiredSpy).not.toHaveBeenCalled();
expect(doAutoFillSpy).not.toHaveBeenCalled();
});
it("ignores the fill request if a master password reprompt is required", async () => {
const cipher = mock<CipherView>({
reprompt: CipherRepromptType.Password,
type: CipherType.Login,
});
overlayBackground["overlayLoginCiphers"] = new Map([["overlay-cipher-1", cipher]]);
overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([
[sender.frameId, { frameId: sender.frameId, tab: sender.tab, details: pageDetails }],
]);
getLoginCiphersSpy = jest.spyOn(overlayBackground["overlayLoginCiphers"], "get");
isPasswordRepromptRequiredSpy.mockResolvedValue(true);
@ -1247,6 +1279,14 @@ describe("OverlayBackground", () => {
["overlay-cipher-2", cipher2],
["overlay-cipher-3", cipher3],
]);
const pageDetailsForTab = {
frameId: sender.frameId,
tab: sender.tab,
details: pageDetails,
};
overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([
[sender.frameId, pageDetailsForTab],
]);
isPasswordRepromptRequiredSpy.mockResolvedValue(false);
sendPortMessage(listPortSpy, {
@ -1262,7 +1302,7 @@ describe("OverlayBackground", () => {
expect(doAutoFillSpy).toHaveBeenCalledWith({
tab: listPortSpy.sender.tab,
cipher: cipher2,
pageDetails: undefined,
pageDetails: [pageDetailsForTab],
fillNewPassword: true,
allowTotpAutofill: true,
});
@ -1278,6 +1318,9 @@ describe("OverlayBackground", () => {
it("copies the cipher's totp code to the clipboard after filling", async () => {
const cipher1 = mock<CipherView>({ id: "overlay-cipher-1" });
overlayBackground["overlayLoginCiphers"] = new Map([["overlay-cipher-1", cipher1]]);
overlayBackground["pageDetailsForTab"][sender.tab.id] = new Map([
[sender.frameId, { frameId: sender.frameId, tab: sender.tab, details: pageDetails }],
]);
isPasswordRepromptRequiredSpy.mockResolvedValue(false);
const copyToClipboardSpy = jest
.spyOn(overlayBackground["platformUtilsService"], "copyToClipboard")

View File

@ -47,7 +47,10 @@ class OverlayBackground implements OverlayBackgroundInterface {
private readonly openViewVaultItemPopout = openViewVaultItemPopout;
private readonly openAddEditVaultItemPopout = openAddEditVaultItemPopout;
private overlayLoginCiphers: Map<string, CipherView> = new Map();
private pageDetailsForTab: Record<number, PageDetail[]> = {};
private pageDetailsForTab: Record<
chrome.runtime.MessageSender["tab"]["id"],
Map<chrome.runtime.MessageSender["frameId"], PageDetail>
> = {};
private userAuthStatus: AuthenticationStatus = AuthenticationStatus.LoggedOut;
private overlayButtonPort: chrome.runtime.Port;
private overlayListPort: chrome.runtime.Port;
@ -107,6 +110,11 @@ class OverlayBackground implements OverlayBackgroundInterface {
* @param tabId - Used to reference the page details of a specific tab
*/
removePageDetails(tabId: number) {
if (!this.pageDetailsForTab[tabId]) {
return;
}
this.pageDetailsForTab[tabId].clear();
delete this.pageDetailsForTab[tabId];
}
@ -203,12 +211,13 @@ class OverlayBackground implements OverlayBackgroundInterface {
details: message.details,
};
if (this.pageDetailsForTab[sender.tab.id]?.length) {
this.pageDetailsForTab[sender.tab.id].push(pageDetails);
const pageDetailsMap = this.pageDetailsForTab[sender.tab.id];
if (!pageDetailsMap) {
this.pageDetailsForTab[sender.tab.id] = new Map([[sender.frameId, pageDetails]]);
return;
}
this.pageDetailsForTab[sender.tab.id] = [pageDetails];
pageDetailsMap.set(sender.frameId, pageDetails);
}
/**
@ -222,7 +231,8 @@ class OverlayBackground implements OverlayBackgroundInterface {
{ overlayCipherId }: OverlayPortMessage,
{ sender }: chrome.runtime.Port,
) {
if (!overlayCipherId) {
const pageDetails = this.pageDetailsForTab[sender.tab.id];
if (!overlayCipherId || !pageDetails?.size) {
return;
}
@ -234,7 +244,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
const totpCode = await this.autofillService.doAutoFill({
tab: sender.tab,
cipher: cipher,
pageDetails: this.pageDetailsForTab[sender.tab.id],
pageDetails: Array.from(pageDetails.values()),
fillNewPassword: true,
allowTotpAutofill: true,
});