1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-10-01 04:37:40 +02:00

[PM-5881] Adjust usage of the chrome.extension.getViews API to ensure expected behavior in mv3 is not modified (#7842)

* [PM-5742] Rework Usage of Extension APIs that Cannot be Called with the Background Service Worker

* [PM-5742] Implementing jest tests for the updated BrowserApi methods

* [PM-5742] Implementing jest tests to validate logic within added API calls

* [PM-5742] Implementing jest tests to validate logic within added API calls

* [PM-5742] Fixing broken Jest tests

* [PM-5742] Fixing linter error

* [PM-5881] Adjust usage of the `chrome.extension.getViews` API to ensure expected behavior in manifest v3

* [PM-5881] Reworking how we handle early returns from `reloadOpenWindows`

* [PM-5881] Implementing jest test to validate changes within BrowserApi.reloadOpenWindows
This commit is contained in:
Cesar Gonzalez 2024-02-23 13:01:45 -06:00 committed by GitHub
parent 34a8d9af86
commit 968355d820
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 59 additions and 48 deletions

View File

@ -219,6 +219,18 @@ describe("BrowserApi", () => {
window.location.reload = reload;
});
it("skips reloading any windows if no views can be found", () => {
Object.defineProperty(window, "location", {
value: { reload: jest.fn(), href: "chrome-extension://id-value/background.html" },
writable: true,
});
chrome.extension.getViews = jest.fn().mockReturnValue([]);
BrowserApi.reloadOpenWindows();
expect(window.location.reload).not.toHaveBeenCalled();
});
it("reloads all open windows", () => {
Object.defineProperty(window, "location", {
value: { reload: jest.fn(), href: "chrome-extension://id-value/index.html" },

View File

@ -404,8 +404,12 @@ export class BrowserApi {
* @param exemptCurrentHref - Whether to exempt the current window location from the reload.
*/
static reloadOpenWindows(exemptCurrentHref = false) {
const currentHref = window?.location.href;
const views = BrowserApi.getExtensionViews();
if (!views.length) {
return;
}
const currentHref = window.location.href;
views
.filter((w) => w.location.href != null && !w.location.href.includes("background.html"))
.filter((w) => !exemptCurrentHref || w.location.href !== currentHref)

View File

@ -1,5 +1,7 @@
import { DeviceType } from "@bitwarden/common/enums";
import { BrowserApi } from "../browser/browser-api";
import BrowserPlatformUtilsService from "./browser-platform-utils.service";
describe("Browser Utils Service", () => {
@ -91,41 +93,24 @@ describe("Browser Utils Service", () => {
});
describe("isViewOpen", () => {
beforeEach(() => {
globalThis.chrome = {
// eslint-disable-next-line
// @ts-ignore
extension: {
getViews: jest.fn(),
},
};
it("returns false if a heartbeat response is not received", async () => {
BrowserApi.sendMessageWithResponse = jest.fn().mockResolvedValueOnce(undefined);
const isViewOpen = await browserPlatformUtilsService.isViewOpen();
expect(isViewOpen).toBe(false);
});
it("returns true if the user is on Firefox and the sidebar is open", async () => {
chrome.extension.getViews = jest.fn().mockReturnValueOnce([window]);
jest
.spyOn(browserPlatformUtilsService, "getDevice")
.mockReturnValueOnce(DeviceType.FirefoxExtension);
it("returns true if a heartbeat response is received", async () => {
BrowserApi.sendMessageWithResponse = jest
.fn()
.mockImplementationOnce((subscriber) =>
Promise.resolve((subscriber === "checkVaultPopupHeartbeat") as any),
);
const result = await browserPlatformUtilsService.isViewOpen();
const isViewOpen = await browserPlatformUtilsService.isViewOpen();
expect(result).toBe(true);
});
it("returns true if a extension view is open as a tab", async () => {
chrome.extension.getViews = jest.fn().mockReturnValueOnce([window]);
const result = await browserPlatformUtilsService.isViewOpen();
expect(result).toBe(true);
});
it("returns false if no extension view is open", async () => {
chrome.extension.getViews = jest.fn().mockReturnValue([]);
const result = await browserPlatformUtilsService.isViewOpen();
expect(result).toBe(false);
expect(isViewOpen).toBe(true);
});
});
});

View File

@ -150,23 +150,13 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
return false;
}
/**
* Identifies if the vault popup is currently open. This is done by sending a
* message to the popup and waiting for a response. If a response is received,
* the view is open.
*/
async isViewOpen(): Promise<boolean> {
if (await BrowserApi.isPopupOpen()) {
return true;
}
if (this.isSafari()) {
return false;
}
// Opera has "sidebar_panel" as a ViewType but doesn't currently work
if (this.isFirefox() && BrowserApi.getExtensionViews({ type: "sidebar" }).length > 0) {
return true;
}
// Opera sidebar has type of "tab" (will stick around for a while after closing sidebar)
const tabOpen = BrowserApi.getExtensionViews({ type: "tab" }).length > 0;
return tabOpen;
return Boolean(await BrowserApi.sendMessageWithResponse("checkVaultPopupHeartbeat"));
}
lockTimeout(): number {

View File

@ -6,6 +6,7 @@ import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
import { BrowserApi } from "../../platform/browser/browser-api";
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
import { BrowserStateService as StateServiceAbstraction } from "../../platform/services/abstractions/browser-state.service";
@ -52,6 +53,25 @@ export class InitService {
}
this.configService.init();
this.setupVaultPopupHeartbeat();
};
}
/**
* Sets up a runtime message listener to indicate to the background
* script that the extension popup is open in some manner.
*/
private setupVaultPopupHeartbeat() {
const respondToHeartbeat = (
message: { command: string },
_sender: chrome.runtime.MessageSender,
sendResponse: (response?: any) => void,
) => {
if (message?.command === "checkVaultPopupHeartbeat") {
sendResponse(true);
}
};
BrowserApi.messageListener("vaultPopupHeartbeat", respondToHeartbeat);
}
}