Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-5880] Refactor browser platform utils service to remove window references #7885

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
96dc70e
[PM-5880] Refactor Browser Platform Utils Service to Remove Window Se…
Feb 8, 2024
0eb8b71
[PM-5880] Implementing BrowserClipboardService to handle clipboard lo…
Feb 8, 2024
c078292
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 9, 2024
30ee36f
[PM-5880] Adjusting how readText is handled within BrowserClipboardSe…
Feb 9, 2024
8e66536
[PM-5880] Adjusting how readText is handled within BrowserClipboardSe…
Feb 9, 2024
849e76a
[PM-5880] Working through implementation of chrome offscreen API usage
Feb 9, 2024
da479e1
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 12, 2024
75e80d3
[PM-5880] Implementing jest tests for the methods added to the Browse…
Feb 12, 2024
6a33ad6
[PM-5880] Implementing jest tests for the OffscreenDocument class
Feb 12, 2024
b72c4d5
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 12, 2024
7861f31
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 14, 2024
eae9e5f
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 14, 2024
5499456
[PM-5880] Working through jest tests for BrowserClipboardService
Feb 12, 2024
28dc5e9
[PM-5880] Adding typing information to the clipboard methods present …
Feb 14, 2024
6dfa863
[PM-5880] Working on adding ServiceWorkerGlobalScope typing information
Feb 14, 2024
cc0b9f2
[PM-5880] Updating window references when calling BrowserPlatformUtil…
Feb 14, 2024
e37b01f
[PM-5880] Finishing out jest tests for the BrowserClipboardService
Feb 14, 2024
8d6c7a6
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 15, 2024
faf95a9
[PM-5880] Finishing out jest tests for the BrowserClipboardService
Feb 14, 2024
339d783
[PM-5880] Implementing jest tests to validate the changes within Brow…
Feb 15, 2024
0dc687d
[PM-5880] Implementing jest tests to ensure coverage within Offscreen…
Feb 15, 2024
3f15fce
[PM-5880] Implementing jest tests for the BrowserPlatformUtilsService
Feb 15, 2024
bb3eb3d
[PM-5880] Removing unused catch statements
Feb 15, 2024
19383f7
[PM-5880] Implementing jest tests for the BrowserPlatformUtilsService
Feb 15, 2024
e5ecbf8
[PM-5880] Implementing jest tests for the BrowserPlatformUtilsService
Feb 15, 2024
5d32c08
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 15, 2024
2a79001
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 16, 2024
90b7921
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 19, 2024
7b767c4
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 19, 2024
365f015
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 21, 2024
f6eff73
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 21, 2024
9b412da
[PM-5880] Fixing broken tests
Feb 21, 2024
71927f6
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 22, 2024
275d5c5
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 22, 2024
c46d47c
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 22, 2024
134e6b2
[PM-5880] Merging in work from main
Feb 23, 2024
211c23d
[PM-5880] Merging in work from main
Feb 23, 2024
2fdc709
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 23, 2024
ec96d29
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 24, 2024
86b60c6
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 26, 2024
33a944f
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 27, 2024
bac2429
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 28, 2024
bc794df
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 28, 2024
1384048
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 28, 2024
9335fa7
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Feb 29, 2024
f33c6a1
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Mar 1, 2024
ce00415
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Mar 4, 2024
51411cd
[PM-5880] Merging in work from main and fixing merge conflicts
Mar 4, 2024
d3533fa
[PM-5880] Merging in work from main and fixing merge conflicts
Mar 5, 2024
ffb61b7
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Mar 5, 2024
44c9fc3
Merge branch 'main' into autofill/pm-5880-refactor-browser-platform-u…
cagonzalezcs Mar 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1259,7 +1259,7 @@ describe("OverlayBackground", () => {
});
await flushPromises();

expect(copyToClipboardSpy).toHaveBeenCalledWith("totp-code", { window });
expect(copyToClipboardSpy).toHaveBeenCalledWith("totp-code");
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
});

if (totpCode) {
this.platformUtilsService.copyToClipboard(totpCode, { window });
this.platformUtilsService.copyToClipboard(totpCode);
}

this.overlayLoginCiphers = new Map([[overlayCipherId, cipher], ...this.overlayLoginCiphers]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class WebRequestBackground {
private cipherService: CipherService,
private authService: AuthService,
) {
if (BrowserApi.manifestVersion === 2) {
if (BrowserApi.isManifestVersion(2)) {
this.webRequest = (window as any).chrome.webRequest;
}
this.isFirefox = platformUtilsService.isFirefox();
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/background/commands.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
private async generatePasswordToClipboard() {
const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {};
const password = await this.passwordGenerationService.generatePassword(options);
this.platformUtilsService.copyToClipboard(password, { window: window });
this.platformUtilsService.copyToClipboard(password);

Check warning on line 67 in apps/browser/src/background/commands.background.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/background/commands.background.ts#L67

Added line #L67 was not covered by tests
await this.passwordGenerationService.addHistory(password);
}

Expand Down
36 changes: 17 additions & 19 deletions apps/browser/src/background/main.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@
popupOnlyContext: boolean;

constructor(public isPrivateMode: boolean = false) {
this.popupOnlyContext = isPrivateMode || BrowserApi.manifestVersion === 3;
this.popupOnlyContext = isPrivateMode || BrowserApi.isManifestVersion(3);

// Services
const lockedCallback = async (userId?: string) => {
Expand Down Expand Up @@ -353,20 +353,18 @@
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
this.storageService = new BrowserLocalStorageService();
this.secureStorageService = new BrowserLocalStorageService();
this.memoryStorageService =
BrowserApi.manifestVersion === 3
? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
this.keyGenerationService,
)
: new MemoryStorageService();
this.memoryStorageForStateProviders =
BrowserApi.manifestVersion === 3
? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
this.keyGenerationService,
)
: new BackgroundMemoryStorageService();
this.memoryStorageService = BrowserApi.isManifestVersion(3)
? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
this.keyGenerationService,
)
: new MemoryStorageService();
this.memoryStorageForStateProviders = BrowserApi.isManifestVersion(3)
? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
this.keyGenerationService,
)
: new BackgroundMemoryStorageService();

const storageServiceProvider = new StorageServiceProvider(
this.storageService as BrowserLocalStorageService,
Expand Down Expand Up @@ -462,7 +460,7 @@
return promise.then((result) => result.response === "unlocked");
}
},
window,
self,
);
this.i18nService = new BrowserI18nService(BrowserApi.getUILanguage(), this.stateService);
this.cryptoService = new BrowserCryptoService(
Expand Down Expand Up @@ -898,11 +896,11 @@
);
if (!this.popupOnlyContext) {
const contextMenuClickedHandler = new ContextMenuClickedHandler(
(options) => this.platformUtilsService.copyToClipboard(options.text, { window: self }),
(options) => this.platformUtilsService.copyToClipboard(options.text),

Check warning on line 899 in apps/browser/src/background/main.background.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/background/main.background.ts#L899

Added line #L899 was not covered by tests
async (_tab) => {
const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {};
const password = await this.passwordGenerationService.generatePassword(options);
this.platformUtilsService.copyToClipboard(password, { window: window });
this.platformUtilsService.copyToClipboard(password);

Check warning on line 903 in apps/browser/src/background/main.background.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/background/main.background.ts#L903

Added line #L903 was not covered by tests
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.passwordGenerationService.addHistory(password);
Expand Down Expand Up @@ -1143,7 +1141,7 @@
await this.reseedStorage();
}

if (BrowserApi.manifestVersion === 3) {
if (BrowserApi.isManifestVersion(3)) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
BrowserApi.sendMessage("updateBadge");
Expand Down
6 changes: 3 additions & 3 deletions apps/browser/src/background/runtime.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
msg.sender === "autofill_cmd",
);
if (totpCode != null) {
this.platformUtilsService.copyToClipboard(totpCode, { window: window });
this.platformUtilsService.copyToClipboard(totpCode);

Check warning on line 175 in apps/browser/src/background/runtime.background.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/background/runtime.background.ts#L175

Added line #L175 was not covered by tests
}
break;
}
Expand Down Expand Up @@ -261,7 +261,7 @@
});
break;
case "getClickedElementResponse":
this.platformUtilsService.copyToClipboard(msg.identifier, { window: window });
this.platformUtilsService.copyToClipboard(msg.identifier);

Check warning on line 264 in apps/browser/src/background/runtime.background.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/background/runtime.background.ts#L264

Added line #L264 was not covered by tests
break;
case "triggerFido2ContentScriptInjection":
await this.fido2Service.injectFido2ContentScripts(sender);
Expand Down Expand Up @@ -319,7 +319,7 @@
});

if (totpCode != null) {
this.platformUtilsService.copyToClipboard(totpCode, { window: window });
this.platformUtilsService.copyToClipboard(totpCode);

Check warning on line 322 in apps/browser/src/background/runtime.background.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/background/runtime.background.ts#L322

Added line #L322 was not covered by tests
}

// reset
Expand Down
3 changes: 2 additions & 1 deletion apps/browser/src/manifest.v3.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"clipboardWrite",
"idle",
"alarms",
"scripting"
"scripting",
"offscreen"
],
"optional_permissions": ["nativeMessaging", "privacy"],
"host_permissions": ["http://*/*", "https://*/*"],
Expand Down
4 changes: 2 additions & 2 deletions apps/browser/src/platform/alarms/alarm-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const alarmState: AlarmState = {
*/
export async function getAlarmTime(commandName: AlarmKeys): Promise<number> {
let alarmTime: number;
if (BrowserApi.manifestVersion == 3) {
if (BrowserApi.isManifestVersion(3)) {
const fromSessionStore = await chrome.storage.session.get(commandName);
alarmTime = fromSessionStore[commandName];
} else {
Expand Down Expand Up @@ -58,7 +58,7 @@ export async function clearAlarmTime(commandName: AlarmKeys): Promise<void> {
}

async function setAlarmTimeInternal(commandName: AlarmKeys, time: number): Promise<void> {
if (BrowserApi.manifestVersion == 3) {
if (BrowserApi.isManifestVersion(3)) {
await chrome.storage.session.set({ [commandName]: time });
} else {
alarmState[commandName] = time;
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/platform/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
tabsOnUpdatedListener,
} from "./listeners";

if (BrowserApi.manifestVersion === 3) {
if (BrowserApi.isManifestVersion(3)) {
chrome.commands.onCommand.addListener(onCommandListener);
chrome.runtime.onInstalled.addListener(onInstallListener);
chrome.alarms.onAlarm.addListener(onAlarmListener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function memoryStorageServiceFactory(
opts: MemoryStorageServiceInitOptions,
): Promise<AbstractMemoryStorageService> {
return factory(cache, "memoryStorageService", opts, async () => {
if (BrowserApi.manifestVersion === 3) {
if (BrowserApi.isManifestVersion(3)) {
return new LocalBackedSessionStorageService(
await encryptServiceFactory(cache, opts),
await keyGenerationServiceFactory(cache, opts),
Expand Down
94 changes: 94 additions & 0 deletions apps/browser/src/platform/browser/browser-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ describe("BrowserApi", () => {
jest.clearAllMocks();
});

describe("isManifestVersion", () => {
beforeEach(() => {
jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(3);
});

it("returns true if the manifest version matches the provided version", () => {
const result = BrowserApi.isManifestVersion(3);

expect(result).toBe(true);
});

it("returns false if the manifest version does not match the provided version", () => {
const result = BrowserApi.isManifestVersion(2);

expect(result).toBe(false);
});
});

describe("getWindow", () => {
it("will get the current window if a window id is not provided", () => {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
Expand Down Expand Up @@ -106,6 +124,38 @@ describe("BrowserApi", () => {
});
});

describe("getTab", () => {
it("returns `null` if the tabId is a falsy value", async () => {
const result = await BrowserApi.getTab(null);

expect(result).toBeNull();
});

it("returns the tab within manifest v3", async () => {
const tabId = 1;
jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(3);
(chrome.tabs.get as jest.Mock).mockImplementation(
(tabId) => ({ id: tabId }) as chrome.tabs.Tab,
);

const result = await BrowserApi.getTab(tabId);

expect(result).toEqual({ id: tabId });
});

it("returns the tab within manifest v2", async () => {
const tabId = 1;
jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(2);
(chrome.tabs.get as jest.Mock).mockImplementation((tabId, callback) =>
callback({ id: tabId } as chrome.tabs.Tab),
);

const result = BrowserApi.getTab(tabId);

await expect(result).resolves.toEqual({ id: tabId });
});
});

describe("getBackgroundPage", () => {
it("returns a null value if the `getBackgroundPage` method is not available", () => {
chrome.extension.getBackgroundPage = undefined;
Expand Down Expand Up @@ -280,6 +330,24 @@ describe("BrowserApi", () => {
});
});

describe("getBrowserAction", () => {
it("returns the `chrome.action` API if the extension manifest is for version 3", () => {
jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(3);

const result = BrowserApi.getBrowserAction();

expect(result).toEqual(chrome.action);
});

it("returns the `chrome.browserAction` API if the extension manifest is for version 2", () => {
jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(2);

const result = BrowserApi.getBrowserAction();

expect(result).toEqual(chrome.browserAction);
});
});

describe("executeScriptInTab", () => {
it("calls to the extension api to execute a script within the give tabId", async () => {
const tabId = 1;
Expand Down Expand Up @@ -456,4 +524,30 @@ describe("BrowserApi", () => {
});
});
});

describe("createOffscreenDocument", () => {
it("creates the offscreen document with the supplied reasons and justification", async () => {
const reasons = [chrome.offscreen.Reason.CLIPBOARD];
const justification = "justification";

await BrowserApi.createOffscreenDocument(reasons, justification);

expect(chrome.offscreen.createDocument).toHaveBeenCalledWith({
url: "offscreen-document/index.html",
reasons,
justification,
});
});
});

describe("closeOffscreenDocument", () => {
it("closes the offscreen document", () => {
const callbackMock = jest.fn();

BrowserApi.closeOffscreenDocument(callbackMock);

expect(chrome.offscreen.closeDocument).toHaveBeenCalled();
expect(callbackMock).toHaveBeenCalled();
});
});
});
51 changes: 48 additions & 3 deletions apps/browser/src/platform/browser/browser-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Observable } from "rxjs";

Check notice on line 1 in apps/browser/src/platform/browser/browser-api.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

✅ Getting better: Primitive Obsession

The ratio of primitive types in function arguments decreases from 46.67% to 45.45%, threshold = 30.0%. The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.

import { DeviceType } from "@bitwarden/common/enums";

Expand All @@ -19,6 +19,15 @@
return chrome.runtime.getManifest().manifest_version;
}

/**
* Determines if the extension manifest version is the given version.
*
* @param expectedVersion - The expected manifest version to check against.
*/
static isManifestVersion(expectedVersion: 2 | 3) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking naming nit (the nittiest) - don't bother if you don't need to make other changes, tho

Suggested change
static isManifestVersion(expectedVersion: 2 | 3) {
static isManifestVersion(expectedManifestVersion: 2 | 3) {

Copy link
Contributor

@jprusik jprusik Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking nit/question:

I'm guessing no, but Is there a scenario where either of these two values in the comparison are not 2 or 3 (say, undefined)? Should we defend against that scenario?

Another way I'm thinking about it; any reason to replace BrowserApi.manifestVersion === 3 with this method other than (the slightly improved) conciseness?

return BrowserApi.manifestVersion === expectedVersion;
}

/**
* Gets the current window or the window with the given id.
*
Expand Down Expand Up @@ -98,12 +107,17 @@
});
}

/**
* Gets the tab with the given id.
*
* @param tabId - The id of the tab to get.
*/
static async getTab(tabId: number): Promise<chrome.tabs.Tab> | null {
if (!tabId) {
return null;
}

if (BrowserApi.manifestVersion === 3) {
if (BrowserApi.isManifestVersion(3)) {
return await chrome.tabs.get(tabId);
}

Expand Down Expand Up @@ -453,8 +467,11 @@
});
}

/**
* Returns the supported BrowserAction API based on the manifest version.
*/
static getBrowserAction() {
return BrowserApi.manifestVersion === 3 ? chrome.action : chrome.browserAction;
return BrowserApi.isManifestVersion(3) ? chrome.action : chrome.browserAction;
}

static getSidebarAction(
Expand Down Expand Up @@ -488,7 +505,7 @@
world: chrome.scripting.ExecutionWorld;
},
): Promise<unknown> {
if (BrowserApi.manifestVersion === 3) {
if (BrowserApi.isManifestVersion(3)) {
return chrome.scripting.executeScript({
target: {
tabId: tabId,
Expand Down Expand Up @@ -546,4 +563,32 @@
chrome.privacy.services.autofillCreditCardEnabled.set({ value });
chrome.privacy.services.passwordSavingEnabled.set({ value });
}

/**
* Opens the offscreen document with the given reasons and justification.
*
* @param reasons - List of reasons for opening the offscreen document.
* @see https://developer.chrome.com/docs/extensions/reference/api/offscreen#type-Reason
* @param justification - Custom written justification for opening the offscreen document.
*/
static async createOffscreenDocument(reasons: chrome.offscreen.Reason[], justification: string) {
await chrome.offscreen.createDocument({
url: "offscreen-document/index.html",
reasons,
justification,
});
}

/**
* Closes the offscreen document.
*
* @param callback - Optional callback to execute after the offscreen document is closed.
*/
static closeOffscreenDocument(callback?: () => void) {
chrome.offscreen.closeDocument(() => {
if (callback) {
callback();
}
});
}
}
Loading
Loading