#!/bin/bash set -euo pipefail # Copyright (c) 2011 Zotero # Center for History and New Media # George Mason University, Fairfax, Virginia, USA # http://zotero.org # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")" . "$APP_ROOT_DIR/config.sh" . "$SCRIPT_DIR/utils.sh" cd "$APP_ROOT_DIR" function usage { cat >&2 < FingerprintingWebCompatService.sys.mjs echo '' > PartitioningExceptionListService.sys.mjs rm Places* echo '' > PlacesExpiration.sys.mjs echo '' > PlacesFrecencyRecalculator.sys.mjs rm -rf services-settings/* touch services-settings/remote-settings.sys.mjs # Causes a startup error -- try an empty file or a shim instead? #rm Telemetry* rm URLDecorationAnnotationsService.sys.mjs popd pushd chrome/toolkit/content/mozapps/extensions rm -rf default-theme popd # This file loads remote-settings.sys.mjs, but if it's empty Zotero crashes replace_line 'async #init\(\) \{' 'async #init() { if (true) { return; }' modules/URLQueryStrippingListService.sys.mjs # Clear most WebExtension manifest properties replace_line 'manifest = normalized.value;' 'manifest = normalized.value; if (this.type == "extension") { if (!manifest.applications?.zotero?.id) { this.manifestError("applications.zotero.id not provided"); } if (!manifest.applications?.zotero?.update_url) { this.manifestError("applications.zotero.update_url not provided"); } if (!manifest.applications?.zotero?.strict_max_version) { this.manifestError("applications.zotero.strict_max_version not provided"); } manifest.browser_specific_settings = undefined; manifest.content_scripts = []; manifest.permissions = []; manifest.host_permissions = []; manifest.web_accessible_resources = undefined; manifest.experiment_apis = {}; }' modules/Extension.sys.mjs # Use applications.zotero instead of applications.gecko replace_line 'let bss = manifest.applications\?.gecko' 'let bss = manifest.applications?.zotero' modules/addons/XPIInstall.sys.mjs replace_line 'manifest.applications\?.gecko' 'manifest.applications?.zotero' modules/Extension.sys.mjs # When installing addon, use app version instead of toolkit version for targetApplication replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/XPIInstall.sys.mjs # Accept zotero@chnm.gmu.edu for target application to allow Zotero 6 plugins to remain # installed in Zotero 7 replace_line "if \(targetApp.id == Services.appinfo.ID\) \{" "if (targetApp.id == 'zotero\@chnm.gmu.edu') targetApp.id = '$APP_ID'; if (targetApp.id == Services.appinfo.ID) {" modules/addons/XPIDatabase.sys.mjs # TEMP: Skip addons without ids until we figure out what's going on with the default theme. # This fixes plugin updating and other things. replace_line 'return addons;' 'return addons.filter(addon => addon.id);' modules/addons/XPIDatabase.sys.mjs # For updates, look for applications.zotero instead of applications.gecko in manifest.json and # use the app id and version for strict_min_version/strict_max_version comparisons replace_line 'gecko: \{\},' 'zotero: {},' modules/addons/AddonUpdateChecker.sys.mjs replace_line 'if \(!\("gecko" in applications\)\) \{' 'if (!("zotero" in applications)) {' modules/addons/AddonUpdateChecker.sys.mjs replace_line '"gecko not in application entry' '"zotero not in application entry' modules/addons/AddonUpdateChecker.sys.mjs replace_line 'let app = getProperty\(applications, "gecko", "object"\);' 'let app = getProperty(applications, "zotero", "object");' modules/addons/AddonUpdateChecker.sys.mjs replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/AddonUpdateChecker.sys.mjs replace_line 'lazy.AddonManagerPrivate.webExtensionsMinPlatformVersion' '"7.0"' modules/addons/AddonUpdateChecker.sys.mjs replace_line 'result.targetApplications.push' 'false && result.targetApplications.push' modules/addons/AddonUpdateChecker.sys.mjs # Set uninstall flag to true if the update config has it set replace_line 'targetApplications: \[appEntry\],' \ 'targetApplications: \[appEntry\], uninstall: getProperty(update, "uninstall", "boolean"),' modules/addons/AddonUpdateChecker.sys.mjs # Prevent automatic installation of default theme # (chrome/toolkit/content/mozapps/extensions/default-theme, removed above) replace_line 'this.maybeInstallBuiltinAddon\(' 'if (false) this.maybeInstallBuiltinAddon(' modules/addons/XPIProvider.sys.mjs # Don't log a warning when checking 'distribution/extensions' for addons replace_line 'logger.warn\(`Checking' 'logger.debug(`Checking' modules/addons/XPIProvider.sys.mjs # Allow addon installation by bypassing confirmation dialogs. If we want a confirmation dialog, # we need to either add gXPInstallObserver from browser-addons.js [1][2] or provide our own with # Ci.amIWebInstallPrompt [3]. # # [1] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/browser/base/content/browser.js#1902-1923 # [2] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/browser/base/content/browser-addons.js#201 # [3] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/toolkit/mozapps/extensions/AddonManager.sys.mjs#3114-3124 replace_line 'if \(info.addon.installPermissions\) \{' 'if (false) {' modules/AddonManager.sys.mjs replace_line '\} else if \(info.addon.sitePermissions\) \{' '} else if (false) {' modules/AddonManager.sys.mjs replace_line '\} else if \(requireConfirm\) \{' '} else if (false) {' modules/AddonManager.sys.mjs # Make addon listener methods wait for promises, to allow calling asynchronous plugin `shutdown` # and `uninstall` methods in our `onInstalling` handler replace_line 'callAddonListeners\(aMethod' 'async callAddonListeners(aMethod' modules/AddonManager.sys.mjs # Don't need this one to be async, but we can't easily avoid modifying its `listener[aMethod].apply()` call replace_line 'callManagerListeners\(aMethod' 'async callManagerListeners(aMethod' modules/AddonManager.sys.mjs replace_line 'AddonManagerInternal.callAddonListeners.apply\(AddonManagerInternal, aArgs\);' \ 'return AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, aArgs);' modules/AddonManager.sys.mjs replace_line 'listener\[aMethod\].apply\(listener, aArgs\);' \ 'let maybePromise = listener[aMethod].apply(listener, aArgs); if (maybePromise && maybePromise.then) await maybePromise;' modules/AddonManager.sys.mjs replace_line 'AddonManagerPrivate.callAddonListeners' 'await AddonManagerPrivate.callAddonListeners' modules/addons/XPIInstall.sys.mjs replace_line 'let uninstall = \(\) => \{' 'let uninstall = async () => {' modules/addons/XPIInstall.sys.mjs replace_line 'cancelUninstallAddon\(aAddon\)' 'async cancelUninstallAddon(aAddon)' modules/addons/XPIInstall.sys.mjs # Check for `uninstall` flag in update config and uninstall the addon if set replace_line 'if \(update && !this.addon.location.locked\) \{' \ 'if (update && !this.addon.location.locked) { if (update.uninstall) { let aId = this.addon.id; logger.debug(`AddonUpdateChecker: uninstall addon \${aId} with uninstall flag`); let addon = await AddonManager.getAddonByID(aId); if (addon && (addon.permissions & AddonManager.PERM_CAN_UNINSTALL)) { try { addon.uninstall(); } catch (err) { logger.error(`AddonUpdateChecker: error uninstalling addon \${aId} with uninstall flag`, err); } sendUpdateAvailableMessages(this, null); return; } else { logger.error(`AddonUpdateChecker: addon \${aId} with uninstall flag not found or cannot be uninstalled`); sendUpdateAvailableMessages(this, null); return; } } ' modules/addons/XPIInstall.sys.mjs # Prevent login manager from trying to load FxAccountsCommon.sys.mjs replace_line 'return login.hostname == lazy.FXA_PWDMGR_HOST;' 'return false;' modules/storage-json.sys.mjs # No idea why this is necessary, but without it initialization fails with "TypeError: "constructor" is read-only" replace_line 'LoginStore.prototype.constructor = LoginStore;' '\/\/LoginStore.prototype.constructor = LoginStore;' modules/LoginStore.sys.mjs # # # Allow proxy password saving # perl -pi -e 's/get _inPrivateBrowsing\(\) \{/get _inPrivateBrowsing() {if (true) { return false; }/' components/nsLoginManagerPrompter.js # # # Change text in update dialog # perl -pi -e 's/A security and stability update for/A new version of/' chrome/en-US/locale/en-US/mozapps/update/updates.properties # perl -pi -e 's/updateType_major=New Version/updateType_major=New Major Version/' chrome/en-US/locale/en-US/mozapps/update/updates.properties # perl -pi -e 's/updateType_minor=Security Update/updateType_minor=New Version/' chrome/en-US/locale/en-US/mozapps/update/updates.properties # perl -pi -e 's/update for &brandShortName; as soon as possible/update as soon as possible/' chrome/en-US/locale/en-US/mozapps/update/updates.dtd # # Set available locales cp "$APP_ROOT_DIR/assets/multilocale.txt" res/multilocale.txt # Use Zotero URL opening in Mozilla dialogs (e.g., app update dialog) replace_line 'function openURL\(aURL\) \{' 'function openURL(aURL) {let {Zotero} = ChromeUtils.importESModule("chrome:\/\/zotero\/content\/zotero.mjs"); Zotero.launchURL(aURL); if (true) { return; }' chrome/toolkit/content/global/contentAreaUtils.js # # Modify Add-ons window # # Prevent display: block from overriding display: none on
s replace_line 'display: block !important;' 'display: block;' chrome/toolkit/content/global/elements/panel-list.css file="chrome/toolkit/content/mozapps/extensions/aboutaddons.css" echo >> $file # Hide search bar, Themes and Plugins tabs, and sidebar footer echo '.main-search, button[name="theme"], button[name="plugin"], sidebar-footer { display: none; }' >> $file echo '.main-heading { margin-top: 2em; }' >> $file # Hide Details/Permissions tabs in addon details so we only show details echo 'addon-details > button-group { display: none !important; }' >> $file # Hide "Run on sites with restrictions" row and helper message echo '.addon-detail-row-quarantined-domains { display: none; }' >> $file echo '.addon-detail-help-row:has([data-l10n-id="addon-detail-quarantined-domains-help"]) { display: none; }' >> $file # Hide "Report" button from plugin menu echo 'addon-card addon-options hr:first-of-type { display: none }' >> $file echo '[data-l10n-id="report-addon-button"] { display: none }' >> $file # Hide "Debug Addons" and "Manage Extension Shortcuts" echo 'panel-item[action="debug-addons"], panel-item[action="reset-update-states"] + hr, panel-item[action="manage-shortcuts"] { display: none }' >> $file # Show cursor feedback on plugin homepage links echo '.addon-detail-row-homepage .text-link { cursor: pointer; color: LinkText; }' >> $file echo '.addon-detail-row-homepage .text-link:hover { text-decoration: underline; }' >> $file file="chrome/toolkit/content/mozapps/extensions/aboutaddons.js" # Hide unsigned-addon warning replace_line 'if \(!isCorrectlySigned\(addon\)\) \{' 'if (!isCorrectlySigned(addon)) {return {};' $file # Hide Private Browsing setting in addon details replace_line 'pbRow\.' '\/\/pbRow.' $file replace_line 'let isAllowed = await isAllowedInPrivateBrowsing' '\/\/let isAllowed = await isAllowedInPrivateBrowsing' $file # Use our own strings for the removal prompt replace_line 'let \{ BrowserAddonUI \} = windowRoot.ownerGlobal;' '' $file replace_line 'await BrowserAddonUI.promptRemoveExtension' 'promptRemoveExtension' $file # Customize empty-list message replace_line 'createEmptyListMessage\(\) {' 'createEmptyListMessage() { var p = document.createElement("p"); p.id = "empty-list-message"; return p;' $file # Swap in include.js, which we need for Zotero.getString(), for abuse-reports.js, which we don't need # Open plugin links in external browser replace_line 'let homepageURL = homepageRow.querySelector\(\"a\"\);' 'let homepageURL = homepageRow.querySelector(\"\.text-link\");' $file replace_line 'homepageURL.href = addon.homepageURL;' 'homepageURL.setAttribute("href", addon.homepageURL);' $file replace_line '<\/a>' \ '