Backports the following Mozilla fixes: https://bugzilla.mozilla.org/show_bug.cgi?id=1858409 (AutoFill/Services menus; fixes #3927) https://bugzilla.mozilla.org/show_bug.cgi?id=1884631 (sheets → dialogs; fixes #3936) https://bugzilla.mozilla.org/show_bug.cgi?id=148624 (ghost tooltips, because why not)
		
			
				
	
	
		
			570 lines
		
	
	
	
		
			26 KiB
			
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			570 lines
		
	
	
	
		
			26 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"
 | 
						|
cd "$APP_ROOT_DIR"
 | 
						|
 | 
						|
function usage {
 | 
						|
	cat >&2 <<DONE
 | 
						|
Usage: $0 -p platforms [-s]
 | 
						|
Options
 | 
						|
 -p PLATFORMS        Platforms to build (m=Mac, w=Windows, l=Linux)
 | 
						|
DONE
 | 
						|
	exit 1
 | 
						|
}
 | 
						|
 | 
						|
BUILD_MAC=0
 | 
						|
BUILD_WIN=0
 | 
						|
BUILD_LINUX=0
 | 
						|
while getopts "p:s" opt; do
 | 
						|
	case $opt in
 | 
						|
		p)
 | 
						|
			for i in `seq 0 1 $((${#OPTARG}-1))`
 | 
						|
			do
 | 
						|
				case ${OPTARG:i:1} in
 | 
						|
					m) BUILD_MAC=1;;
 | 
						|
					w) BUILD_WIN=1;;
 | 
						|
					l) BUILD_LINUX=1;;
 | 
						|
					*)
 | 
						|
						echo "$0: Invalid platform option ${OPTARG:i:1}"
 | 
						|
						usage
 | 
						|
						;;
 | 
						|
				esac
 | 
						|
			done
 | 
						|
			;;
 | 
						|
	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
 | 
						|
 | 
						|
function replace_line {
 | 
						|
	pattern=$1
 | 
						|
	replacement=$2
 | 
						|
	file=$3
 | 
						|
	
 | 
						|
	if egrep -q "$pattern" "$file"; then
 | 
						|
		perl -pi -e "s/$pattern/$replacement/" "$file"
 | 
						|
	else
 | 
						|
		echo "$pattern" not found in "$file" -- aborting 2>&1
 | 
						|
		exit 1
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
function remove_line {
 | 
						|
	pattern=$1
 | 
						|
	file=$2
 | 
						|
	
 | 
						|
	if egrep -q "$pattern" "$file"; then
 | 
						|
		egrep -v "$pattern" "$file" > "$file.tmp"
 | 
						|
		mv "$file.tmp" "$file"
 | 
						|
	else
 | 
						|
		echo "$pattern" not found in "$file" -- aborting 2>&1
 | 
						|
		exit 1
 | 
						|
	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
 | 
						|
	
 | 
						|
	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_SERVICES_HEALTHREPORT:' 'MOZ_SERVICES_HEALTHREPORT: false \&\&' modules/AppConstants.sys.mjs
 | 
						|
	replace_line 'MOZ_TELEMETRY_REPORTING:' 'MOZ_TELEMETRY_REPORTING: false \&\&' modules/AppConstants.sys.mjs
 | 
						|
	replace_line 'MOZ_TELEMETRY_ON_BY_DEFAULT:' 'MOZ_TELEMETRY_ON_BY_DEFAULT: 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
 | 
						|
	
 | 
						|
	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
 | 
						|
	
 | 
						|
	# Do not trigger LoginManager event that logs an error on autocomplete submission
 | 
						|
	remove_line 'DOMInputPasswordAdded: \{\},' modules/ActorManagerParent.sys.mjs
 | 
						|
	
 | 
						|
	# 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
 | 
						|
	rm modules/FxAccounts*
 | 
						|
	# Causes a startup error -- try an empty file or a shim instead?
 | 
						|
	#rm modules/Telemetry*
 | 
						|
	rm modules/URLDecorationAnnotationsService.sys.mjs
 | 
						|
	rm -rf modules/services-*
 | 
						|
	
 | 
						|
	# 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.jsm
 | 
						|
	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.jsm
 | 
						|
	
 | 
						|
	# 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.jsm
 | 
						|
	
 | 
						|
	# 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.jsm
 | 
						|
	
 | 
						|
	# 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
 | 
						|
	
 | 
						|
	# 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.userPermissions\) \{' '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.jsm
 | 
						|
	replace_line 'let uninstall = \(\) => \{' 'let uninstall = async () => {' modules/addons/XPIInstall.jsm
 | 
						|
	replace_line 'cancelUninstallAddon\(aAddon\)' 'async cancelUninstallAddon(aAddon)' modules/addons/XPIInstall.jsm
 | 
						|
	
 | 
						|
	# 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 "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: -moz-nativehyperlinktext; }' >> $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
 | 
						|
	
 | 
						|
	# 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\"' \
 | 
						|
		chrome/toolkit/content/global/commonDialog.xhtml
 | 
						|
	replace_line '<\?xml-stylesheet href=\"chrome:\/\/global\/skin\/commonDialog.css\" type=\"text\/css\"\?>' \
 | 
						|
		'<\?xml-stylesheet href=\"chrome:\/\/global\/skin\/commonDialog.css\" type=\"text\/css\"\?>
 | 
						|
		<\?xml-stylesheet href=\"chrome:\/\/zotero-platform\/content\/zotero.css\" type=\"text\/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 '<script>' \
 | 
						|
		'<script>
 | 
						|
		try {
 | 
						|
			Services.dirsvc.get(\"ProfD\", Ci.nsIFile);
 | 
						|
			Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/include.js\", this);
 | 
						|
			Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/customElements.js\", this);
 | 
						|
		} catch (e) {}' \
 | 
						|
		chrome/toolkit/content/global/commonDialog.xhtml
 | 
						|
	
 | 
						|
	# 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.js
 | 
						|
	
 | 
						|
	# Show Services menu on input fields
 | 
						|
	# (JS part of https://hg.mozilla.org/mozilla-central/rev/defc298879844981e7c1817f13e3fc2caf84f148 for input boxes)
 | 
						|
	replace_line '<menupopup class="textbox-contextmenu">' '<menupopup class="textbox-contextmenu" showservicesmenu="true">' chrome/toolkit/content/global/elements/moz-input-box.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 .
 | 
						|
		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=sha256sum
 | 
						|
			else
 | 
						|
				shasum=shasum
 | 
						|
			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
 | 
						|
	
 | 
						|
	if [ ! -e "Firefox $GECKO_VERSION.app.zip" ]; then
 | 
						|
		rm "Firefox.dmg"
 | 
						|
	fi
 | 
						|
	
 | 
						|
	#if [ ! -e "Firefox $GECKO_VERSION MacOS.zip" ]; then
 | 
						|
	#	rm "MacOS.zip"
 | 
						|
	#fi
 | 
						|
	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"
 | 
						|
	
 | 
						|
	for arch in win32 win-x64 win-aarch64; do
 | 
						|
		xdir=firefox-$arch
 | 
						|
		
 | 
						|
		rm -rf $xdir
 | 
						|
		mkdir $xdir
 | 
						|
		
 | 
						|
		if [ -e "Firefox Setup $GECKO_VERSION-$arch.exe" ]; then
 | 
						|
			echo "Using Firefox Setup $GECKO_VERSION-$arch.exe"
 | 
						|
			cp "Firefox Setup $GECKO_VERSION-$arch.exe" "Firefox%20Setup%20$GECKO_VERSION.exe"
 | 
						|
		else
 | 
						|
			if [ $arch = "win-x64" ]; then
 | 
						|
				curl -O "$DOWNLOAD_URL/win64/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
 | 
						|
			elif [ $arch = "win-aarch64" ]; then
 | 
						|
				curl -O "$DOWNLOAD_URL/win64-aarch64/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
 | 
						|
			else
 | 
						|
				curl -O "$DOWNLOAD_URL/$arch/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
 | 
						|
			fi
 | 
						|
		fi
 | 
						|
		
 | 
						|
		7z x Firefox%20Setup%20$GECKO_VERSION.exe -o$xdir 'core/*'
 | 
						|
		mv $xdir/core $xdir-core
 | 
						|
		rm -rf $xdir
 | 
						|
		mv $xdir-core $xdir
 | 
						|
		
 | 
						|
		pushd $xdir
 | 
						|
		
 | 
						|
		# 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. We could
 | 
						|
		# require binutils, which has GNU strings, but you need to build on Windows to make an
 | 
						|
		# installer, so a Windows build from macOS likely isn't being deployed, and there's no
 | 
						|
		# reason the Perl command above should fail anyway.)
 | 
						|
		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
 | 
						|
		
 | 
						|
		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
 | 
						|
	done
 | 
						|
	echo $("$SCRIPT_DIR/xulrunner_hash" -p w) > hash-win
 | 
						|
fi
 | 
						|
 | 
						|
if [ $BUILD_LINUX == 1 ]; then
 | 
						|
	GECKO_VERSION="$GECKO_VERSION_LINUX"
 | 
						|
	DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
 | 
						|
 | 
						|
 | 
						|
	# Include 32-bit build if not in CI
 | 
						|
	if [[ "${CI:-}" = "1" ]] || [[ "${SKIP_32:-}" = "1" ]]; then
 | 
						|
		arches="x86_64"
 | 
						|
	else
 | 
						|
		arches="i686 x86_64"
 | 
						|
	fi
 | 
						|
	for arch in $arches; do
 | 
						|
		xdir="firefox-$arch"
 | 
						|
		rm -rf $xdir
 | 
						|
		
 | 
						|
		archived_file="firefox-$GECKO_VERSION-$arch.tar.bz2"
 | 
						|
		if [ -e "$archived_file" ]; then
 | 
						|
			echo "Using $archived_file"
 | 
						|
			cp "$archived_file" "firefox-$GECKO_VERSION.tar.bz2"
 | 
						|
		else
 | 
						|
			curl -O "$DOWNLOAD_URL/linux-$arch/en-US/firefox-$GECKO_VERSION.tar.bz2"
 | 
						|
		fi
 | 
						|
 | 
						|
		tar xvf firefox-$GECKO_VERSION.tar.bz2
 | 
						|
		mv firefox firefox-$arch
 | 
						|
 | 
						|
		pushd firefox-$arch
 | 
						|
		modify_omni $arch
 | 
						|
		popd
 | 
						|
		echo $($SCRIPT_DIR/xulrunner_hash -p l) > hash-linux
 | 
						|
		rm "firefox-$GECKO_VERSION.tar.bz2"
 | 
						|
	done
 | 
						|
fi
 | 
						|
 | 
						|
echo Done
 |