1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-10-04 05:08:06 +02:00

[PM-8518] Autofill scripts do not inject into sub frames on install (#9459)

* [PM-8518] Autofill scripts do not inject into sub-frames on install

* [PM-8518] Implementing jest tests for added BrowserApi methods

* [PM-8518] Adding generic typing to tabSendMessage call
This commit is contained in:
Cesar Gonzalez 2024-06-11 15:50:03 -05:00 committed by GitHub
parent 882a432ca6
commit 3b0005b48c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 77 additions and 7 deletions

View File

@ -209,6 +209,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<chrome.webNavigation.GetAllFrameResultDetails>({ frameId: 0 })]);
jest
.spyOn(autofillService, "getOverlayVisibility")
.mockResolvedValue(AutofillOverlayVisibility.OnFieldFocus);
@ -219,6 +222,7 @@ describe("AutofillService", () => {
jest.spyOn(autofillService, "injectAutofillScripts");
await autofillService.loadAutofillScriptsOnInstall();
await flushPromises();
expect(BrowserApi.tabsQuery).toHaveBeenCalledWith({});
expect(autofillService.injectAutofillScripts).toHaveBeenCalledWith(tab1, 0, false);

View File

@ -2136,7 +2136,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));
}
}
}

View File

@ -62,7 +62,8 @@
"scripting",
"offscreen",
"webRequest",
"webRequestAuthProvider"
"webRequestAuthProvider",
"webNavigation"
],
"optional_permissions": ["nativeMessaging", "privacy"],
"host_permissions": ["https://*/*", "http://*/*"],

View File

@ -235,6 +235,46 @@ describe("BrowserApi", () => {
});
});
describe("getFrameDetails", () => {
it("returns the frame details of the specified frame", async () => {
const tabId = 1;
const frameId = 2;
const mockFrameDetails = mock<chrome.webNavigation.GetFrameResultDetails>();
chrome.webNavigation.getFrame = jest
.fn()
.mockImplementation((_details, callback) => callback(mockFrameDetails));
const returnFrame = await BrowserApi.getFrameDetails({ tabId, frameId });
expect(chrome.webNavigation.getFrame).toHaveBeenCalledWith(
{ tabId, frameId },
expect.any(Function),
);
expect(returnFrame).toEqual(mockFrameDetails);
});
});
describe("getAllFrameDetails", () => {
it("returns all sub frame details of the specified tab", async () => {
const tabId = 1;
const mockFrameDetails1 = mock<chrome.webNavigation.GetAllFrameResultDetails>();
const mockFrameDetails2 = mock<chrome.webNavigation.GetAllFrameResultDetails>();
chrome.webNavigation.getAllFrames = jest
.fn()
.mockImplementation((_details, callback) =>
callback([mockFrameDetails1, mockFrameDetails2]),
);
const frames = await BrowserApi.getAllFrameDetails(tabId);
expect(chrome.webNavigation.getAllFrames).toHaveBeenCalledWith(
{ tabId },
expect.any(Function),
);
expect(frames).toEqual([mockFrameDetails1, mockFrameDetails2]);
});
});
describe("reloadExtension", () => {
it("reloads the window location if the passed globalContext is for the window", () => {
const windowMock = mock<Window>({

View File

@ -176,21 +176,21 @@ export class BrowserApi {
return BrowserApi.tabSendMessage(tab, obj);
}
static async tabSendMessage<T>(
static async tabSendMessage<T, TResponse = unknown>(
tab: chrome.tabs.Tab,
obj: T,
options: chrome.tabs.MessageSendOptions = null,
): Promise<void> {
): Promise<TResponse> {
if (!tab || !tab.id) {
return;
}
return new Promise<void>((resolve) => {
chrome.tabs.sendMessage(tab.id, obj, options, () => {
return new Promise<TResponse>((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<chrome.webNavigation.GetFrameResultDetails> {
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<chrome.webNavigation.GetAllFrameResultDetails[]> {
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: [

View File

@ -135,6 +135,8 @@ const permissions = {
};
const webNavigation = {
getFrame: jest.fn(),
getAllFrames: jest.fn(),
onCommitted: {
addListener: jest.fn(),
removeListener: jest.fn(),