From 814bf9de28ea8da4ac07d21eacd0150ff521c73b Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Fri, 31 May 2024 09:56:46 -0500 Subject: [PATCH] [PM-8518] Autofill scripts do not inject into sub-frames on install --- .../services/autofill.service.spec.ts | 4 +++ .../src/autofill/services/autofill.service.ts | 3 +- apps/browser/src/manifest.v3.json | 3 +- .../src/platform/browser/browser-api.ts | 30 ++++++++++++++++--- apps/browser/test.setup.ts | 2 ++ 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts index 23f690544df..6542a29b945 100644 --- a/apps/browser/src/autofill/services/autofill.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill.service.spec.ts @@ -124,6 +124,9 @@ describe("AutofillService", () => { tab2 = createChromeTabMock({ id: 2, url: "http://some-url.com" }); tab3 = createChromeTabMock({ id: 3, url: "chrome-extension://some-extension-route" }); jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValueOnce([tab1, tab2]); + jest + .spyOn(BrowserApi, "getAllFrameDetails") + .mockResolvedValue([mock({ frameId: 0 })]); jest .spyOn(autofillService, "getOverlayVisibility") .mockResolvedValue(AutofillOverlayVisibility.OnFieldFocus); @@ -134,6 +137,7 @@ describe("AutofillService", () => { jest.spyOn(autofillService, "injectAutofillScripts"); await autofillService.loadAutofillScriptsOnInstall(); + await flushPromises(); expect(BrowserApi.tabsQuery).toHaveBeenCalledWith({}); expect(autofillService.injectAutofillScripts).toHaveBeenCalledWith(tab1, 0, false); diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 9ec2052381a..27b8e1fd531 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -2094,7 +2094,8 @@ export default class AutofillService implements AutofillServiceInterface { for (let index = 0; index < tabs.length; index++) { const tab = tabs[index]; if (tab.url?.startsWith("http")) { - void this.injectAutofillScripts(tab, 0, false); + const frames = await BrowserApi.getAllFrameDetails(tab.id); + frames.forEach((frame) => this.injectAutofillScripts(tab, frame.frameId, false)); } } } diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 952396758df..53e0b6e3f3f 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -62,7 +62,8 @@ "scripting", "offscreen", "webRequest", - "webRequestAuthProvider" + "webRequestAuthProvider", + "webNavigation" ], "optional_permissions": ["nativeMessaging", "privacy"], "host_permissions": ["https://*/*", "http://*/*"], diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index d0695d53fd1..674f785eb28 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -180,17 +180,17 @@ export class BrowserApi { tab: chrome.tabs.Tab, obj: T, options: chrome.tabs.MessageSendOptions = null, - ): Promise { + ): Promise { if (!tab || !tab.id) { return; } - return new Promise((resolve) => { - chrome.tabs.sendMessage(tab.id, obj, options, () => { + return new Promise((resolve) => { + chrome.tabs.sendMessage(tab.id, obj, options, (response) => { if (chrome.runtime.lastError) { // Some error happened } - resolve(); + resolve(response); }); }); } @@ -263,6 +263,28 @@ export class BrowserApi { ); } + /** + * Gathers the details for a specified sub-frame of a tab. + * + * @param details - The details of the frame to get. + */ + static async getFrameDetails( + details: chrome.webNavigation.GetFrameDetails, + ): Promise { + return new Promise((resolve) => chrome.webNavigation.getFrame(details, resolve)); + } + + /** + * Gets all frames associated with a tab. + * + * @param tabId - The id of the tab to get the frames for. + */ + static async getAllFrameDetails( + tabId: chrome.tabs.Tab["id"], + ): Promise { + return new Promise((resolve) => chrome.webNavigation.getAllFrames({ tabId }, resolve)); + } + // Keep track of all the events registered in a Safari popup so we can remove // them when the popup gets unloaded, otherwise we cause a memory leak private static trackedChromeEventListeners: [ diff --git a/apps/browser/test.setup.ts b/apps/browser/test.setup.ts index 4800b4c17f3..87707b8295f 100644 --- a/apps/browser/test.setup.ts +++ b/apps/browser/test.setup.ts @@ -133,6 +133,8 @@ const permissions = { }; const webNavigation = { + getFrame: jest.fn(), + getAllFrames: jest.fn(), onCommitted: { addListener: jest.fn(), removeListener: jest.fn(),