From 55a8e1233672d6aae55c12daa1bcb6b256cf15a5 Mon Sep 17 00:00:00 2001 From: Hinton Date: Tue, 5 Nov 2024 12:19:24 +0100 Subject: [PATCH] Remove manifest and index.html logic from gulp --- apps/browser/package.json | 40 +++++----- apps/browser/src/manifest.json | 58 +++++++++++---- apps/browser/src/manifest.v3.json | 44 ++++++++--- .../src/popup/{index.html => index.ebs} | 2 +- apps/browser/webpack.config.js | 63 +++------------- apps/browser/webpack/manifest.js | 73 +++++++++++++++++++ 6 files changed, 181 insertions(+), 99 deletions(-) rename apps/browser/src/popup/{index.html => index.ebs} (84%) create mode 100644 apps/browser/webpack/manifest.js diff --git a/apps/browser/package.json b/apps/browser/package.json index 4a749522545..a859073d9a0 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -2,26 +2,28 @@ "name": "@bitwarden/browser", "version": "2024.11.0", "scripts": { - "build": "cross-env MANIFEST_VERSION=3 webpack", - "build:mv2": "webpack", + "build": "npm run build:chrome", + "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack", + "build:edge": "cross-env BROWSER=edge webpack", + "build:firefox": "cross-env BROWSER=firefox webpack", + "build:opera": "cross-env BROWSER=opera webpack", + "build:safari": "cross-env BROWSER=safari webpack", "build:watch": "npm run build:watch:chrome", - "build:watch:chrome": "cross-env MANIFEST_VERSION=3 BROWSER=chrome webpack --watch", - "build:watch:firefox": "cross-env MANIFEST_VERSION=3 BROWSER=firefox webpack --watch", - "build:watch:safari": "cross-env MANIFEST_VERSION=3 BROWSER=safari webpack --watch", - "build:watch:mv2": "webpack --watch", - "build:prod": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" webpack", - "build:prod:watch": "cross-env NODE_ENV=production webpack --watch", - "dist": "npm run build:prod && gulp dist", - "dist:mv3": "cross-env MANIFEST_VERSION=3 npm run build:prod && cross-env MANIFEST_VERSION=3 gulp dist", - "dist:chrome": "npm run build:prod && gulp dist:chrome", - "dist:chrome:beta": "cross-env MANIFEST_VERSION=3 npm run build:prod:beta && cross-env MANIFEST_VERSION=3 BETA_BUILD=1 gulp dist:chrome", - "dist:firefox": "npm run build:prod && gulp dist:firefox", - "dist:opera": "npm run build:prod && gulp dist:opera", - "dist:safari": "cross-env BROWSER=safari npm run build:prod && gulp dist:safari", - "dist:safari:mv3": "cross-env MANIFEST_VERSION=3 BROWSER=safari run build:prod && cross-env MANIFEST_VERSION=3 BROWSER=safari gulp dist:safari", - "dist:safari:mas": "npm run build:prod && gulp dist:safari:mas", - "dist:safari:masdev": "npm run build:prod && gulp dist:safari:masdev", - "dist:safari:dmg": "npm run build:prod && gulp dist:safari:dmg", + "build:watch:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack --watch", + "build:watch:edge": "cross-env BROWSER=edge webpack --watch", + "build:watch:firefox": "cross-env BROWSER=firefox webpack --watch", + "build:watch:opera": "cross-env BROWSER=opera webpack --watch", + "build:watch:safari": "cross-env BROWSER=safari webpack --watch", + "build:prod:chrome": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:chrome", + "build:prod:edge": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:edge", + "build:prod:firefox": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:firefox", + "build:prod:opera": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:opera", + "build:prod:safari": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:safari", + "dist:chrome": "npm run build:prod:chrome", + "dist:edge": "npm run build:prod:edge", + "dist:firefox": "npm run build:prod:firefox", + "dist:opera": "npm run build:prod:opera", + "dist:safari": "npm run build:prod:safari", "test": "jest", "test:watch": "jest --watch", "test:watch:all": "jest --watchAll" diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 0d9a4189578..29a697a3aa9 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -52,19 +52,37 @@ "permissions": [ "", "*://*/*", - "tabs", - "contextMenus", - "storage", - "unlimitedStorage", + "alarms", "clipboardRead", "clipboardWrite", + "contextMenus", "idle", - "alarms", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation", "webRequest", - "webRequestBlocking", - "webNavigation" + "webRequestBlocking" + ], + "__safari__permissions": [ + "", + "*://*/*", + "alarms", + "clipboardRead", + "clipboardWrite", + "contextMenus", + "idle", + "nativeMessaging", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation", + "webRequest", + "webRequestBlocking" ], "optional_permissions": ["nativeMessaging", "privacy"], + "__firefox__optional_permissions": ["nativeMessaging"], + "__safari__optional_permissions": null, "content_security_policy": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'", "sandbox": { "pages": [ @@ -75,6 +93,7 @@ ], "content_security_policy": "sandbox allow-scripts; script-src 'self'" }, + "__firefox__sandbox": null, "commands": { "_execute_browser_action": { "suggested_key": { @@ -83,7 +102,14 @@ }, "description": "__MSG_commandOpenPopup__" }, - "_execute_sidebar_action": { + "__firefox___execute_sidebar_action": { + "suggested_key": { + "default": "Alt+Shift+Y", + "linux": "Alt+Shift+U" + }, + "description": "__MSG_commandOpenSidebar__" + }, + "__opera___execute_sidebar_action": { "suggested_key": { "default": "Alt+Shift+Y", "linux": "Alt+Shift+U" @@ -125,20 +151,26 @@ "overlay/list.html", "popup/fonts/*" ], - "applications": { + "__firefox__browser_specific_settings": { "gecko": { "id": "{446900e4-71c2-419f-a6a7-df9c091e268b}", "strict_min_version": "91.0" } }, - "sidebar_action": { + "__firefox__sidebar_action": { "default_title": "Bitwarden", "default_panel": "popup/index.html?uilocation=sidebar", "default_icon": "images/icon19.png", "open_at_install": false, "browser_style": false }, - "storage": { - "managed_schema": "managed_schema.json" - } + "__opera__sidebar_action": { + "default_title": "Bitwarden", + "default_panel": "popup/index.html?uilocation=sidebar", + "default_icon": "images/icon19.png", + "open_at_install": false, + "browser_style": false + }, + "storage": { "managed_schema": "managed_schema.json" }, + "__firefox__storage": null } diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index f805b701551..41fa17d3dd0 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -51,21 +51,40 @@ }, "permissions": [ "activeTab", - "tabs", - "contextMenus", - "storage", - "unlimitedStorage", + "alarms", "clipboardRead", "clipboardWrite", + "contextMenus", "idle", - "alarms", - "scripting", "offscreen", + "scripting", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation", "webRequest", - "webRequestAuthProvider", - "webNavigation" + "webRequestAuthProvider" + ], + "__safari__permissions": [ + "activeTab", + "alarms", + "clipboardRead", + "clipboardWrite", + "contextMenus", + "idle", + "nativeMessaging", + "offscreen", + "scripting", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation", + "webRequest", + "webRequestAuthProvider" ], "optional_permissions": ["nativeMessaging", "privacy"], + "__firefox__optional_permissions": ["nativeMessaging"], + "__safari__optional_permissions": null, "host_permissions": ["https://*/*", "http://*/*"], "content_security_policy": { "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'", @@ -87,7 +106,7 @@ }, "description": "__MSG_commandOpenPopup__" }, - "_execute_sidebar_action": { + "__firefox___execute_sidebar_action": { "suggested_key": { "default": "Alt+Shift+Y", "linux": "Alt+Shift+U" @@ -133,13 +152,13 @@ "matches": [""] } ], - "applications": { + "__firefox__browser_specific_settings": { "gecko": { "id": "{446900e4-71c2-419f-a6a7-df9c091e268b}", "strict_min_version": "91.0" } }, - "sidebar_action": { + "__firefox__sidebar_action": { "default_title": "Bitwarden", "default_panel": "popup/index.html?uilocation=sidebar", "default_icon": "images/icon19.png", @@ -147,5 +166,6 @@ }, "storage": { "managed_schema": "managed_schema.json" - } + }, + "__firefox__storage": null } diff --git a/apps/browser/src/popup/index.html b/apps/browser/src/popup/index.ebs similarity index 84% rename from apps/browser/src/popup/index.html rename to apps/browser/src/popup/index.ebs index e76a8c749fd..20b79b6f067 100644 --- a/apps/browser/src/popup/index.html +++ b/apps/browser/src/popup/index.ebs @@ -1,5 +1,5 @@ - + diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index 5436e51011a..7557693bb86 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -7,58 +7,14 @@ const { AngularWebpackPlugin } = require("@ngtools/webpack"); const TerserPlugin = require("terser-webpack-plugin"); const { TsconfigPathsPlugin } = require("tsconfig-paths-webpack-plugin"); const configurator = require("./config/config"); +const manifest = require("./webpack/manifest"); if (process.env.NODE_ENV == null) { process.env.NODE_ENV = "development"; } const ENV = (process.env.ENV = process.env.NODE_ENV); const manifestVersion = process.env.MANIFEST_VERSION == 3 ? 3 : 2; -const browser = process.env.BROWSER; - -function modifyManifestV3(buffer) { - if (manifestVersion === 2 || !browser) { - return buffer; - } - - const manifest = JSON.parse(buffer.toString()); - - if (browser === "chrome") { - // Remove unsupported properties - delete manifest.applications; - delete manifest.sidebar_action; - delete manifest.commands._execute_sidebar_action; - - return JSON.stringify(manifest, null, 2); - } - - // Update the background script reference to be an event page - const backgroundScript = manifest.background.service_worker; - delete manifest.background.service_worker; - manifest.background.scripts = [backgroundScript]; - - // Remove unsupported properties - delete manifest.content_security_policy.sandbox; - delete manifest.sandbox; - delete manifest.applications; - - manifest.permissions = manifest.permissions.filter((permission) => permission !== "offscreen"); - - if (browser === "safari") { - delete manifest.sidebar_action; - delete manifest.commands._execute_sidebar_action; - delete manifest.optional_permissions; - manifest.permissions.push("nativeMessaging"); - } - - if (browser === "firefox") { - delete manifest.storage; - manifest.optional_permissions = manifest.optional_permissions.filter( - (permission) => permission !== "privacy", - ); - } - - return JSON.stringify(manifest, null, 2); -} +const browser = process.env.BROWSER ?? "chrome"; console.log(`Building Manifest Version ${manifestVersion} app`); @@ -142,9 +98,10 @@ const requiredPlugins = [ const plugins = [ new HtmlWebpackPlugin({ - template: "./src/popup/index.html", + template: "./src/popup/index.ebs", filename: "popup/index.html", chunks: ["popup/polyfills", "popup/vendor-angular", "popup/vendor", "popup/main"], + browser: browser, }), new HtmlWebpackPlugin({ template: "./src/autofill/notification/bar.html", @@ -178,13 +135,11 @@ const plugins = [ }), new CopyWebpackPlugin({ patterns: [ - manifestVersion == 3 - ? { - from: "./src/manifest.v3.json", - to: "manifest.json", - transform: (content) => modifyManifestV3(content), - } - : "./src/manifest.json", + { + from: manifestVersion == 3 ? "./src/manifest.v3.json" : "./src/manifest.json", + to: "manifest.json", + transform: manifest.transform(browser), + }, { from: "./src/managed_schema.json", to: "managed_schema.json" }, { from: "./src/_locales", to: "_locales" }, { from: "./src/images", to: "images" }, diff --git a/apps/browser/webpack/manifest.js b/apps/browser/webpack/manifest.js new file mode 100644 index 00000000000..fedc9b84c79 --- /dev/null +++ b/apps/browser/webpack/manifest.js @@ -0,0 +1,73 @@ +/** + * Transform the manifest template into a browser specific manifest. + * + * We support a simple browser prefix to the manifest keys. Example: + * + * ```json + * { + * "name": "Default name", + * "__chrome__name": "Chrome override" + * } + * ``` + * + * Will result in the following manifest: + * + * ```json + * { + * "name": "Chrome override" + * } + * ``` + * + * for Chrome. + */ +function transform(browser) { + return (buffer) => { + let manifest = JSON.parse(buffer.toString()); + + manifest = transformPrefixes(manifest, browser); + + return JSON.stringify(manifest, null, 2); + }; +} + +const browsers = ["chrome", "firefox", "opera", "edge", "safari"]; + +/** + * Flatten the browser prefixes in the manifest. + * + * - Removes unrelated browser prefixes. + * - A null value deletes the non prefixed key. + */ +function transformPrefixes(manifest, browser) { + const prefix = `__${browser}__`; + + function transformObject(obj) { + return Object.keys(obj).reduce((acc, key) => { + // Determine if we need to recurse into the object. + const isObjectNotArray = + typeof obj[key] === "object" && obj[key] !== null && !Array.isArray(obj[key]); + + if (key.startsWith(prefix)) { + const newKey = key.slice(prefix.length); + + // Null values are used to remove keys. + if (obj[key] == null) { + delete acc[newKey]; + return acc; + } + + acc[newKey] = isObjectNotArray ? transformObject(obj[key]) : obj[key]; + } else if (!browsers.some((b) => key.startsWith(`__${b}__`))) { + acc[key] = isObjectNotArray ? transformObject(obj[key]) : obj[key]; + } + + return acc; + }, {}); + } + + return transformObject(manifest); +} + +module.exports = { + transform, +};