mirror of
https://github.com/zotero/zotero.git
synced 2026-08-28 05:25:31 +00:00
795 lines
36 KiB
Bash
Executable file
795 lines
36 KiB
Bash
Executable file
#!/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 <http://www.gnu.org/licenses/>.
|
||
|
||
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"
|
||
|
||
usage() {
|
||
cat >&2 <<'DONE'
|
||
Usage: fetch_xulrunner.sh -p <m|w|l> [-a ARCH]
|
||
|
||
Required
|
||
-p PLATFORM m (Mac), w (Windows), l (Linux)
|
||
|
||
Optional / Conditional
|
||
-a ARCH - Mac: disallowed (builds are universal)
|
||
- Windows: x64 | arm64 | win32
|
||
- Linux: x64 (or x86_64) | arm64 | i686
|
||
If omitted on Windows/Linux, all standard archs are fetched.
|
||
DONE
|
||
exit 1
|
||
}
|
||
|
||
BUILD_MAC=0
|
||
BUILD_WIN=0
|
||
BUILD_LINUX=0
|
||
arch=""
|
||
while getopts 'p:a:' opt; do
|
||
case $opt in
|
||
p)
|
||
for (( i=0; i<${#OPTARG}; i++ )); do
|
||
case ${OPTARG:i:1} in
|
||
m) BUILD_MAC=1;;
|
||
w) BUILD_WIN=1;;
|
||
l) BUILD_LINUX=1;;
|
||
*) echo "Invalid platform: ${OPTARG:i:1}"; echo; usage;;
|
||
esac
|
||
done;;
|
||
a) arch="$OPTARG";;
|
||
*) usage;;
|
||
esac
|
||
shift $((OPTIND-1)); OPTIND=1
|
||
done
|
||
|
||
# Require at least one platform
|
||
if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; then
|
||
usage
|
||
fi
|
||
|
||
# Sanity‑check arch flag vs platform selections
|
||
if [[ $BUILD_MAC == 1 ]]; then
|
||
[[ -n $arch ]] && { echo "-a is not allowed for Mac" >&2; echo; usage; }
|
||
fi
|
||
if [[ $BUILD_WIN == 1 || $BUILD_LINUX == 1 ]]; then
|
||
# arch optional; if provided validate & map aliases now
|
||
if [[ -n $arch ]]; then
|
||
if [[ $BUILD_WIN == 1 && $BUILD_LINUX == 1 ]]; then
|
||
echo "-a cannot be used when building both Windows and Linux simultaneously" >&2; echo; usage
|
||
fi
|
||
if [[ $BUILD_WIN == 1 ]]; then
|
||
case $arch in
|
||
x64) arch="win-x64" ;;
|
||
arm64) arch="win-arm64" ;;
|
||
win32|win-x64|win-arm64) ;;
|
||
*) echo "Invalid Windows arch: $arch" >&2; echo; usage;;
|
||
esac
|
||
fi
|
||
if [[ $BUILD_LINUX == 1 ]]; then
|
||
[[ $arch == x64 ]] && arch="x86_64"
|
||
case $arch in x86_64|i686|arm64) ;; *) echo "Invalid Linux arch: $arch" >&2; echo; usage;; esac
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
function get_utf16_chars {
|
||
str=$(echo -n "$1" | xxd -p | fold -w 2 | sed -r 's/(.+)/\\\\x{\1}\\\\x{00}/')
|
||
# Add NUL padding
|
||
if [ -n "${2:-}" ]; then
|
||
# Multiply characters x 2 for UTF-16
|
||
for i in `seq 1 $(($2 * 2))`; do
|
||
str+=$(echo '\\x{00}')
|
||
done
|
||
fi
|
||
echo $str | xargs | sed 's/ //g'
|
||
}
|
||
|
||
#
|
||
# Make various modifications to the stock Firefox app
|
||
#
|
||
function modify_omni {
|
||
platform=$1
|
||
|
||
mkdir omni
|
||
mv omni.ja omni
|
||
cd omni
|
||
# omni.ja is an "optimized" ZIP file, so use a script from Mozilla to avoid a warning from unzip
|
||
# here and to make it work after rezipping below
|
||
python3 "$APP_ROOT_DIR/scripts/optimizejars.py" --deoptimize ./ ./ ./
|
||
rm -f omni.ja.log
|
||
unzip omni.ja
|
||
rm omni.ja
|
||
|
||
rm -r chrome/pdfjs
|
||
|
||
rm -r modules/narrate
|
||
#rm -r modules/translation
|
||
|
||
rm actors/AboutTranslations{Parent,Child}.sys.mjs
|
||
rm actors/CookieBanner{Parent,Child}.sys.mjs
|
||
rm actors/PictureInPictureChild.sys.mjs
|
||
rm actors/ThumbnailsChild.sys.mjs
|
||
rm actors/Translations{Engine,}{Parent,Child}.sys.mjs
|
||
rm actors/AudioPlayback{Parent,Child}.sys.mjs
|
||
|
||
replace_line 'BROWSER_CHROME_URL:.+' 'BROWSER_CHROME_URL: "chrome:\/\/zotero\/content\/zoteroPane.xhtml",' modules/AppConstants.sys.mjs
|
||
|
||
# https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html
|
||
#
|
||
# It's not clear that most of these do anything anymore when not compiled in, but just in case
|
||
replace_line 'MOZ_REQUIRE_SIGNING:' 'MOZ_REQUIRE_SIGNING: false \&\&' modules/AppConstants.sys.mjs
|
||
replace_line 'MOZ_DATA_REPORTING:' 'MOZ_DATA_REPORTING: false \&\&' modules/AppConstants.sys.mjs
|
||
replace_line 'MOZ_TELEMETRY_REPORTING:' 'MOZ_TELEMETRY_REPORTING: false \&\&' modules/AppConstants.sys.mjs
|
||
replace_line 'MOZ_CRASHREPORTER:' 'MOZ_CRASHREPORTER: false \&\&' modules/AppConstants.sys.mjs
|
||
replace_line 'MOZ_UPDATE_CHANNEL:.+' 'MOZ_UPDATE_CHANNEL: "none",' modules/AppConstants.sys.mjs
|
||
replace_line '"https:\/\/[^\/]+mozilla.com.+"' '""' modules/AppConstants.sys.mjs
|
||
|
||
# Don't use Mozilla Maintenance Service on Windows
|
||
replace_line 'MOZ_MAINTENANCE_SERVICE:' 'MOZ_MAINTENANCE_SERVICE: false \&\&' modules/AppConstants.sys.mjs
|
||
# Continue using app.update.auto in prefs.js on Windows
|
||
replace_line 'PER_INSTALLATION_PREFS_PLATFORMS = \["win"\]' 'PER_INSTALLATION_PREFS_PLATFORMS = []' modules/UpdateUtils.sys.mjs
|
||
|
||
# Prompt if major update is available instead of installing automatically on restart
|
||
replace_line 'if \(!updateAuto\) \{' 'if (update.type == "major") {
|
||
LOG("UpdateService:_selectAndInstallUpdate - prompting because it is a major update");
|
||
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_SHOWPROMPT_PREF);
|
||
Services.obs.notifyObservers(update, "update-available", "show-prompt");
|
||
return;
|
||
}
|
||
if (!updateAuto) {' modules/UpdateService.sys.mjs
|
||
|
||
# Avoid console warning about resource://gre/modules/FxAccountsCommon.js
|
||
replace_line 'const logins = this._data.logins;' 'const logins = this._data.logins; if (this._data.logins.length != -1) return;' modules/LoginStore.sys.mjs
|
||
|
||
# Prevent error during network requests
|
||
replace_line 'async lazyInit\(\) \{' 'async lazyInit() { if (this.features) return false;' modules/UrlClassifierExceptionListService.sys.mjs
|
||
|
||
# Silence CrashManager "Could not get children of [...]/Crash Reports/events" spam
|
||
replace_line '\/\/ Missing events folders are allowed' '\/\/ Missing events folders are allowed (silence warning)
|
||
if (ex) return entries;' modules/CrashManager.sys.mjs
|
||
|
||
# Several services pull in services-settings (which we strip) and throw
|
||
# at profile-after-change. Remove their #init() methods to prevent errors.
|
||
replace_line 'async #init\(\) \{' 'async #init() {} async _unused_init() {' modules/EssentialDomainsRemoteSettings.sys.mjs
|
||
replace_line 'async #init\(\) \{' 'async #init() {} async _unused_init() {' modules/SimpleURIUnknownSchemesRemoteObserver.sys.mjs
|
||
# RemotePermissionService uses a top-level import, so even loading the
|
||
# module throws
|
||
replace_line 'import \{ RemoteSettings \} from "resource:\/\/services-settings\/remote-settings.sys.mjs";' \
|
||
'const RemoteSettings = () => ({ on() {}, get: async () => [] });' \
|
||
modules/RemotePermissionService.sys.mjs
|
||
|
||
replace_line 'pref\("network.captive-portal-service.enabled".+' 'pref("network.captive-portal-service.enabled", false);' greprefs.js
|
||
replace_line 'pref\("network.connectivity-service.enabled".+' 'pref("network.connectivity-service.enabled", false);' greprefs.js
|
||
replace_line 'pref\("toolkit.telemetry.server".+' 'pref("toolkit.telemetry.server", "");' greprefs.js
|
||
replace_line 'pref\("toolkit.telemetry.unified".+' 'pref("toolkit.telemetry.unified", false);' greprefs.js
|
||
replace_line 'pref\("media.gmp-manager.url".+' 'pref("media.gmp-manager.url", "");' greprefs.js
|
||
|
||
#
|
||
# # Disable transaction timeout
|
||
# perl -pi -e 's/let timeoutPromise/\/*let timeoutPromise/' modules/Sqlite.jsm
|
||
# perl -pi -e 's/return Promise.race\(\[transactionPromise, timeoutPromise\]\);/*\/return transactionPromise;/' modules/Sqlite.jsm
|
||
# rm -f jsloader/resource/gre/modules/Sqlite.jsm
|
||
#
|
||
# Disable unwanted components
|
||
remove_line '(RemoteSettings|services-|telemetry|Telemetry|URLDecorationAnnotationsService)' components/components.manifest
|
||
|
||
file="modules/ActorManagerParent.sys.mjs"
|
||
# Remove deleted actors
|
||
remove_between 'AboutTranslations: \{' '^ },' $file
|
||
remove_between 'CookieBanner: \{' '^ },' $file
|
||
remove_between 'PictureInPictureLauncher: \{' '^ },' $file
|
||
remove_between 'PictureInPictureToggle: \{' '^ },' $file
|
||
remove_between 'PictureInPicture: \{' '^ },' $file
|
||
remove_between 'Thumbnails: \{' '^ },' $file
|
||
remove_between 'Translations: \{' '^ },' $file
|
||
remove_between 'TranslationsEngine: \{' '^ },' $file
|
||
remove_between 'AudioPlayback: \{' '^ },' $file
|
||
# Do not trigger LoginManager event that logs an error on autocomplete submission
|
||
remove_line 'DOMInputPasswordAdded: \{\},' $file
|
||
|
||
# On Mac/Linux, ignore relative paths in PATH in Subprocess.pathSearch(), used when a bare command is passed to Utilities.Internal.subprocess()
|
||
if [[ $platform != win* ]]; then
|
||
replace_line 'let path = PathUtils.join\(dir, bin\);' 'if (!dir.startsWith("\/")) continue; let path = PathUtils.join(dir, bin);' modules/subprocess/subprocess_unix.sys.mjs
|
||
fi
|
||
|
||
# Remove unwanted files
|
||
pushd modules
|
||
rm Bookmarks*
|
||
rm FxAccounts*
|
||
echo '' > 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
|
||
|
||
# Set strictCompatibility to false in beta/dev/source builds to skip strict_max_version check
|
||
replace_line 'addon.strictCompatibility = true;' \
|
||
'let version = Services.appinfo.version;
|
||
addon.strictCompatibility = !version.includes("-beta") && !version.includes("-dev") && !version.includes("SOURCE");' modules/addons/XPIInstall.sys.mjs
|
||
|
||
# In beta/dev/source builds, ignore the persisted strictCompatibility flag for already-installed
|
||
# plugins, so plugins installed on a stable build (where the flag was set to true) aren't marked
|
||
# incompatible after upgrading. The manifest isn't re-parsed on app upgrade, so the flag set at
|
||
# install time sticks around.
|
||
replace_line '!this\.addon\.strictCompatibility' \
|
||
'(!this.addon.strictCompatibility || Services.appinfo.version.includes("-beta") || Services.appinfo.version.includes("-dev") || Services.appinfo.version.includes("SOURCE"))' modules/addons/XPIInstall.sys.mjs
|
||
replace_line '!this\.strictCompatibility &&' \
|
||
'(!this.strictCompatibility || Services.appinfo.version.includes("-beta") || Services.appinfo.version.includes("-dev") || Services.appinfo.version.includes("SOURCE")) &&' modules/addons/XPIDatabase.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
|
||
# In beta/dev/source builds, don't set strictCompatibility on parsed update entries so that
|
||
# AddonUpdateChecker doesn't reject an available update whose maxVersion is lower than the
|
||
# current (non-stable) app version.
|
||
replace_line 'result\.strictCompatibility = appEntry\.maxVersion != "\*";' \
|
||
'let appVer = Services.appinfo.version; result.strictCompatibility = appEntry.maxVersion != "*" && !appVer.includes("-beta") && !appVer.includes("-dev") && !appVer.includes("SOURCE");' 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 <hr>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 target="_blank" dir="ltr"><\/a>' \
|
||
'<label target="_blank" class="text-link" dir="ltr"><\/label>' \
|
||
chrome/toolkit/content/mozapps/extensions/aboutaddons.html
|
||
|
||
# Add zotero.ftl to the addons page for our own localized strings
|
||
replace_line 'href="toolkit\/about\/aboutAddons.ftl"' 'href="toolkit\/about\/aboutAddons.ftl" \/>\n <link rel="localization" href="zotero.ftl"' \
|
||
chrome/toolkit/content/mozapps/extensions/aboutaddons.html
|
||
# Use our own localized string for blocked plugin notification
|
||
file="chrome/toolkit/content/mozapps/extensions/aboutaddons.js"
|
||
replace_line 'details-notification-hard-blocked-\$\{typeSuffix\}' 'plugins-blocked-plugin' $file
|
||
|
||
# TODO: Use our own strings, but for now fix the bundled English ones
|
||
replace_line 'Manage Your Extensions' 'Manage Your Plugins' localization/en-US/toolkit/about/aboutAddons.ftl
|
||
replace_line '= Extensions' '= Plugins' localization/en-US/toolkit/about/aboutAddons.ftl
|
||
replace_line 'Add-on' 'Plugin' localization/en-US/toolkit/about/aboutAddons.ftl
|
||
replace_line 'add-ons' 'plugins' localization/en-US/toolkit/about/aboutAddons.ftl
|
||
replace_line 'Select add-on to install' 'Select plugin to install' localization/en-US/toolkit/about/aboutAddons.ftl
|
||
|
||
# Hide Recommendations tab in sidebar and recommendations in main pane
|
||
replace_line 'function isDiscoverEnabled\(\) \{' 'function isDiscoverEnabled() {return false;' chrome/toolkit/content/mozapps/extensions/aboutaddonsCommon.js
|
||
replace_line 'pref\("extensions.htmlaboutaddons.recommendations.enabled".+' 'pref("extensions.htmlaboutaddons.recommendations.enabled", false);' greprefs.js
|
||
|
||
# Hide Report option
|
||
replace_line 'pref\("extensions.abuseReport.enabled".+' 'pref("extensions.abuseReport.enabled", false);' greprefs.js
|
||
|
||
# The first displayed Services.prompt dialog's size jumps around because sizeToContent() is called twice
|
||
# Fix by preventing the first sizeToContent() call if the icon hasn't been loaded yet
|
||
replace_line 'window.sizeToContent\(\);' 'if (ui.infoIcon.complete) window.sizeToContent();' chrome/toolkit/content/global/commonDialog.js
|
||
replace_line 'ui.infoIcon.addEventListener' 'if (!ui.infoIcon.complete) ui.infoIcon.addEventListener' chrome/toolkit/content/global/commonDialog.js
|
||
|
||
# Import style into built-in dialogs
|
||
replace_line 'id=\"commonDialogWindow\"' \
|
||
'id=\"commonDialogWindow\" class=\"zotero-dialog\" drawintitlebar-platforms=\"mac\"' \
|
||
chrome/toolkit/content/global/commonDialog.xhtml
|
||
# commonDialog.css link is split across multiple lines, so we have to do a weird substitution,
|
||
# so check the one-line global.css to make sure the format hasn't changed
|
||
check_line '<html:link rel="stylesheet" href="chrome:\/\/global\/skin\/global.css" \/>'
|
||
replace_line 'chrome:\/\/global\/skin\/commonDialog.css"' \
|
||
'chrome:\/\/global\/skin\/commonDialog.css"\/>
|
||
<html:link rel="stylesheet" href="chrome:\/\/zotero-platform\/content\/zotero.css"' \
|
||
chrome/toolkit/content/global/commonDialog.xhtml
|
||
# commonDialog.xhtml can be opened via Services.prompt in the Profile Manager, so check for a profile first
|
||
replace_line 'function commonDialogOnLoad\(\) \{' \
|
||
'function commonDialogOnLoad() {
|
||
try {
|
||
Services.dirsvc.get(\"ProfD\", Ci.nsIFile);
|
||
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/include.js\", this);
|
||
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/titlebar.js\", this);
|
||
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/customElements.js\", this);
|
||
} catch (e) {}' \
|
||
chrome/toolkit/content/global/commonDialog.js
|
||
|
||
# Use native checkbox instead of Firefox-themed version in prompt dialogs
|
||
replace_line '<xul:checkbox' '<xul:checkbox native=\"true\"' chrome/toolkit/content/global/commonDialog.xhtml
|
||
|
||
# The <browser> CE appends its autoscroll <popup> to document.documentElement, which doesn't work in zoteroPane.xhtml
|
||
# (in Firefox browser.xhtml, document.documentElement is <html>, which can contain <popup>s; in zoteroPane.xhtml, it's
|
||
# <window>, which can't)
|
||
# Fix by appending it to a <popupset> instead
|
||
replace_line 'document.documentElement.appendChild\(this._autoScrollPopup\);' 'let popupset = document.querySelector("popupset");
|
||
if (!popupset) {
|
||
popupset = document.createXULElement("popupset");
|
||
document.documentElement.appendChild(popupset);
|
||
}
|
||
popupset.appendChild(this._autoScrollPopup);' chrome/toolkit/content/global/elements/browser-custom-element.mjs
|
||
|
||
# Remove aria-autocomplete unnecessarily added to search-textbox to not confuse screen readers
|
||
remove_line 'this.inputField.setAttribute\("aria-autocomplete' chrome/toolkit/content/global/elements/search-textbox.js
|
||
|
||
# Remove non-native text input styles
|
||
remove_between 'html\|input\:where\(' '^}' chrome/toolkit/skin/classic/global/global-shared.css
|
||
|
||
# Prevent window dragging on scrollbars
|
||
echo 'scrollbar { -moz-window-dragging: no-drag; }' >> chrome/toolkit/res/scrollbars.css
|
||
|
||
# Provide a mechanism for non-UA stylesheets to override the overflow property
|
||
# of a text control's anonymous text node child
|
||
echo '::placeholder, ::-moz-text-control-editing-root, ::-moz-text-control-preview {
|
||
overflow: var(--moz-text-control-overflow);
|
||
}' >> chrome/toolkit/res/forms.css
|
||
|
||
# By default, an autocomplete popup's width is calculated based on the input that opened it.
|
||
# Allow an ancestor to designate itself as the width container instead. (For creator inputs.)
|
||
replace_line 'aElement.getBoundingClientRect\(\).width' \
|
||
'(aElement.closest(".autocomplete-popup-width-container") || aElement).getBoundingClientRect().width' \
|
||
chrome/toolkit/content/global/elements/autocomplete-popup.js
|
||
|
||
zip -qr9XD omni.ja *
|
||
mv omni.ja ..
|
||
cd ..
|
||
python3 "$APP_ROOT_DIR/scripts/optimizejars.py" --optimize ./ ./ ./
|
||
rm -rf omni
|
||
|
||
# Unzip browser/omni.ja and leave unzipped
|
||
cd browser
|
||
mkdir omni
|
||
mv omni.ja omni
|
||
cd omni
|
||
ls -la
|
||
set +e
|
||
unzip omni.ja
|
||
set -e
|
||
rm omni.ja
|
||
|
||
# Remove Firefox update URLs
|
||
remove_line 'pref\("app.update.url.(manual|details)' defaults/preferences/firefox-branding.js
|
||
|
||
# Remove Firefox overrides (e.g., to use Firefox-specific strings for connection errors)
|
||
remove_line '(override)' chrome/chrome.manifest
|
||
|
||
# Remove WebExtension APIs
|
||
remove_line ext-browser.json components/components.manifest
|
||
|
||
# Don't open a second window if app is already open when launching .exe (Windows) or running via
|
||
# command line (macOS)
|
||
replace_line 'function dch_handle\(cmdLine\) \{' 'function dch_handle(cmdLine) {
|
||
if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) { return; }
|
||
' modules/BrowserContentHandler.sys.mjs
|
||
}
|
||
|
||
mkdir -p xulrunner
|
||
cd xulrunner
|
||
|
||
if [ $BUILD_MAC == 1 ]; then
|
||
GECKO_VERSION="$GECKO_VERSION_MAC"
|
||
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
|
||
rm -rf Firefox.app
|
||
|
||
if [ -e "Firefox $GECKO_VERSION.app.zip" ]; then
|
||
echo "Using Firefox $GECKO_VERSION.app.zip"
|
||
unzip "Firefox $GECKO_VERSION.app.zip"
|
||
else
|
||
curl -o Firefox.dmg "$DOWNLOAD_URL/mac/en-US/Firefox%20$GECKO_VERSION.dmg"
|
||
set +e
|
||
hdiutil detach -quiet /Volumes/Firefox 2>/dev/null
|
||
set -e
|
||
hdiutil attach -quiet Firefox.dmg
|
||
cp -a /Volumes/Firefox/Firefox.app .
|
||
# Store local copy of unmodified app
|
||
zip -r "Firefox $GECKO_VERSION.app.zip" Firefox.app
|
||
hdiutil detach -quiet /Volumes/Firefox
|
||
fi
|
||
|
||
# Download custom components (e.g., XUL), as an xz-compressed tarball of the MacOS folder with
|
||
# custom files in it
|
||
echo
|
||
rm -rf MacOS
|
||
if [ -n "$custom_components_hash_mac" ]; then
|
||
if [ -e "Firefox $GECKO_VERSION MacOS.zip" ]; then
|
||
echo "Using Firefox $GECKO_VERSION MacOS.zip"
|
||
unzip "Firefox $GECKO_VERSION MacOS.zip"
|
||
else
|
||
echo "Downloading custom Firefox components"
|
||
echo
|
||
curl -o MacOS.tar.xz "${custom_components_url}mac/$custom_components_hash_mac.tar.xz"
|
||
|
||
# Verify hash
|
||
if [[ "`uname`" = "Darwin" ]]; then
|
||
shasum="shasum -a 256"
|
||
else
|
||
shasum=sha256sum
|
||
fi
|
||
echo
|
||
echo "$custom_components_hash_mac MacOS.tar.xz" | $shasum -c -
|
||
echo
|
||
|
||
tar xvf MacOS.tar.xz
|
||
fi
|
||
echo
|
||
fi
|
||
|
||
pushd Firefox.app/Contents/Resources
|
||
modify_omni mac
|
||
popd
|
||
|
||
# Replace "FirefoxCP" with "ZoteroCP" for subprocesses ("Isolated Web Content", "Socket Process", "Web Content", etc.)
|
||
info_plist=Firefox.app/Contents/MacOS/plugin-container.app/Contents/Resources/English.lproj/InfoPlist.strings
|
||
from=$(get_utf16_chars "FirefoxCP")
|
||
to=$(get_utf16_chars "ZoteroCP")
|
||
perl -pi -e "s/$from/$to/" $info_plist
|
||
# Check for UTF-16 "ZoteroCP"
|
||
if ! grep -a -q "Z.o.t.e.r.o.C.P." $info_plist; then
|
||
echo '"ZoteroCP" not found in InfoPlist.strings after replacement'
|
||
exit 1
|
||
fi
|
||
|
||
rm -f Firefox.dmg
|
||
|
||
echo $("$SCRIPT_DIR/xulrunner_hash" -p m) > hash-mac
|
||
fi
|
||
|
||
if [ $BUILD_WIN == 1 ]; then
|
||
GECKO_VERSION="$GECKO_VERSION_WIN"
|
||
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
|
||
|
||
if [[ -n $arch ]]; then
|
||
arch_list=("$(get_canonical_arch w $arch)")
|
||
else
|
||
arch_list=(win-x64 win-arm64 win32)
|
||
fi
|
||
for arch in "${arch_list[@]}"; do
|
||
xdir=firefox-$arch
|
||
|
||
rm -rf $xdir
|
||
mkdir $xdir
|
||
|
||
archived_file="Firefox Setup $GECKO_VERSION-$arch.exe"
|
||
if [ -e "$archived_file" ]; then
|
||
echo "Using $archived_file"
|
||
cp "$archived_file" "Firefox%20Setup%20$GECKO_VERSION.exe"
|
||
else
|
||
if [ $arch = "win-x64" ]; then
|
||
moz_arch=win64
|
||
elif [ $arch = "win-arm64" ]; then
|
||
moz_arch=win64-aarch64
|
||
else
|
||
moz_arch=$arch
|
||
fi
|
||
curl -O "$DOWNLOAD_URL/$moz_arch/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
|
||
# Store local copy of unmodified app
|
||
cp "Firefox%20Setup%20$GECKO_VERSION.exe" "$archived_file"
|
||
fi
|
||
|
||
7z x Firefox%20Setup%20$GECKO_VERSION.exe -o$xdir 'core/*'
|
||
mv $xdir/core $xdir-core
|
||
rm -rf $xdir
|
||
mv $xdir-core $xdir
|
||
|
||
# Download custom components, as an architecture-specific ZIP containing xul.dll, etc.
|
||
echo
|
||
hash_arch_name="${arch/-/_}"
|
||
arch_var_name="custom_components_hash_${hash_arch_name}"
|
||
hash=${!arch_var_name:-}
|
||
if [ -n "$hash" ]; then
|
||
zipfile="Firefox $GECKO_VERSION-$arch Components.zip"
|
||
# ZIP filename doesn't include "esr" or "win-"
|
||
url="${custom_components_url}win/${GECKO_VERSION//esr}-${arch#win-}-$hash.zip"
|
||
|
||
if [[ "$(uname)" = "Darwin" ]]; then
|
||
shasum="shasum -a 256"
|
||
else
|
||
shasum=sha256sum
|
||
fi
|
||
|
||
download_zip() {
|
||
echo "Downloading custom Firefox components"
|
||
echo
|
||
echo $url
|
||
curl -fo "$zipfile" "$url"
|
||
}
|
||
|
||
verify_hash() {
|
||
echo "$hash $zipfile" | $shasum -c -
|
||
}
|
||
|
||
# Download if file doesn't exist
|
||
if [ ! -e "$zipfile" ]; then
|
||
download_zip
|
||
fi
|
||
|
||
echo
|
||
if ! verify_hash; then
|
||
echo "SHA hash mismatch. Redownloading ZIP…"
|
||
rm -f "$zipfile"
|
||
download_zip
|
||
echo
|
||
if ! verify_hash; then
|
||
echo "Hash still doesn't match. Aborting."
|
||
exit 1
|
||
fi
|
||
fi
|
||
echo
|
||
|
||
unzip -o "$zipfile" -d "$xdir"
|
||
echo
|
||
fi
|
||
|
||
pushd $xdir
|
||
|
||
#
|
||
# NOTE: Below is disabled, since the full UTF-16 string no longer appears directly in the DLL
|
||
# -- it's now truncated after "e594e8d1" for x64/win32 and doesn't appear at all for ARM64
|
||
#
|
||
|
||
# Replace "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38" with "Zotero" for C:\ProgramData directory
|
||
#
|
||
# Mozilla uses a UUID in the path because they previously used just "Mozilla" and needed to
|
||
# recreate the folder with correct permissions for security reasons, but we never had a folder in
|
||
# ProgramData, so we can just create it as "Zotero". Instead of using a custom xul.dll, just
|
||
# replace the hard-coded string with "Zotero" and add a bunch of NULs.
|
||
#
|
||
#from=$(get_utf16_chars "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38")
|
||
#to=$(get_utf16_chars "Zotero" 38)
|
||
#perl -pe "s/$from/$to/" < xul.dll > xul.dll.new
|
||
#mv xul.dll.new xul.dll
|
||
|
||
# Check for UTF-16 "Zotero" in DLL
|
||
#
|
||
# (The macOS strings command doesn't have an encoding option, so skip on macOS. You need to
|
||
# build on Windows to make an installer, so a Windows build from macOS likely isn't being
|
||
# deployed.)
|
||
#
|
||
#if [[ "`uname`" != "Darwin" ]] && [[ -z "$(strings -e l xul.dll | grep -m 1 Zotero)" ]]; then
|
||
# echo '"Zotero" not found in xul.dll after replacement'
|
||
# exit 1
|
||
#fi
|
||
|
||
# We re-sign DLL in build.sh, but presumably this file, which is for the Widevine CDM,
|
||
# isn't vaid anymore
|
||
rm xul.dll.sig
|
||
|
||
modify_omni $arch
|
||
# Disable skeleton UI window
|
||
# https://forums.zotero.org/discussion/comment/437636/#Comment_437636
|
||
replace_line 'pref\("browser\.startup\.preXulSkeletonUI", true\);' 'pref("browser.startup.preXulSkeletonUI", false);' defaults/preferences/firefox.js
|
||
popd
|
||
|
||
# Uncomment to create local copies for reuse
|
||
#cp "Firefox%20Setup%20$GECKO_VERSION.exe" "Firefox Setup $GECKO_VERSION-$arch.exe"
|
||
rm "Firefox%20Setup%20$GECKO_VERSION.exe"
|
||
echo
|
||
echo
|
||
|
||
echo "$( "$SCRIPT_DIR/xulrunner_hash" -p w -a "$arch" )" > "hash-$arch"
|
||
done
|
||
fi
|
||
|
||
if [ $BUILD_LINUX == 1 ]; then
|
||
GECKO_VERSION="$GECKO_VERSION_LINUX"
|
||
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
|
||
|
||
if [[ -n $arch ]]; then
|
||
arch_list=("$arch")
|
||
else
|
||
arch_list=(x86_64 arm64 i686)
|
||
fi
|
||
for arch in "${arch_list[@]}"; do
|
||
xdir="firefox-linux-$arch"
|
||
rm -rf $xdir
|
||
|
||
archived_file="firefox-$GECKO_VERSION-$arch.tar.xz"
|
||
if [ -e "$archived_file" ]; then
|
||
echo "Using $archived_file"
|
||
cp "$archived_file" "firefox-$GECKO_VERSION.tar.xz"
|
||
else
|
||
if [ $arch = "arm64" ]; then
|
||
moz_arch=aarch64
|
||
else
|
||
moz_arch=$arch
|
||
fi
|
||
curl -O "$DOWNLOAD_URL/linux-$moz_arch/en-US/firefox-$GECKO_VERSION.tar.xz"
|
||
# Store local copy of unmodified app
|
||
cp "firefox-$GECKO_VERSION.tar.xz" "$archived_file"
|
||
fi
|
||
|
||
tar xvf firefox-$GECKO_VERSION.tar.xz
|
||
mv firefox $xdir
|
||
|
||
pushd $xdir
|
||
modify_omni $arch
|
||
popd
|
||
|
||
rm "firefox-$GECKO_VERSION.tar.xz"
|
||
|
||
echo "$( $SCRIPT_DIR/xulrunner_hash -p l -a "$arch" )" > "hash-linux-${arch}"
|
||
done
|
||
fi
|
||
|
||
echo Done
|