Compare commits

..

No commits in common. "main" and "5.0.67.3" have entirely different histories.

3810 changed files with 133150 additions and 584266 deletions

View file

@ -7,12 +7,14 @@
"ignore": [
"chrome/content/zotero/include.js",
"chrome/content/zotero/xpcom/citeproc.js",
"chrome/content/ace/*",
"chrome/content/scaffold/templates/*",
"resource/react.js",
"resource/react-dom.js",
"resource/react-virtualized.js",
"test/resource/*.js"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
[
"transform-es2015-modules-commonjs",
{

18
.eslintignore Normal file
View file

@ -0,0 +1,18 @@
build/*
chrome/content/zotero/xpcom/citeproc.js
chrome/content/zotero/xpcom/isbn.js
chrome/content/zotero/xpcom/rdf/*
chrome/content/zotero/xpcom/xregexp/*
chrome/content/zotero/xpcom/translation/tlds.js
chrome/skin/default/zotero/timeline/bundle.js
chrome/skin/default/zotero/timeline/timeline-api.js
resource/bluebird/*
resource/classnames.js
resource/csl-validator.js
resource/jspath.js
resource/prop-types.js
resource/react*
resource/schema/connectorTypeSchemaData.js
resource/tinymce/*
test/resource/*
translators/*

44
.eslintrc Normal file
View file

@ -0,0 +1,44 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"globals": {
"Zotero": false,
"ZOTERO_CONFIG": false,
"AddonManager": false,
"assert": false,
"Cc": false,
"Ci": false,
"Components": false,
"ConcurrentCaller": false,
"ctypes": false,
"OS": false,
"PluralForm": false,
"Services": false,
"XPCOMUtils": false,
"XRegExp": false
},
"extends": [
"@zotero",
"plugin:react/recommended"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"plugins": [
"react"
],
"settings": {
"react": {
"version": "16.8"
}
},
"rules": {}
}

4
.gitattributes vendored
View file

@ -1,5 +1 @@
*.sql text eol=lf
app/win/zotero.exe.tar.xz filter=lfs diff=lfs merge=lfs -text
app/mac/updater.tar.xz filter=lfs diff=lfs merge=lfs -text
app/win/updater.exe.tar.xz filter=lfs diff=lfs merge=lfs -text
app/linux/updater.tar.xz filter=lfs diff=lfs merge=lfs -text

7
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,7 @@
**READ THIS BEFORE CREATING AN ISSUE**
Zotero does not use GitHub Issues for bug reports or feature requests.
Please post all such requests to the Zotero Forums at https://forums.zotero.org, where Zotero developers and many others can help. For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.
See https://www.zotero.org/support/zotero_support for more information on how Zotero support works.

View file

@ -1,439 +0,0 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
schedule:
# Monthly, to catch runner image and OS changes
- cron: '0 6 1 * *'
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
# Decide whether to run the network-filesystem and Windows tests: always for manual and
# scheduled runs, and otherwise only when the changes touch the Gecko version, the files
# responsible for network-filesystem database handling, or (for Windows) the files that
# make up the Windows build
changes:
name: Detect changes
runs-on: ubuntu-latest
if: >
github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository
outputs:
network: ${{ steps.check.outputs.network }}
windows: ${{ steps.check.outputs.windows }}
steps:
- name: Check for network-filesystem-related changes
id: check
env:
EVENT: ${{ github.event_name }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
BEFORE: ${{ github.event.before }}
BASE: ${{ github.event.pull_request.base.sha }}
run: |
network=false
windows=false
if [[ "$EVENT" == "workflow_dispatch" || "$EVENT" == "schedule" ]]; then
network=true
windows=true
else
base="$BASE"
[[ -z "$base" ]] && base="$BEFORE"
git init -q repo && cd repo
git remote add origin "https://github.com/$REPO"
git fetch -q --depth 1 origin "$SHA"
# Run the tests if the base of the change can't be determined (e.g., a new
# branch or an unfetchable pre-force-push commit)
if [[ -z "$base" || "$base" == 0000000000000000000000000000000000000000 ]] \
|| ! git fetch -q --depth 1 origin "$base" 2>/dev/null; then
network=true
windows=true
else
files=$(git diff --name-only "$base" "$SHA")
if grep -qE '^(app/config\.sh|chrome/content/zotero/xpcom/(db|file)\.js|\.github/workflows/ci\.yml)$' <<< "$files"; then
network=true
windows=true
fi
if grep -qE '^app/(win/|build\.sh$|scripts/(dir_build|fetch_xulrunner|prepare_build)$)' <<< "$files"; then
windows=true
fi
fi
fi
echo "network=$network" >> "$GITHUB_OUTPUT"
echo "windows=$windows" >> "$GITHUB_OUTPUT"
test:
name: Test (${{ matrix.shard }})
runs-on: ubuntu-latest
timeout-minutes: 30
needs: changes
# Same-repo PRs are covered by the push run, so run pull_request jobs only for forks
if: >
github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository
strategy:
matrix:
# Numbered shards split the full suite. 'smb' and 'nfs' run the db and file tests
# with the test data directory on a loopback CIFS or NFS mount, so that connection
# initialization itself runs against a network filesystem, which is where
# network-filesystem failures (startup crashes and hangs) occur. The network shards
# run only when the 'changes' job detects relevant changes.
shard: ${{ fromJSON(needs.changes.outputs.network == 'true'
&& '["1", "2", "3", "4", "smb", "nfs"]'
|| '["1", "2", "3", "4"]') }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
#cache: npm
# Local via act
- name: Install packages for act
if: env.ACT == 'true'
run: apt update && apt install -y zstd xvfb dbus-x11 libgtk-3-0 libx11-xcb1 libdbus-glib-1-2 libxt6
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/firefox-x86_64
key: xulrunner-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p l
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
# Create deployment ZIP from the build output, then replace build/ with the
# unzipped contents so that tests run against the same artifact that gets
# deployed. This catches problems that only manifest after a zip round-trip
# (e.g., resolved symlinks, missing files).
- name: Create deployment ZIP
run: |
cd build
zip -r ../build.zip *
cd ..
- name: Replace build with ZIP contents
run: |
rm -rf build
mkdir build
cd build
unzip ../build.zip
- name: Set up SMB share
if: matrix.shard == 'smb'
timeout-minutes: 10
run: |
sudo apt install -y --no-install-recommends samba cifs-utils
sudo mkdir -p /srv/zotero-smb /mnt/zotero-smb
sudo chown $(whoami) /srv/zotero-smb
sudo tee -a /etc/samba/smb.conf > /dev/null <<EOF
[ztest]
path = /srv/zotero-smb
read only = no
EOF
(echo smbtestpass; echo smbtestpass) | sudo smbpasswd -s -a $(whoami)
sudo systemctl restart smbd
sudo mount -t cifs //127.0.0.1/ztest /mnt/zotero-smb \
-o user=$(whoami),pass=smbtestpass,vers=3.0,uid=$(id -u),gid=$(id -g)
- name: Set up NFS share
if: matrix.shard == 'nfs'
timeout-minutes: 10
run: |
sudo apt install -y --no-install-recommends nfs-kernel-server
sudo mkdir -p /srv/zotero-nfs /mnt/zotero-nfs
sudo chown $(whoami) /srv/zotero-nfs
echo "/srv/zotero-nfs localhost(rw,no_subtree_check,insecure)" | sudo tee -a /etc/exports
sudo exportfs -ra
sudo mount -t nfs4 localhost:/srv/zotero-nfs /mnt/zotero-nfs
- name: Run tests
if: matrix.shard != 'smb' && matrix.shard != 'nfs'
run: xvfb-run test/runtests.sh -f -r 3 -p ${{ matrix.shard }}/4
# The step timeout turns a startup hang into a failure
- name: Run tests on network share
if: matrix.shard == 'smb' || matrix.shard == 'nfs'
timeout-minutes: 10
run: TMPDIR=/mnt/zotero-${{ matrix.shard }} xvfb-run test/runtests.sh -f -r 3 -b db file
# Run the db and file tests on macOS with the test data directory on a loopback NFS
# mount. On macOS, SQLite selects locking methods without shared-memory support on
# network filesystems, which is what the network-filesystem database handling has to
# detect and handle, so this runs the real code path that crashed on SMB/NFS/sshfs
# data directories.
test-mac:
name: Test (macOS NFS)
runs-on: macos-15
timeout-minutes: 30
needs: changes
if: >
needs.changes.outputs.network == 'true'
&& (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository)
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/Firefox.app
key: xulrunner-mac-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p m
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-mac-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
- name: Set up NFS share
run: |
sudo mkdir -p /private/var/zotero-nfs
sudo chown $(whoami) /private/var/zotero-nfs
echo "/private/var/zotero-nfs -mapall=$(whoami) localhost" | sudo tee /etc/exports
sudo nfsd enable || sudo nfsd start
sleep 2
showmount -e localhost
mkdir -p "$HOME/zotero-nfs"
sudo mount_nfs -o vers=3 localhost:/private/var/zotero-nfs "$HOME/zotero-nfs"
# The step timeout turns a startup hang into a failure
- name: Run tests on network share
timeout-minutes: 10
run: TMPDIR="$HOME/zotero-nfs" test/runtests.sh -f -r 3 -b db file
# Boot the Windows build -- which uses custom-built Firefox components (xul.dll) rather
# than stock Mozilla binaries -- and run the db and file tests. A Gecko bump commit
# updates the custom-component hashes in config.sh along with the version, so this
# tests each new set of custom components.
test-win:
name: Test (Windows ${{ matrix.arch }})
runs-on: ${{ matrix.os }}
timeout-minutes: 40
needs: changes
if: >
needs.changes.outputs.windows == 'true'
&& (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository)
strategy:
fail-fast: false
matrix:
include:
- arch: x64
os: windows-latest
- arch: arm64
os: windows-11-arm
defaults:
run:
shell: bash
steps:
# The repository contains symlinks, which Git checks out as plain files on Windows
# by default, breaking the build
- name: Enable symlinks
run: git config --global core.symlinks true
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
# Git Bash has unzip but not zip, which fetch_xulrunner needs to repack omni.ja
- name: Install zip
run: choco install zip --no-progress -y
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/firefox-win-${{ matrix.arch }}
key: xulrunner-win-${{ matrix.arch }}-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p w -a ${{ matrix.arch }}
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-win-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
# Committed symlinks point into node_modules, which doesn't exist at checkout time,
# so Git creates the directory ones with the wrong symlink type on Windows. Recreate
# them now that the targets exist.
- name: Recreate symlinks
run: |
git ls-files -s | awk '$1 == 120000 { print $4 }' | xargs rm
git checkout -- .
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
# The step timeout turns a startup hang into a failure
- name: Run tests
timeout-minutes: 15
run: test/runtests.sh -f -r 3 -b -x app/staging/Zotero_win-${{ matrix.arch }}/zotero.exe db file
utilities:
name: Utilities Tests
runs-on: ubuntu-latest
if: >
github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache utilities Node modules
id: utilities-node-cache
uses: actions/cache@v4
with:
path: chrome/content/zotero/xpcom/utilities/node_modules
key: utilities-node-modules-24-${{ hashFiles('chrome/content/zotero/xpcom/utilities/package-lock.json') }}
- name: Install utilities Node modules
if: steps.utilities-node-cache.outputs.cache-hit != 'true'
run: npm install --prefix chrome/content/zotero/xpcom/utilities
- name: Run utilities tests
run: |
npm test --prefix chrome/content/zotero/xpcom/utilities -- -j resource/schema/global/schema.json
# Build deployment ZIPs from main, version branches (e.g., 9.0, 10.0), and *-hotfix branches
deploy:
name: Build, Upload
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.repository == 'zotero/zotero'
steps:
- name: Check if deployment branch
id: check-deploy
if: env.ACT != 'true'
run: |
branch="${GITHUB_REF#refs/heads/}"
if [[ "$branch" == "main" || "$branch" =~ ^[0-9]+\.[0-9]+$ || "$branch" == *-hotfix ]]; then
echo "deploy=true" >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@v4
if: steps.check-deploy.outputs.deploy == 'true'
with:
submodules: recursive
lfs: true
- name: Install Node
if: steps.check-deploy.outputs.deploy == 'true'
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache Node modules
id: node-cache
if: steps.check-deploy.outputs.deploy == 'true'
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.check-deploy.outputs.deploy == 'true' && steps.node-cache.outputs.cache-hit != 'true'
run: npm install
- name: Build Zotero
if: steps.check-deploy.outputs.deploy == 'true'
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
- name: Create deployment ZIP
if: steps.check-deploy.outputs.deploy == 'true'
run: |
cd build
zip -r ../build.zip *
cd ..
- uses: ruby/setup-ruby@v1
if: steps.check-deploy.outputs.deploy == 'true'
with:
ruby-version: '3.3'
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Upload deployment ZIP
if: steps.check-deploy.outputs.deploy == 'true'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
mkdir build-zip
cp build.zip build-zip/$GITHUB_SHA.zip
gem install --no-document dpl -v '>= 2.0'
dpl s3 --bucket zotero-download --local_dir build-zip --upload_dir ci/client --acl public_read

3
.gitignore vendored
View file

@ -1,6 +1,5 @@
.DS_Store
node_modules
build/
build
.signatures.json
tmp
app/win/codesign

42
.gitmodules vendored
View file

@ -1,14 +1,14 @@
[submodule "translators"]
path = translators
url = https://github.com/zotero/translators.git
url = git://github.com/zotero/translators.git
branch = master
[submodule "chrome/content/zotero/locale/csl"]
path = chrome/content/zotero/locale/csl
url = https://github.com/citation-style-language/locales.git
url = git://github.com/citation-style-language/locales.git
branch = master
[submodule "styles"]
path = styles
url = https://github.com/zotero/bundled-styles.git
url = git://github.com/zotero/bundled-styles.git
branch = master
[submodule "test/resource/chai"]
path = test/resource/chai
@ -18,37 +18,7 @@
path = test/resource/mocha
url = https://github.com/mochajs/mocha.git
branch = master
[submodule "resource/schema/global"]
path = resource/schema/global
url = https://github.com/zotero/zotero-schema.git
[submodule "test/resource/chai-as-promised"]
path = test/resource/chai-as-promised
url = https://github.com/domenic/chai-as-promised.git
branch = master
[submodule "resource/SingleFile"]
path = resource/SingleFile
url = https://github.com/gildas-lormeau/SingleFile.git
[submodule "pdf-reader"]
path = reader
url = https://github.com/zotero/reader.git
branch = master
[submodule "pdf-worker"]
path = document-worker
url = https://github.com/zotero/document-worker.git
branch = master
[submodule "note-editor"]
path = note-editor
url = https://github.com/zotero/note-editor.git
branch = master
[submodule "chrome/content/zotero/xpcom/utilities"]
path = chrome/content/zotero/xpcom/utilities
url = https://github.com/zotero/utilities.git
[submodule "chrome/content/zotero/xpcom/translate"]
path = chrome/content/zotero/xpcom/translate
url = https://github.com/zotero/translate.git
[submodule "app/modules/zotero-word-for-mac-integration"]
path = app/modules/zotero-word-for-mac-integration
url = https://github.com/zotero/zotero-word-for-mac-integration.git
[submodule "app/modules/zotero-word-for-windows-integration"]
path = app/modules/zotero-word-for-windows-integration
url = https://github.com/zotero/zotero-word-for-windows-integration.git
[submodule "app/modules/zotero-libreoffice-integration"]
path = app/modules/zotero-libreoffice-integration
url = https://github.com/zotero/zotero-libreoffice-integration.git

54
.travis.yml Normal file
View file

@ -0,0 +1,54 @@
sudo: false
language: node_js
node_js:
- "8"
cache:
directories:
- "build"
- "node_modules"
env:
global:
secure: "NxvkbZ7/Op7BTGQRR3C4q8lLoO29f8WtyNN27NSH7AO3H0vBr1Vp5xO8gn+H2qHEug5HvM+YrZ/xAkNXaZVbOInmBmKVMxqVvdpKp9JM1Amf+gzsXWQphfySvs6iqzyP6cwU/jspdvX/WSakgU5v7PWXxtUIaKxdANt6Rw7W+Pc="
matrix:
- FX_VERSION="52.0.3"
matrix:
fast_finish: true
#allow_failures:
# - env: FX_CHANNEL="beta"
notifications:
email: false
addons:
apt:
packages:
- dbus-x11
install:
- if [ $FX_VERSION = "52.0.3" ]; then
wget -O tarball "https://archive.mozilla.org/pub/firefox/tinderbox-builds/mozilla-release-linux64-add-on-devel/1491732920/firefox-52.0.3.en-US.linux-x86_64-add-on-devel.tar.bz2";
fi
- tar xf tarball
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm i
- npm run build
- if [[ $TRAVIS_REPO_SLUG = "zotero/zotero" &&
($TRAVIS_BRANCH = "master" || $TRAVIS_BRANCH = *-hotfix) &&
$TRAVIS_PULL_REQUEST = "false" ]]; then
mkdir build-zip;
cd build;
zip -r ../build-zip/$TRAVIS_COMMIT.zip *;
cd ..;
gem install dpl;
dpl --provider=s3
--access-key-id=AKIAJFDVJ54MCAEXPQ5Q
--bucket=zotero-download
--local-dir=build-zip
--upload-dir=ci/client
--acl=public-read
--skip_cleanup=true;
fi
- unset AWS_SECRET_ACCESS_KEY
# Fix warnings in output
- dbus-launch
script:
- test/runtests.sh -x firefox/firefox -f

110
CLAUDE.md
View file

@ -1,110 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Zotero is a Firefox-based desktop research management application. It runs as a XUL/XHTML application on a Mozilla platform (not Electron, not a web app). The UI uses a mix of XUL/XHTML, custom elements (Web Components), and React components.
## Build & Development Commands
```bash
npm install # Install dependencies
npm run build # Full build (Babel transpile JS/JSX, compile SCSS, browserify, symlink)
npm run clean-build # Clean build output and rebuild
```
### Running Tests
Tests run inside a built Zotero application instance via `test/runtests.sh`:
```bash
test/runtests.sh # Run all tests
test/runtests.sh item # Run a single test file (omit "Test" and .js extension)
test/runtests.sh item collections # Run multiple test files
test/runtests.sh -g "pattern" # Run only tests matching a grep pattern
test/runtests.sh -f # Stop after first failure
test/runtests.sh -s item # Start at a specific test file
test/runtests.sh -e item # End at a specific test file
test/runtests.sh -c # Open JS console, don't quit on completion
test/runtests.sh -b # Skip bundled translator/style installation (for faster startup when not necessary)
test/runtests.sh -d 5 # Enable debug logging (level 1-5; default is 3)
```
Tests use Mocha + Chai (assert style) + Sinon. Test files live in `test/tests/` and follow the naming convention `<module>Test.js`. Test helpers are in `test/content/support.js`.
The test runner automatically triggers `npm run build` if the watch process isn't running, so it's not necessary to manually run a build before running tests.
Tips:
- Pass flags before test names (e.g., `test/runtests.sh -f item`).
- `-f` should almost always be used, since test failures can cause later spurious test failures.
- To view debug logging for a specific test when running multiple tests, add `Zotero.Debug.init(1)` at the beginning of the test and run with `-d 5`.
### Linting
```bash
npx eslint <file> # Lint a specific file
```
ESLint v9 flat config is in `eslint.config.mjs`. Uses `@zotero/eslint-config`, Babel parser, and React plugin. Currently, the code base is not fully linted, so only worry about issues with new or modified code lines.
## Architecture
### Core Layers
- **XPCOM modules** (`chrome/content/zotero/xpcom/`) -- Core business logic. Loaded sequentially by `chrome/content/zotero/zotero.mjs`. All modules attach to the global `Zotero` namespace (e.g., `Zotero.Items`, `Zotero.Sync.Runner`).
- **Data model** (`chrome/content/zotero/xpcom/data/`) -- ORM-like classes for database entities. `Zotero.DataObject` is the base class; `Zotero.Item`, `Zotero.Collection`, `Zotero.Search`, `Zotero.Library`, etc. extend it. Plural classes (`Zotero.Items`, `Zotero.Collections`) manage object caches and queries. Data objects use an async `saveTx()` pattern for persistence.
- **Database** (`chrome/content/zotero/xpcom/db.js`) -- SQLite via Mozilla's mozStorage API. Accessed through `Zotero.DB.queryAsync()`, `Zotero.DB.executeTransaction()`.
- **Sync system** (`chrome/content/zotero/xpcom/sync/`) -- `syncRunner.js` orchestrates sync. `syncEngine.js` handles object-level sync logic. `syncAPIClient.js` communicates with the Zotero API server. `syncLocal.js` manages local sync state. Similar `storage*` files for file syncing, along with zfs.js (Zotero Storage) and webdav.js (WebDAV).
- **HTTP server** (`chrome/content/zotero/xpcom/server/`) -- Local HTTP server for browser connector integration and local API.
### UI Layers
- **Main window**: `chrome/content/zotero/zoteroPane.xhtml` + `zoteroPane.js`
- **Custom elements** (`chrome/content/zotero/elements/`) -- XUL custom elements inheriting from `XULElementBase` (defined in `elements/base.js`), which provides lifecycle helpers (`init()`, `destroy()`, `content` getter for templates). Registered via `customElements.define()`.
- **React components** (`chrome/content/zotero/components/`) -- Used for complex interactive UI (tag selector, virtualized table, item tree, collection tree). Major tree views are `itemTree.jsx` and `collectionTree.jsx`.
- **SCSS styles** (`scss/`) -- Compiled to CSS. Platform-specific overrides in `scss/mac/`, `scss/win/`, `scss/linux/`.
- **Localization** -- Fluent (`.ftl` files) in `chrome/locale/en-US/zotero/`. Accessed via `data-l10n-id` attributes or `Zotero.getString()` for legacy `.properties` strings. All new strings must be added to `.ftl` files -- do not add new strings to `.properties` or `.dtd` files.
### Submodules
Several features are developed in separate repos and included as Git submodules:
- `reader/` -- PDF/EPUB/snapshot reader with annotations
- `note-editor/` -- Rich text note editor
- `document-worker/` -- PDF processing (extraction, manipulation)
- `translators/` -- 760+ web translators for importing metadata from websites
- `styles/` -- CSL citation styles
- `chrome/content/zotero/xpcom/utilities/` -- Shared utility library
- `chrome/content/zotero/xpcom/translate/` -- Translation framework
- `resource/SingleFile/` -- Web page archiving
### Build System (`js-build/`)
Custom Node.js build system (not Webpack). `js-build/config.js` defines what gets built:
- **JS/JSX files** in `chrome/`, `components/`, `defaults/`, `test/` are transpiled via Babel (React JSX, CommonJS modules)
- **SCSS files** in `scss/` and `chrome/skin/` are compiled with Dart Sass
- **Browserify** bundles select npm packages (react-select, sinon, url) for use in the XUL environment
- **Symlinks** connect source files and submodules into the `build/` directory
- The built app reads from `build/`, not directly from source
## Code Style
- Tabs for indentation, not spaces
- Use `let` instead of `const` except for true scalar constants (e.g., `const MAX_SECONDS = 5 * 60;`)
- No cuddled braces (opening brace on same line, but `else`/`catch`/etc. on their own line)
- Use two hyphens `--` in comments, not an em dash
- Indent blank lines to match surrounding indentation level
- Objects attach to the `Zotero` global namespace rather than using ES module exports
- Async code uses `async`/`await` throughout
- Mozilla/XPCOM APIs are available globally: `Cc`, `Ci`, `Cu`, `Cr`, `Services`, `ChromeUtils`, `IOUtils`, `PathUtils`
## Translators
See `translators/CLAUDE.md` for guidelines. Key rules: **never** generate translators from scratch, **never** generate UUIDs, **never** generate test cases -- all of these must be done through Zotero's Scaffold tool.

View file

@ -2,7 +2,7 @@
## Bug Reports and Feature Requests
Zotero does not use GitHub Issues for bug reports, feature requests, or support questions. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
Zotero does not use GitHub Issues for bug reports or feature requests. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. See [How Zotero Support Works](https://www.zotero.org/support/zotero_support) for more information.
For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories.

View file

@ -1,9 +1,9 @@
Zotero
======
[![CI](https://github.com/zotero/zotero/actions/workflows/ci.yml/badge.svg)](https://github.com/zotero/zotero/actions/workflows/ci.yml)
[![Build Status](https://travis-ci.org/zotero/zotero.svg?branch=master)](https://travis-ci.org/zotero/zotero)
[Zotero](https://www.zotero.org/) is a free, easy-to-use tool to help you collect, organize, cite, and share your research sources.
Please post feature requests or bug reports to the [Zotero Forums](https://forums.zotero.org/). If you're having trouble with Zotero, see [Getting Help](https://www.zotero.org/support/getting_help).
For more information on how to use this source code, see the [Zotero documentation](https://www.zotero.org/support/dev/source_code).
For more information on how to use this source code, see the [Zotero wiki](https://www.zotero.org/support/dev/source_code).

12
app/.gitignore vendored
View file

@ -1,12 +0,0 @@
*~
cache
config-custom.sh
dist
lastrev
staging
tmp
xulrunner
win/resource_hacker
win/firefox-*.win32.zip
win/firefox-*.win64.zip
win/firefox-*.win64-aarch64.zip

View file

@ -1,4 +0,0 @@
# Zotero Standalone build utility
These files are used to bundle the [Zotero core](https://github.com/zotero/zotero) into distributable bundles for Mac, Windows, and Linux.
Instructions for building and packaging are available on the [Zotero wiki](https://www.zotero.org/support/dev/client_coding/building_the_standalone_client).

View file

@ -1,18 +0,0 @@
[App]
Vendor=Zotero
Name=Zotero
Version={{VERSION}}
BuildID={{BUILDID}}
Copyright=Copyright (c) 2006-2025 Contributors
ID=zotero@zotero.org
[Gecko]
MinVersion=153.0
MaxVersion=153.99.*
[XRE]
EnableExtensionManager=1
EnableProfileMigrator=1
[AppUpdate]
URL=https://www.zotero.org/download/client/update/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/update.xml

View file

@ -1 +0,0 @@
<!ENTITY brandShortName "Zotero">

View file

@ -1,8 +0,0 @@
-brand-shorter-name = Zotero
-brand-short-name = Zotero
-brand-full-name = Zotero
-brand-product-name = Zotero
-vendor-short-name = Zotero
-app-name = Zotero
-subscription-name = Zotero Storage
trademarkInfo = Zotero is a trademark of the Corporation for Digital Scholarship.

View file

@ -1,3 +0,0 @@
brandShorterName=Zotero
brandShortName=Zotero
brandFullName=Zotero

View file

@ -1,3 +0,0 @@
locale branding en-US chrome/en-US/locale/branding/
content branding chrome/branding/content/
skin browser preferences chrome/skin/

View file

@ -1,104 +0,0 @@
let { CommandLineOptions, TestOptions } = ChromeUtils.importESModule("chrome://zotero/content/modules/commandLineOptions.mjs");
// Only allow BrowserContentHandler to open a new window if this is the initial launch,
// meaning our CLH isn't registered yet.
if (cmdLine.state != Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
cmdLine.preventDefault = true;
}
// Force debug output to window
if (cmdLine.handleFlag("ZoteroDebug", false)) {
CommandLineOptions.forceDebugLog = 2;
}
// Force debug output to text console
else if (cmdLine.handleFlag("ZoteroDebugText", false)) {
CommandLineOptions.forceDebugLog = 1;
}
// Pressing Ctrl-C via the terminal is interpreted as a crash, and after three crashes
// Firefox starts up in automatic safe mode (troubleshooting mode). To avoid this, we clear the crash
// counter when using one of the debug-logging flags, which generally imply terminal usage.
if (CommandLineOptions.forceDebugLog) {
Services.prefs.getBranch("toolkit.startup.").clearUserPref("recent_crashes");
}
CommandLineOptions.forceDataDir = cmdLine.handleFlagWithParam("datadir", false);
// Set here, to be acted upon in xpcom/commandLineHandler.js
CommandLineOptions.file = cmdLine.handleFlagWithParam("file", false);
CommandLineOptions.url = cmdLine.handleFlagWithParam("url", false);
if (CommandLineOptions.url) {
CommandLineOptions.url = cmdLine.resolveURI(CommandLineOptions.url);
}
var processTestOptions = false;
if (cmdLine.handleFlag("ZoteroTest", false)) {
CommandLineOptions.test = true;
processTestOptions = true;
}
if (cmdLine.handleFlag("ZoteroAutomatedTest", false)) {
CommandLineOptions.automatedTest = true;
}
if (cmdLine.handleFlag("ZoteroSkipBundledFiles", false)) {
CommandLineOptions.skipBundledFiles = true;
}
if (processTestOptions) {
TestOptions.tests = cmdLine.handleFlagWithParam("test", false);
TestOptions.noquit = cmdLine.handleFlag("noquit", false);
TestOptions.makeTestData = cmdLine.handleFlag("makeTestData", false);
TestOptions.noquit = !TestOptions.makeTestData && TestOptions.noquit;
TestOptions.runTests = !TestOptions.makeTestData;
TestOptions.bail = cmdLine.handleFlag("bail", false);
TestOptions.startAt = cmdLine.handleFlagWithParam("startAtTestFile", false);
TestOptions.stopAt = cmdLine.handleFlagWithParam("stopAtTestFile", false);
TestOptions.shard = cmdLine.handleFlagWithParam("shard", false);
TestOptions.grep = cmdLine.handleFlagWithParam("grep", false);
TestOptions.timeout = cmdLine.handleFlagWithParam("ZoteroTestTimeout", false);
TestOptions.retries = cmdLine.handleFlagWithParam("retries", false) || 0;
Services.ww.openWindow(
null,
"chrome://zotero-unit/content/runtests.html",
"_blank",
"chrome,dialog=no,all",
Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray)
);
cmdLine.preventDefault = true;
}
if (cmdLine.handleFlag("debugger", false)) {
(async function () {
try {
let portOrPath = Services.prefs.getBranch('').getIntPref('devtools.debugger.remote-port');
const { DevToolsLoader } = ChromeUtils.importESModule(
"resource://devtools/shared/loader/Loader.sys.mjs"
);
const loader = new DevToolsLoader({
freshCompartment: true,
});
const { DevToolsServer } = loader.require("devtools/server/devtools-server");
const { SocketListener } = loader.require("devtools/shared/security/socket");
if (DevToolsServer.initialized) {
dump("Debugger server already initialized\n\n");
return;
}
DevToolsServer.init();
DevToolsServer.registerAllActors();
DevToolsServer.allowChromeProcess = true;
const socketOptions = { portOrPath };
const listener = new SocketListener(DevToolsServer, socketOptions);
await listener.open();
if (!DevToolsServer.listeningSockets) {
throw new Error("No listening sockets");
}
dump(`Debugger server started on ${portOrPath}\n\n`);
}
catch (e) {
dump(e + "\n\n");
Components.utils.reportError(e);
}
})();
}

View file

@ -1,242 +0,0 @@
/*
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Firefox Preferences System.
#
# The Initial Developer of the Original Code is
# Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
# Kevin Gerich <webmail@kmgerich.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
*/
.windowDialog {
padding: 12px;
font: -moz-dialog;
}
.paneSelector {
list-style-image: url("chrome://browser/skin/preferences/Options.png");
}
/* ----- GENERAL BUTTON ----- */
radio[pane=paneGeneral],
radio[pane=paneMain] {
-moz-image-region: rect(0px, 32px, 32px, 0px);
}
/* ----- TABS BUTTON ----- */
radio[pane=paneTabs] {
-moz-image-region: rect(0px, 64px, 32px, 32px);
}
/* ----- CONTENT BUTTON ----- */
radio[pane=paneContent] {
-moz-image-region: rect(0px, 96px, 32px, 64px);
}
/* ----- APPLICATIONS BUTTON ----- */
radio[pane=paneApplications] {
-moz-image-region: rect(0px, 128px, 32px, 96px);
}
/* ----- PRIVACY BUTTON ----- */
radio[pane=panePrivacy] {
-moz-image-region: rect(0px, 160px, 32px, 128px);
}
/* ----- SECURITY BUTTON ----- */
radio[pane=paneSecurity] {
-moz-image-region: rect(0px, 192px, 32px, 160px);
}
/* ----- ADVANCED BUTTON ----- */
radio[pane=paneAdvanced] {
-moz-image-region: rect(0px, 224px, 32px, 192px);
}
/* ----- SYNC BUTTON ----- */
radio[pane=paneSync] {
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
}
/* ----- APPLICATIONS PREFPANE ----- */
#BrowserPreferences[animated="true"] #handlersView {
height: 25em;
}
#BrowserPreferences[animated="false"] #handlersView {
-moz-box-flex: 1;
}
description {
font: small-caption;
font-weight: normal;
line-height: 1.3em;
margin-bottom: 4px !important;
}
prefpane .groupbox-body {
-moz-appearance: none;
padding: 8px 4px 4px 4px;
}
#paneTabs > groupbox {
margin: 0;
}
#tabPrefsBox {
margin: 12px 4px;
}
prefpane .groupbox-title {
background: url("chrome://global/skin/50pct_transparent_grey.png") repeat-x bottom left;
margin-bottom: 4px;
}
tabpanels {
padding: 20px 7px 7px;
}
caption {
-moz-padding-start: 5px;
padding-top: 4px;
padding-bottom: 2px;
}
#paneMain description,
#paneContent description,
#paneAdvanced description,
#paneSecurity description {
font: -moz-dialog;
}
#paneContent {
padding-top: 8px;
}
#paneContent row {
padding: 2px 4px;
-moz-box-align: center;
}
#popupPolicyRow,
#enableSoftwareInstallRow,
#enableImagesRow {
margin-bottom: 4px !important;
padding-bottom: 4px !important;
border-bottom: 1px solid #ccc;
}
#browserUseCurrent,
#browserUseBookmark,
#browserUseBlank {
margin-top: 10px;
}
#advancedPrefs {
margin: 0 8px;
}
#privacyPrefs {
padding: 0 4px;
}
#privacyPrefs > tabpanels {
padding: 18px 10px 10px;
}
#OCSPDialogPane {
font: message-box !important;
}
/**
* Privacy Pane
*/
/* styles for the link elements copied from .text-link in global.css */
.inline-link {
color: LinkText;
text-decoration: underline;
}
.inline-link:not(:focus) {
outline: 1px dotted transparent;
}
/**
* Update Preferences
*/
#autoInstallOptions {
-moz-margin-start: 20px;
}
.updateControls {
-moz-margin-start: 10px;
}
/**
* Clear Private Data
*/
#SanitizeDialogPane > groupbox {
margin-top: 0;
}
/* ----- SYNC PANE ----- */
#syncDesc {
padding: 0 8em;
}
#accountCaptionImage {
list-style-image: url("chrome://mozapps/skin/profile/profileicon.png");
}
#syncAddDeviceLabel {
margin-top: 1em;
margin-bottom: 1em;
}
#syncEnginesList {
height: 10em;
}

View file

@ -1 +0,0 @@
en-US,ar,bg-BG,br,ca-AD,cs-CZ,da-DK,de,el-GR,en-AU,en-CA,en-GB,en-NZ,es-ES,et-EE,eu-ES,fa,fi-FI,fr-FR,gl-ES,hu-HU,id-ID,is-IS,it-IT,ja-JP,km,ko-KR,lt-LT,nb-NO,nl-NL,pl-PL,pt-BR,pt-PT,ro-RO,ru-RU,sk-SK,sl-SI,sr-RS,sv-SE,ta,th-TH,tr-TR,uk-UA,vi-VN,zh-CN,zh-TW

View file

@ -1,172 +0,0 @@
// We only want a single window, I think
pref("toolkit.singletonWindowType", "navigator:browser");
// For debugging purposes, show errors in console by default
pref("javascript.options.showInConsole", true);
// Don't retrieve unrequested links when performing standalone translation
pref("network.prefetch-next", false);
// Don't make DNS requests for links in snapshots
pref("network.dns.disablePrefetch", true);
// Don't open TCP connection when clicking on a link in a snapshot
pref("network.http.speculative-parallel-limit", 0);
// Let operations run as long as necessary
pref("dom.max_chrome_script_run_time", 0);
pref("intl.locale.requested", '');
pref("intl.regional_prefs.use_os_locales", false);
// Fix error initializing login manager after this was changed in Firefox 57
// Could also disable this with MOZ_LOADER_SHARE_GLOBAL, supposedly
pref("jsloader.shareGlobal", false);
// Needed due to https://bugzilla.mozilla.org/show_bug.cgi?id=1181977
pref("browser.hiddenWindowChromeURL", "chrome://zotero/content/standalone/hiddenWindow.xhtml");
// Use basicViewer for opening new DOM windows from content (for TinyMCE)
pref("browser.chromeURL", "chrome://zotero/content/standalone/basicViewer.xhtml");
// We need these to get the save dialog working with contentAreaUtils.js
pref("browser.download.useDownloadDir", false);
pref("browser.download.manager.showWhenStarting", false);
pref("browser.download.folderList", 1);
// Don't show add-on selection dialog
pref("extensions.shownSelectionUI", true);
pref("extensions.autoDisableScope", 11);
pref("network.protocol-handler.expose-all", false);
pref("network.protocol-handler.expose.zotero", true);
pref("network.protocol-handler.expose.http", true);
pref("network.protocol-handler.expose.https", true);
// Never go offline
pref("offline.autoDetect", false);
pref("network.manage-offline-status", false);
// Without this, we will throw up dialogs if asked to translate strange pages
pref("browser.xul.error_pages.enabled", true);
// Without this, scripts may decide to open popups
pref("dom.disable_open_during_load", true);
// Don't show security warning. The "warn_viewing_mixed" warning just lets the user know that some
// page elements were loaded over an insecure connection. This doesn't matter if all we're doing is
// scraping the page, since we don't provide any information to the site.
pref("security.warn_viewing_mixed", false);
// Temporarily allow plugins to load their own scripts from jar:file: URIs with loadSubScript()
// without passing allowUnsafeURL (Mozilla bug 1974213). This will be disabled once plugins have
// had time to update.
pref("security.allow_unsafe_subscript_loads", true);
// We do need synchronous XHR
pref("network.xhr.block_sync_system_requests", false);
// Preferences for add-on discovery
pref("extensions.getAddons.cache.enabled", false);
//pref("extensions.getAddons.maxResults", 15);
//pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/search/guid:%IDS%?src=thunderbird&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
//pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/%APP%/search?q=%TERMS%");
//pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%?src=thunderbird");
//pref("extensions.webservice.discoverURL", "https://www.zotero.org/support/plugins");
// Check Windows certificate store for custom CAs
pref("security.enterprise_roots.enabled", true);
// Disable add-on signature checking with unbranded Firefox build
pref("xpinstall.signatures.required", false);
// Allow legacy extensions (though this might not be necessary)
pref("extensions.legacy.enabled", true);
// Allow installing XPIs from any host
pref("xpinstall.whitelist.required", false);
// Allow installing XPIs when using a custom CA
pref("extensions.install.requireBuiltInCerts", false);
pref("extensions.update.requireBuiltInCerts", false);
// Don't connect to the Mozilla extensions blocklist
pref("extensions.blocklist.enabled", false);
// Avoid warning in console when opening Tools -> Add-ons
pref("extensions.getAddons.link.url", "");
// Disable places
pref("places.history.enabled", false);
// Probably not used, but prevent an error in the console
pref("app.support.baseURL", "https://www.zotero.org/support/");
// Disable Telemetry, Health Report, error reporting, and remote settings
pref("toolkit.telemetry.unified", false);
pref("toolkit.telemetry.enabled", false);
pref("datareporting.policy.dataSubmissionEnabled", false);
pref("toolkit.crashreporter.enabled", false);
pref("extensions.remoteSettings.disabled", true);
pref("extensions.update.url", "");
// Don't try to load the "Get Add-ons" tab on first load of Add-ons window
pref("extensions.ui.lastCategory", "addons://list/extension");
// Don't show "Using experimental APIs requires a privileged add-on" warning
pref("extensions.experiments.enabled", true);
// Not set on Windows in Firefox anymore since it's a per-installation pref,
// but we override that in fetch_xulrunner
pref("app.update.auto", true);
// URL user can browse to manually if for some reason all update installation
// attempts fail.
pref("app.update.url.manual", "https://www.zotero.org/download");
// A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "https://www.zotero.org/support/changelog");
// Interval: Time between checks for a new version (in seconds)
// default=1 day
pref("app.update.interval", 86400);
// The minimum delay in seconds for the timer to fire.
// default=2 minutes
pref("app.update.timerMinimumDelay", 120);
// Whether or not we show a dialog box informing the user that the update was
// successfully applied. This is off in Firefox by default since we show a
// upgrade start page instead! Other apps may wish to show this UI, and supply
// a whatsNewURL field in their brand.properties that contains a link to a page
// which tells users what's new in this new update.
// update channel for this build
pref("app.update.channel", "default");
// This should probably not be a preference that's used in toolkit....
pref("browser.preferences.instantApply", false);
// Allow elements to be displayed full-screen
pref("full-screen-api.enabled", true);
// Allow chrome access in DevTools
// This enables the input field in the Browser Console tool
pref("devtools.chrome.enabled", true);
// Default mousewheel action with Alt/Option is History Back/Forward in Firefox
// We don't have History navigation and users want to scroll the tree with Option
// key held down
pref("mousewheel.with_alt.action", 1);
// Use the system print dialog instead of the new tab-based print dialog in Firefox
pref("print.prefer_system_dialog", true);
// Disable libvpx decoding/encoding
pref("media.webm.enabled", false);
pref("media.encoder.webm.enabled", false);
pref("media.mediasource.webm.enabled", false);
pref("media.mediasource.webm.audio.enabled", false);
pref("media.mediasource.vp9.enabled", false);
pref("media.ffvpx.enabled", false);
pref("media.ffvpx.mp3.enabled", false);
pref("media.rdd-vpx.enabled", false);
pref("media.rdd-ffvpx.enabled", false);
pref("media.utility-ffvpx.enabled", false);
// Allow collectionTree scrolling when Control is highlighting collections on win
pref("mousewheel.with_control.action", 1);

View file

@ -1,188 +0,0 @@
%if 0
/*
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Firefox Preferences System.
#
# The Initial Developer of the Original Code is
# Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
*/
%endif
/* Global Styles */
#BrowserPreferences radio[pane] {
list-style-image: url("chrome://browser/skin/preferences/Options.png");
}
radio[pane=paneMain] {
-moz-image-region: rect(0px, 32px, 32px, 0px)
}
radio[pane=paneTabs] {
-moz-image-region: rect(0px, 64px, 32px, 32px)
}
radio[pane=paneContent] {
-moz-image-region: rect(0px, 96px, 32px, 64px)
}
radio[pane=paneApplications] {
-moz-image-region: rect(0px, 128px, 32px, 96px)
}
radio[pane=panePrivacy] {
-moz-image-region: rect(0px, 160px, 32px, 128px)
}
radio[pane=paneSecurity] {
-moz-image-region: rect(0px, 192px, 32px, 160px)
}
radio[pane=paneAdvanced] {
-moz-image-region: rect(0px, 224px, 32px, 192px)
}
%ifdef MOZ_SERVICES_SYNC
radio[pane=paneSync] {
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png") !important;
}
%endif
/* Applications Pane */
#BrowserPreferences[animated="true"] #handlersView {
height: 25em;
}
#BrowserPreferences[animated="false"] #handlersView {
-moz-box-flex: 1;
}
/* Privacy Pane */
/* styles for the link elements copied from .text-link in global.css */
.inline-link {
color: LinkText;
text-decoration: underline;
}
.inline-link:not(:focus) {
outline: 1px dotted transparent;
}
/* Modeless Window Dialogs */
.windowDialog,
.windowDialog prefpane {
padding: 0px;
}
.contentPane {
margin: 9px 8px 5px 8px;
}
.actionButtons {
margin: 0px 3px 6px 3px !important;
}
/* Cookies Manager */
#cookiesChildren::-moz-tree-image(domainCol) {
width: 16px;
height: 16px;
margin: 0px 2px;
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
#paneApplications {
margin-left: 4px;
margin-right: 4px;
padding-left: 0;
padding-right: 0;
}
#linksOpenInBox {
margin-top: 5px;
}
#paneAdvanced {
padding-bottom: 10px;
}
#advancedPrefs {
margin-left: 0;
margin-right: 0;
}
#cookiesChildren::-moz-tree-image(domainCol, container) {
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
}
#cookieInfoBox {
border: 1px solid ThreeDShadow;
border-radius: 0px;
margin: 4px;
padding: 0px;
}
/* bottom-most box containing a groupbox in a prefpane. Prevents the bottom
of the groupbox from being cutoff */
.bottomBox {
padding-bottom: 4px;
}
/**
* Clear Private Data
*/
#SanitizeDialogPane > groupbox {
margin-top: 0;
}
%ifdef MOZ_SERVICES_SYNC
/* Sync Pane */
#syncDesc {
padding: 0 8em;
}
#accountCaptionImage {
list-style-image: url("chrome://mozapps/skin/profile/profileicon.png");
}
#syncAddDeviceLabel {
margin-top: 1em;
margin-bottom: 1em;
}
#syncEnginesList {
height: 10em;
}
%endif

View file

@ -1,4 +0,0 @@
; This file is in the UTF-8 encoding
[Strings]
Title=Zotero Update
Info=Zotero is installing your updates and will start in a few moments…

View file

@ -1,178 +0,0 @@
/*
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Firefox Preferences System.
#
# The Initial Developer of the Original Code is
# Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
*/
/* Global Styles */
#BrowserPreferences radio[pane] {
list-style-image: url("chrome://browser/skin/preferences/Options.png");
padding: 5px 3px 1px;
}
radio[pane=paneMain] {
-moz-image-region: rect(0, 32px, 32px, 0);
}
radio[pane=paneTabs] {
-moz-image-region: rect(0, 64px, 32px, 32px);
}
radio[pane=paneContent] {
-moz-image-region: rect(0, 96px, 32px, 64px);
}
radio[pane=paneApplications] {
-moz-image-region: rect(0, 128px, 32px, 96px);
}
radio[pane=panePrivacy] {
-moz-image-region: rect(0, 160px, 32px, 128px);
}
radio[pane=paneSecurity] {
-moz-image-region: rect(0, 192px, 32px, 160px);
}
radio[pane=paneAdvanced] {
-moz-image-region: rect(0, 224px, 32px, 192px);
}
%ifdef MOZ_SERVICES_SYNC
radio[pane=paneSync] {
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png") !important;
}
%endif
/* Applications Pane */
#BrowserPreferences[animated="true"] #handlersView {
height: 25em;
}
#BrowserPreferences[animated="false"] #handlersView {
-moz-box-flex: 1;
}
/* Privacy Pane */
/* styles for the link elements copied from .text-link in global.css */
.inline-link {
color: LinkText;
text-decoration: underline;
}
.inline-link:not(:focus) {
outline: 1px dotted transparent;
}
/* Modeless Window Dialogs */
.windowDialog,
.windowDialog prefpane {
padding: 0;
}
.contentPane {
margin: 9px 8px 5px;
}
.actionButtons {
margin: 0 3px 6px !important;
}
/* Cookies Manager */
#cookiesChildren::-moz-tree-image(domainCol) {
width: 16px;
height: 16px;
margin: 0 2px;
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png") !important;
}
#cookiesChildren::-moz-tree-image(domainCol, container) {
list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#cookiesChildren::-moz-tree-image(domainCol, container, open) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
#cookieInfoBox {
border: 1px solid ThreeDShadow;
border-radius: 0;
margin: 4px;
padding: 0;
}
/* Advanced Pane */
/* Adding padding-bottom prevents the bottom of the tabpanel from being cutoff
when browser.preferences.animateFadeIn = true */
#advancedPrefs {
padding-bottom: 8px;
}
/* bottom-most box containing a groupbox in a prefpane. Prevents the bottom
of the groupbox from being cutoff */
.bottomBox {
padding-bottom: 4px;
}
%ifdef MOZ_SERVICES_SYNC
/* Sync Pane */
#syncDesc {
padding: 0 8em;
}
.syncGroupBox {
padding: 10px;
}
#accountCaptionImage {
list-style-image: url("chrome://mozapps/skin/profile/profileicon.png");
}
#syncAddDeviceLabel {
margin-top: 1em;
margin-bottom: 1em;
}
#syncEnginesList {
height: 11em;
}
%endif

File diff suppressed because it is too large Load diff

View file

@ -1,73 +0,0 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Version of Gecko to build with
GECKO_VERSION_MAC="153.3.0esr"
GECKO_VERSION_LINUX="153.3.0esr"
GECKO_VERSION_WIN="153.3.0esr"
RUST_VERSION=1.94.0
# URL prefix for custom builds of Firefox components
custom_components_url="https://download.zotero.org/dev/firefox-components/"
custom_components_hash_mac="6b586ed383a480fed9d9786ad18a6e6d90b140739e5e4491096df291ec887571"
custom_components_hash_win_x64="db7c9556bd0f9e36120069391ce66cc199ed1ec4e2fdd3b931f71e35b4586d2b"
custom_components_hash_win_arm64="fd0f44b38a471009ce1cdd926f7f48be43ed773dad350c62383b8a7235da8711"
custom_components_hash_win32="694944349fdfd91296f272a68ac5d03183f02d47fdaa7ba9f2b1c74747652aa2"
APP_NAME="Zotero"
APP_ID="zotero\@zotero.org"
# Whether to sign builds
SIGN=0
# OS X Developer ID certificate information
DEVELOPER_ID=F0F1FE48DB909B263AC51C8215374D87FDC12121
# Keychain and keychain password, if not building via the GUI
KEYCHAIN=""
KEYCHAIN_PASSWORD=""
NOTARIZATION_BUNDLE_ID=""
NOTARIZATION_USER=""
NOTARIZATION_TEAM_ID=""
NOTARIZATION_PASSWORD=""
# Name of a notarytool keychain profile (see `xcrun notarytool store-credentials`), e.g., for an
# App Store Connect API key -- used instead of the Apple ID settings above if set
NOTARIZATION_PROFILE=""
# Paths for Windows installer build
NSIS_DIR='C:\Program Files (x86)\NSIS\'
SIGNTOOL_DELAY=5
# Directory for unpacked binaries
STAGE_DIR="$DIR/staging"
# Directory for packed binaries
DIST_DIR="$DIR/dist"
SOURCE_REPO_URL="https://github.com/zotero/zotero"
S3_BUCKET="zotero-download"
S3_CI_ZIP_PATH="ci/client"
S3_DIST_PATH="client"
DEPLOY_HOST="deploy.zotero"
DEPLOY_PATH="www/www-production/public/download/client"
BUILD_PLATFORMS=""
NUM_INCREMENTALS=6
if [ "`uname`" = "Darwin" ]; then
shopt -s expand_aliases
fi
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
export WIN_NATIVE=1
else
export WIN_NATIVE=0
fi
# Make utilities (mar/mbsdiff) available in the path
PATH="$DIR/xulrunner/bin:$PATH"
if [ -f "$DIR/config-custom.sh" ]; then
. "$DIR/config-custom.sh"
fi
unset DIR

View file

@ -1,44 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Usage: $0 x64|arm64" >&2
exit 1
fi
gecko_path=~/src/firefox
arch=$1
if [ ! -d "$gecko_path" ]; then
echo "$gecko_path is not a directory" >&2
exit 1
fi
# Install required Rust version
rustup toolchain install $RUST_VERSION
if [ $arch = "x64" ]; then
rust_target=x86_64
elif [ $arch = "arm64" ]; then
rust_target=aarch64
else
echo "Unknown architecture $arch" >&2
exit 1
fi
rustup target add $rust_target-unknown-linux-gnu
rm "$gecko_path/mozconfig"
touch "$gecko_path/mozconfig"
if [ $arch == "arm64" ]; then
echo "ac_add_options --target=aarch64-linux-gnu" >> "$gecko_path/mozconfig"
fi
cat "$SCRIPT_DIR/mozconfig" >> "$gecko_path/mozconfig"
cd "$gecko_path"
./mach build
./mach package

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<polygon style="fill: #000000" points="13.863 2.73 13.027 1 2.137 1 2.137 3.8 2.137 3.921 8.822 3.921 1.289 13.233 2.137 15 13.863 15 13.863 12.142 13.863 12.021 6.448 12.021 13.863 2.73"/>
</svg>

Before

Width:  |  Height:  |  Size: 283 B

View file

@ -1,25 +0,0 @@
ac_add_options --enable-bootstrap
mk_add_options AUTOCLOBBER=1
# These don't all affect the stub, but they can't hurt, and we'll want them if
# we switch to custom XUL builds
ac_add_options MOZ_ENABLE_JS_DUMP=1
ac_add_options MOZ_ENABLE_FORKSERVER=
ac_add_options MOZ_TELEMETRY_REPORTING=
ac_add_options MOZ_DATA_REPORTING=
ac_add_options --disable-tests
ac_add_options --disable-debug
ac_add_options --disable-debug-symbols
ac_add_options --disable-webrtc
# This causes aarch64 build of 140.0esr to fail
#ac_add_options --disable-eme
export MOZILLA_OFFICIAL=1
export RELEASE_OR_BETA=1
MOZ_REQUIRE_SIGNING=
ac_add_options --enable-official-branding
# Build updater without MAR signature verification
ac_add_options --enable-unverified-updates

View file

@ -1,13 +0,0 @@
#!/bin/bash -e
#
# Run this to update the launcher file with the current path to the application icon
#
APPDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ -w "$APPDIR"/zotero.desktop ]; then
sed -i -e "s@^Icon=.*@Icon=$APPDIR/icons/icon128.png@" "$APPDIR"/zotero.desktop
else
echo "$APPDIR"/zotero.desktop is not writable
exit 1
fi

View file

@ -1,43 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"
if [ $# -ne 1 ]; then
echo "Usage: $0 <firefox-version>"
exit 1
fi
version="$1"
for arch in x86_64 aarch64; do
package="firefox-${version}.en-US.linux-${arch}.tar.xz"
if [ $arch = 'aarch64' ]; then
arch="arm64"
fi
if [ ! -f "$package" ]; then
echo "Error: Package not found: $package"
exit 1
fi
echo "Extracting updater from $package..."
tar --extract --xz --file="$package" --to-stdout "firefox/updater" > "updater-$arch"
# Add missing RUNPATH to custom fx140 builds, which is missing for unclear reasons.
# Without this, the updater fails looking for libmozsqlite3.so.
patchelf --set-rpath '$ORIGIN' "updater-$arch"
done
echo "Creating updater.tar.xz..."
if [ `uname` = "Darwin" ]; then
tar=gtar
else
tar=tar
fi
$tar --owner=0 --group=0 --numeric-owner --no-xattrs --mode=0755 -cJvf updater.tar.xz updater-x86_64 updater-arm64
rm updater-x86_64 updater-arm64
echo "Done: updater.tar.xz created"

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a1f368bb36408d61d4b8f9dda561738686e626513346e138d55a0ef58055a6fc
size 123776

View file

@ -1,20 +0,0 @@
#!/bin/bash
# Increase open files limit
#
# Mozilla file functions (OS.File.move()/copy(), NetUtil.asyncFetch/asyncCopy()) can leave file
# descriptors open for a few seconds (even with an explicit inputStream.close() in the case of
# the latter), so a source installation that copies ~500 translators and styles (with fds for
# source and target) can exceed the default 1024 limit.
# Current hard-limit on Ubuntu 16.10 is 4096
ulimit -n 4096
# Allow profile downgrade for Zotero
export MOZ_ALLOW_DOWNGRADE=1
# Don't create dedicated profile (default-esr)
export MOZ_LEGACY_PROFILES=1
# Use wayland backend when available
export MOZ_ENABLE_WAYLAND=1
CALLDIR="$(dirname "$(readlink -f "$0")")"
"$CALLDIR/zotero-bin" -app "$CALLDIR/app/application.ini" "$@"

View file

@ -1,9 +0,0 @@
[Desktop Entry]
Name=Zotero
Exec=bash -c "$(dirname $(realpath $(echo %k | sed -e 's/^file:\\/\\///')))/zotero -url %U"
Icon=zotero.ico
Type=Application
Terminal=false
Categories=Office;
MimeType=text/plain;x-scheme-handler/zotero;application/x-research-info-systems;text/x-research-info-systems;text/ris;application/x-endnote-refer;application/x-inst-for-Scientific-info;application/mods+xml;application/rdf+xml;application/x-bibtex;text/x-bibtex;application/marc;application/vnd.citationstyles.style+xml
X-GNOME-SingleWindow=true

View file

@ -1,204 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<!-- Import formats -->
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ris</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-research-info-systems</string>
<string>text/x-research-info-systems</string>
<string>text/ris</string>
<string>ris</string>
<string>application/x-endnote-refer</string>
</array>
<key>CFBundleTypeName</key>
<string>Research Information Systems Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ciw</string>
<string>isi</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-inst-for-Scientific-info</string>
</array>
<key>CFBundleTypeName</key>
<string>ISI Common Export Format Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mods</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/mods+xml</string>
</array>
<key>CFBundleTypeName</key>
<string>Metadata Object Description Schema Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>rdf</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/rdf+xml</string>
</array>
<key>CFBundleTypeName</key>
<string>Resource Description Framework Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>bib</string>
<string>bibtex</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-bibtex</string>
<string>text/x-bibtex</string>
</array>
<key>CFBundleTypeName</key>
<string>BibTeX Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mrc</string>
<string>marc</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/marc</string>
</array>
<key>CFBundleTypeName</key>
<string>MARC Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<!-- Citation styles -->
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>csl</string>
<string>csl.txt</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.citationstyles.style+xml</string>
</array>
<key>CFBundleTypeName</key>
<string>CSL Citation Style</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<!-- Hopefully, we don't become the default app for these -->
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>xml</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeName</key>
<string>XML Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>txt</string>
</array>
<!--<key>CFBundleTypeIconFile</key>
<string>document.icns</string>-->
<key>CFBundleTypeName</key>
<string>Text File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>zotero</string>
<key>CFBundleGetInfoString</key>
<string>Zotero {{VERSION}}, © 2006-2018 Contributors</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>org.zotero.zotero</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Zotero</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>{{VERSION_NUMERIC}}</string>
<key>CFBundleSignature</key>
<string>ZOTR</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<!--<key>CFBundleURLIconFile</key>
<string>document.icns</string>-->
<key>CFBundleURLName</key>
<string>zotero URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>zotero</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>{{VERSION_NUMERIC}}</string>
<key>NSAppleScriptEnabled</key>
<true/>
<key>CGDisableCoalescedUpdates</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>10.9.0</string>
</dict>
</plist>

View file

@ -1 +0,0 @@
APPLZOTR

Binary file not shown.

View file

@ -1,93 +0,0 @@
#!/bin/bash
set -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
fx_app_name=Firefox.app
# Get Mozilla source directory from command line
if [ -z "${1:-}" ]; then
echo "Usage: $0 /path/to/firefox" >&2
exit 1
fi
GECKO_PATH="$(cd "$1" && pwd)"
mach=$GECKO_PATH/mach
BUILD_DIR=`mktemp -d`
function cleanup {
rm -rf $BUILD_DIR
}
trap cleanup EXIT
set -x
export MOZ_BUILD_DATE=`date "+%Y%m%d%H%M%S"`
# Install required Rust version
rustup toolchain install $RUST_VERSION
rustup target add --toolchain $RUST_VERSION aarch64-apple-darwin x86_64-apple-darwin
rustup default $RUST_VERSION
cp "$SCRIPT_DIR/mozconfig" "$GECKO_PATH"
# Build Firefox for Intel and Apple Silicon
export Z_ARCH=x64
$mach build
$mach package
export Z_ARCH=aarch64
$mach build
$mach package
cd $BUILD_DIR
# Unify into Universal build
# From https://searchfox.org/mozilla-central/rev/97c902e8f92b15dc63eb584bfc594ecb041242a4/taskcluster/scripts/misc/unify.sh
for i in x86_64 aarch64; do
$mach python -m mozbuild.action.unpack_dmg "$GECKO_PATH"/obj-$i-apple-darwin/dist/*.dmg $i
done
mv x86_64 x64
$mach python "$GECKO_PATH/toolkit/mozapps/installer/unify.py" x64/*.app aarch64/*.app
cp x64/$fx_app_name/Contents/MacOS/firefox zotero
xz zotero
mv zotero.xz "$APP_ROOT_DIR/mac/zotero.xz"
# Package custom Firefox components for fetch_xulrunner. Add XUL to this list if needed.
# Upload the archive to ${custom_components_url}mac/ and set custom_components_hash_mac
# in config.sh to the hash printed below.
custom_components=(libmozglue.dylib)
mkdir MacOS
for component in "${custom_components[@]}"; do
cp "x64/$fx_app_name/Contents/MacOS/$component" MacOS/
done
tar cfJ MacOS.tar.xz MacOS
components_hash=$(shasum -a 256 MacOS.tar.xz | cut -d ' ' -f 1)
components_file="$APP_ROOT_DIR/mac/$components_hash.tar.xz"
mv MacOS.tar.xz "$components_file"
echo "Custom Firefox components: $components_file"
echo "custom_components_hash_mac=\"$components_hash\""
# Save a copy of ChannelPrefs.framework
#
# Frameworks need to be signed, and we can't sign source builds, so store a signed copy with
# 'default' replaced with 'source'. Deployed builds will then replace 'source' with
# 'release'/'beta'/'dev'.
rm -rf "$APP_ROOT_DIR/mac/ChannelPrefs.framework"
cp -R x64/$fx_app_name/Contents/Frameworks/ChannelPrefs.framework "$APP_ROOT_DIR/mac/ChannelPrefs.framework"
pushd "$APP_ROOT_DIR/mac"
./set-channel-prefs-channel ChannelPrefs.framework/ChannelPrefs default source
security -v unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/$KEYCHAIN.keychain-db
/usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" ChannelPrefs.framework
/usr/bin/codesign -vvvv ChannelPrefs.framework
tar cfvJ ChannelPrefs.framework.tar.xz ChannelPrefs.framework
rm -rf ChannelPrefs.framework
popd
# Uncomment to build updater
#cd x64/$fx_app_name/Contents/MacOS/
#"$APP_ROOT_DIR/mac/updater_renamer"
#cat updater.app/Contents/Resources/English.lproj/InfoPlist.strings
#tar cfvJ "$APP_ROOT_DIR/mac/updater.tar.xz" updater.app/

View file

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
Entitlements to apply during codesigning of production builds.
-->
<plist version="1.0">
<dict>
<!-- Firefox needs to create executable pages (without MAP_JIT) -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
<!-- Allow loading third party libraries. Needed for Flash and CDMs -->
<!-- Disabled for Zotero -->
<key>com.apple.security.cs.disable-library-validation</key><false/>
<!-- Firefox needs to access the microphone on sites the user allows -->
<!-- Disabled for Zotero -->
<key>com.apple.security.device.audio-input</key><false/>
<!-- Firefox needs to access the camera on sites the user allows -->
<!-- Disabled for Zotero -->
<key>com.apple.security.device.camera</key><false/>
<!-- Firefox needs to access the location on sites the user allows -->
<!-- Disabled for Zotero -->
<key>com.apple.security.personal-information.location</key><false/>
<!-- For SmartCardServices(7) -->
<!-- Disabled for Zotero -->
<key>com.apple.security.smartcard</key><false/>
<!-- Added for Zotero to control Word and bring windows to the front -->
<key>com.apple.security.automation.apple-events</key><true/>
</dict>
</plist>

View file

@ -1,35 +0,0 @@
if [ "$Z_ARCH" == "x64" ]; then
ac_add_options --target=x86_64-apple-darwin
elif [ "$Z_ARCH" == "aarch64" ]; then
ac_add_options --target=aarch64-apple-darwin
fi
ac_add_options --enable-bootstrap
ac_add_options --with-macos-sdk=$HOME/src/MacOSX26.5.sdk
mk_add_options AUTOCLOBBER=1
# These don't all affect the stub, but they can't hurt, and we'll want them if
# we switch to custom XUL builds
ac_add_options MOZ_ENABLE_JS_DUMP=1
ac_add_options MOZ_ENABLE_FORKSERVER=
ac_add_options MOZ_TELEMETRY_REPORTING=
ac_add_options MOZ_DATA_REPORTING=
ac_add_options --disable-tests
ac_add_options --disable-debug
ac_add_options --disable-debug-symbols
ac_add_options --disable-webrtc
ac_add_options --disable-eme
export MOZILLA_OFFICIAL=1
export RELEASE_OR_BETA=1
MOZ_REQUIRE_SIGNING=
# Official builds require source info, which Mozilla only detects automatically from
# Mercurial checkouts
export MOZ_SOURCE_REPO=https://github.com/mozilla-firefox/firefox
export MOZ_SOURCE_CHANGESET=$(git -C "$topsrcdir" rev-parse HEAD)
ac_add_options --enable-official-branding
# Build updater without MAR signature verification
ac_add_options --enable-unverified-updates

File diff suppressed because it is too large Load diff

View file

@ -1,242 +0,0 @@
diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -193,19 +193,31 @@ static bool HasFlag(int argc, char* argv[], const char* s) {
}
return false;
}
#endif
constinit Bootstrap::UniquePtr gBootstrap;
static int do_main(int argc, char* argv[], char* envp[]) {
+ // Allow profile downgrade for Zotero
+ setenv("MOZ_ALLOW_DOWNGRADE", "1", 1);
+ // Don't create dedicated profile (default-esr)
+ setenv("MOZ_LEGACY_PROFILES", "1", 1);
+
// Allow firefox.exe to launch XULRunner apps via -app <application.ini>
// Note that -app must be the *first* argument.
- const char* appDataFile = getenv("XUL_APP_FILE");
+ UniqueFreePtr<char> iniPath = BinaryPath::GetApplicationIni();
+ if (!iniPath) {
+ Output("Couldn't find application.ini.\n");
+ return 255;
+ }
+ char *appDataFile = iniPath.get();
+
+
if ((!appDataFile || !*appDataFile) && (argc > 1 && IsFlag(argv[1], "app"))) {
if (argc == 2) {
Output("Incorrect number of arguments passed to -app");
return 255;
}
appDataFile = argv[2];
char appEnv[MAXPATHLEN];
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h
--- a/xpcom/build/BinaryPath.h
+++ b/xpcom/build/BinaryPath.h
@@ -126,16 +126,56 @@ class BinaryPath {
} else {
rv = NS_ERROR_FAILURE;
}
CFRelease(executableURL);
return rv;
}
+ static nsresult GetApplicationIni(char aResult[MAXPATHLEN])
+ {
+ // Works even if we're not bundled.
+ CFBundleRef appBundle = CFBundleGetMainBundle();
+ if (!appBundle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFURLRef iniURL = CFBundleCopyResourceURL(appBundle, CFSTR("application.ini"),
+ NULL, CFSTR("app"));
+ if (!iniURL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ if (CFURLGetFileSystemRepresentation(iniURL, false, (UInt8*)aResult,
+ MAXPATHLEN)) {
+ // Sanitize path in case the app was launched from Terminal via
+ // './firefox' for example.
+ size_t readPos = 0;
+ size_t writePos = 0;
+ while (aResult[readPos] != '\0') {
+ if (aResult[readPos] == '.' && aResult[readPos + 1] == '/') {
+ readPos += 2;
+ } else {
+ aResult[writePos] = aResult[readPos];
+ readPos++;
+ writePos++;
+ }
+ }
+ aResult[writePos] = '\0';
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ CFRelease(iniURL);
+ return rv;
+ }
+
#elif defined(ANDROID)
static nsresult Get(char aResult[MAXPATHLEN]) {
// On Android, we use the MOZ_ANDROID_LIBDIR variable that is set by the
// Java bootstrap code.
const char* libDir = getenv("MOZ_ANDROID_LIBDIR");
if (!libDir) {
return NS_ERROR_FAILURE;
}
@@ -281,16 +321,29 @@ class BinaryPath {
if (NS_FAILED(Get(path))) {
return nullptr;
}
UniqueFreePtr<char> result;
result.reset(strdup(path));
return result;
}
+#if defined(XP_MACOSX)
+ static UniqueFreePtr<char> GetApplicationIni()
+ {
+ char path[MAXPATHLEN];
+ if (NS_FAILED(GetApplicationIni(path))) {
+ return nullptr;
+ }
+ UniqueFreePtr<char> result;
+ result.reset(strdup(path));
+ return result;
+ }
+#endif
+
#ifdef MOZILLA_INTERNAL_API
static nsresult GetFile(nsIFile** aResult) {
nsCOMPtr<nsIFile> lf;
# ifdef XP_WIN
wchar_t exePath[MAXPATHLEN];
nsresult rv = GetW(exePath);
# else
char exePath[MAXPATHLEN];
diff --git a/mozglue/build/AudioQueueSanitizer.cpp b/mozglue/build/AudioQueueSanitizer.cpp
new file mode 100644
index 000000000000..9efa3255c472
--- /dev/null
+++ b/mozglue/build/AudioQueueSanitizer.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Work around a use-after-free in the macOS 27 system speech synthesizer.
+//
+// When an utterance finishes, the AudioQueue player in the private
+// TextToSpeech framework (used by both NSSpeechSynthesizer and
+// AVSpeechSynthesizer) enqueues a short de-click ramp that starts from a
+// sample read out of a buffer it has already freed. With the system
+// allocator, which zeroes freed memory, that sample is 0 and the ramp is
+// silent. mozjemalloc poisons freed memory with 0xE5 instead, which reads
+// as -1.36e23 in float32, so every utterance ends with a ~10 ms full-scale
+// burst.
+//
+// We can't fix the framework, so we interpose AudioQueueEnqueueBuffer and
+// zero any float samples that are non-finite or far outside the nominal
+// [-1, 1] range before they reach CoreAudio. Real audio never gets close
+// to kMaxSampleMagnitude, so this only ever affects garbage.
+//
+// dyld only honors __interpose tuples in images loaded at launch, which is
+// why this needs to be in mozglue rather than in XUL (loaded with dlopen)
+// alongside OSXSpeechSynthesizerService.
+
+#include <AudioToolbox/AudioToolbox.h>
+#include <math.h>
+
+namespace {
+
+constexpr float kMaxSampleMagnitude = 16.0f;
+
+void SanitizeBuffer(AudioQueueRef aQueue, AudioQueueBufferRef aBuffer) {
+ AudioStreamBasicDescription format;
+ UInt32 size = sizeof(format);
+ if (AudioQueueGetProperty(aQueue, kAudioQueueProperty_StreamDescription,
+ &format, &size) != noErr) {
+ return;
+ }
+ if (format.mFormatID != kAudioFormatLinearPCM ||
+ !(format.mFormatFlags & kAudioFormatFlagIsFloat) ||
+ format.mBitsPerChannel != 32) {
+ return;
+ }
+
+ float* samples = static_cast<float*>(aBuffer->mAudioData);
+ UInt32 count = aBuffer->mAudioDataByteSize / sizeof(float);
+ for (UInt32 i = 0; i < count; i++) {
+ // Written so that NaN also fails the comparison.
+ if (!(fabsf(samples[i]) <= kMaxSampleMagnitude)) {
+ samples[i] = 0.0f;
+ }
+ }
+}
+
+OSStatus SanitizingEnqueueBuffer(
+ AudioQueueRef aQueue, AudioQueueBufferRef aBuffer, UInt32 aNumPackets,
+ const AudioStreamPacketDescription* aPacketDescs) {
+ SanitizeBuffer(aQueue, aBuffer);
+ return AudioQueueEnqueueBuffer(aQueue, aBuffer, aNumPackets, aPacketDescs);
+}
+
+OSStatus SanitizingEnqueueBufferWithParameters(
+ AudioQueueRef aQueue, AudioQueueBufferRef aBuffer, UInt32 aNumPackets,
+ const AudioStreamPacketDescription* aPacketDescs, UInt32 aTrimFrames,
+ UInt32 aTrimFramesAtEnd, UInt32 aNumParamValues,
+ const AudioQueueParameterEvent* aParamValues,
+ const AudioTimeStamp* aStartTime, AudioTimeStamp* aActualStartTime) {
+ SanitizeBuffer(aQueue, aBuffer);
+ return AudioQueueEnqueueBufferWithParameters(
+ aQueue, aBuffer, aNumPackets, aPacketDescs, aTrimFrames,
+ aTrimFramesAtEnd, aNumParamValues, aParamValues, aStartTime,
+ aActualStartTime);
+}
+
+struct Interpose {
+ const void* mReplacement;
+ const void* mReplacee;
+};
+
+__attribute__((used, section("__DATA,__interpose"))) const Interpose
+ kInterposes[] = {
+ {reinterpret_cast<const void*>(&SanitizingEnqueueBuffer),
+ reinterpret_cast<const void*>(&AudioQueueEnqueueBuffer)},
+ {reinterpret_cast<const void*>(&SanitizingEnqueueBufferWithParameters),
+ reinterpret_cast<const void*>(
+ &AudioQueueEnqueueBufferWithParameters)},
+};
+
+} // namespace
diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build
index f8f01e999e54..111fe5af1b0d 100644
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -41,6 +41,14 @@ if CONFIG["OS_TARGET"] == "WINNT":
"winmm.dll",
]
+if CONFIG["OS_TARGET"] == "Darwin" and FORCE_SHARED_LIB:
+ SOURCES += [
+ "AudioQueueSanitizer.cpp",
+ ]
+ OS_LIBS += [
+ "-framework AudioToolbox",
+ ]
+
if CONFIG["MOZ_WIDGET_TOOLKIT"]:
if CONFIG["MOZ_MEMORY"] and FORCE_SHARED_LIB:
pass

File diff suppressed because it is too large Load diff

View file

@ -1,69 +0,0 @@
#!/bin/bash
# Replace channel in ChannelPrefs, with appropriate null-byte padding
#
# ChannelPrefs.framework needs to be signed, which we can't do during normal source builds, so we
# use this to replace 'default' in our custom framework with 'source\0' and store that, and then
# when making real builds we replace 'source\0' with 'release'/'beta'/'dev'/'test' (padded to 7 chars)
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
if [ -z "${1:-}" ] || [ -z "${2:-}" ] || [ -z "${3:-}" ]; then
echo "Usage: $0 /path/to/ChannelPrefs.framework/ChannelPrefs from_channel to_channel" >&2
exit 1
fi
binary=$1
from_channel=$2
to_channel=$3
# `strings` has a 4-character minimum by default, but we need 3 for 'dev'
# `-arch all` scans all slices of the universal binary rather than just the host architecture
strings_cmd="strings -n 3 -arch all"
if [ ${#to_channel} -gt 7 ]; then
echo "Channel length cannot exceed 7 characters -- aborting" >&2
exit 1
fi
if [ $($strings_cmd "$binary" | grep -c "$from_channel") -ne 2 ] ; then
echo "$from_channel not found twice in ChannelPrefs" >&2
echo >&2
$strings_cmd "$binary" >&2
exit 1
fi
original_size=$(wc -c "$binary" | awk '{print $1}')
perl -pi -e '
binmode(STDIN);
binmode(STDOUT);
use bytes;
my @channels = qw(release beta dev test source); # List of valid channels
# Channels from Bash
my $from_channel = "'"$from_channel"'";
my $to_channel = "'"$to_channel"'";
if (grep { $_ eq $to_channel } @channels) {
# Pad channels with null bytes
$from_channel .= "\0" x (7 - length($from_channel));
$to_channel .= "\0" x (7 - length($to_channel));
} else {
die "Unknown update channel: $to_channel\n";
}
s/$from_channel/$to_channel/g;
' "$binary"
new_size=$(wc -c "$binary" | awk '{print $1}')
if [ $original_size -ne $new_size ]; then
echo "File size of ChannelPrefs has changed -- aborting" >&2
exit 1
fi
if [ $($strings_cmd "$binary" | grep -c "$to_channel") -ne 2 ] ; then
echo "$to_channel not found twice in ChannelPrefs" >&2
$strings_cmd "$binary" >&2
exit 1
fi
$strings_cmd "$binary"

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:361730d7cea854b38c6968143555c5f5fe251e1c95f265a6133514ee72ea2c53
size 98904

View file

@ -1,10 +0,0 @@
#!/usr/bin/env python3
# Convert localized app name from "Firefox Software Update" to "Zotero Software Update"
#
# plist is UTF-16
f='updater.app/Contents/Resources/English.lproj/InfoPlist.strings'
with open(f, 'r', encoding='utf-16') as inputfile:
newText = inputfile.read().replace('Firefox', 'Zotero')
with open(f, 'w', encoding='utf-16') as outputfile:
outputfile.write(newText)

Binary file not shown.

@ -1 +0,0 @@
Subproject commit 2e78b1010ad49b4dde70ec9f0094b34a7b6f8965

@ -1 +0,0 @@
Subproject commit 300886d1984de7a0006d7a48310baa0c8c99bea0

@ -1 +0,0 @@
Subproject commit c0aa6e4bef039d94e17e81cb28b1fe9170c45b96

View file

@ -1,64 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="release"
BRANCH="10.0"
BUILD_ONLY=0
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/release/safari"
export SAFARI_APP_EXTENSION="$ROOT_DIR/../safari-app-extension-builds/release/ZoteroSafariExtension.appex"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
if [ ! -d "$SAFARI_APP_EXTENSION" ]; then
echo "Safari App Extension not found at $SAFARI_APP_EXTENSION -- aborting" >&2
exit 1
fi
fi
while getopts "b" opt; do
case $opt in
b)
BUILD_ONLY=1
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL
if [ $BUILD_ONLY -eq 1 ]; then
echo
echo "Build only -- skipping deploy."
echo "To deploy, run on the deploy server:"
echo " $DEPLOY_PATH/deploy $CHANNEL $VERSION $BUILD_PLATFORMS"
else
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS
fi

View file

@ -1,55 +0,0 @@
#!/bin/bash
set -euo pipefail
#
# Zip a file directly into app/omni.ja in staging/
#
# Zip paths are relative to the current directory, so this should be run from
# the client build/ directory
#
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
cat >&2 <<DONE
Usage: $0 path/to/file
DONE
exit 1
}
if [ -z "${1:-}" ]; then
usage
fi
files="$@"
for file in $files; do
if [ ! -f "$file" ]; then
echo "Error: $file not found!"
exit 1
fi
done
mac_path="$STAGE_DIR/Zotero.app/Contents/Resources"
win_path="$STAGE_DIR/Zotero_win-x64"
linux_path="$STAGE_DIR/Zotero_linux-x86_64"
added=0
build_id=$(date +%Y%m%d%H%M%S)
for path in "$mac_path" "$win_path" "$linux_path"; do
if [ -d "$path" ]; then
echo "$path/app/omni.ja"
echo "Updating $(basename $(dirname $(dirname $path)))"
zip "$path/app/omni.ja" $files
# Bump BuildID so that startup caches are invalidated
perl -pi -e "s/^BuildID=.*/BuildID=$build_id/" "$path/app/application.ini"
added=1
fi
done
if [ $added -eq 0 ]; then
echo "No directories found in staging!"
exit 1
fi

View file

@ -1,62 +0,0 @@
#!/usr/bin/env python3
"""
Update a builds manifest with info on a given build
"""
import argparse
import os
import re
import sys
import shutil
import json
import traceback
MAJOR = None
parser = argparse.ArgumentParser(
description='Update a builds manifest with info on a given build',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-f', '--file', required=True, help="path to updates.json")
parser.add_argument('-v', '--version', required=True, help='version number of build')
parser.add_argument('-b', '--build_id', required=True, help="build ID ('20160801142343')")
args = parser.parse_args()
def main():
try:
file = args.file
version = args.version
short_version = re.match(r'\d+\.\d+', version).group(0)
# Back up JSON file
shutil.copy2(file, file + '.bak')
# Read in existing file
with open(file) as f:
updates = json.loads(f.read())
updates.append({
'version': version,
'buildID': args.build_id,
'detailsURL': f'https://www.zotero.org/support/{short_version}_changelog',
'major': MAJOR
})
# Keep last 5 entries
updates = updates[-5:]
# Write new file
updates = json.dumps(updates, indent=2)
with open(file, 'w') as f:
f.write(updates + "\n")
print(updates)
return 0
except Exception as err:
sys.stderr.write("\n" + traceback.format_exc())
return 1
if __name__ == '__main__':
sys.exit(main())

View file

@ -1,64 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="beta"
BRANCH="main"
BUILD_ONLY=0
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/beta/safari"
export SAFARI_APP_EXTENSION="$ROOT_DIR/../safari-app-extension-builds/beta/ZoteroSafariExtension.appex"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
if [ ! -d "$SAFARI_APP_EXTENSION" ]; then
echo "Safari App Extension not found at $SAFARI_APP_EXTENSION -- aborting" >&2
exit 1
fi
fi
while getopts "b" opt; do
case $opt in
b)
BUILD_ONLY=1
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL
if [ $BUILD_ONLY -eq 1 ]; then
echo
echo "Build only -- skipping deploy."
echo "To deploy, run on the deploy server:"
echo " $DEPLOY_PATH/deploy $CHANNEL $VERSION $BUILD_PLATFORMS"
else
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS
fi

View file

@ -1,98 +0,0 @@
#!/bin/bash -e
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname $APP_ROOT_DIR)"
# Set ZOTERO_PROFILE environment variable to choose profile
profile_args=()
if [ -n "${ZOTERO_PROFILE:-}" ]; then
profile_args=(-p "$ZOTERO_PROFILE")
fi
FORCE_FULL=0
NO_REBUILD=0
SKIP_BUNDLED_FILES=0
DEBUGGER=0
while getopts "rfnbd" opt; do
case $opt in
r)
# Deprecated -- rebuilding is now the default
echo "-r is deprecated -- rebuilding is now the default (use -n to skip)" >&2
;;
f)
FORCE_FULL=1
;;
n)
NO_REBUILD=1
;;
b)
SKIP_BUNDLED_FILES=1
;;
d)
DEBUGGER=1
;;
\?)
exit 1
;;
esac
done
# Remove options from $@
shift $((OPTIND-1))
if [ $NO_REBUILD -eq 0 ]; then
PARAMS=""
if [ $DEBUGGER -eq 1 ]; then
PARAMS="-t"
fi
if [ $FORCE_FULL -eq 1 ]; then
PARAMS="$PARAMS -f"
fi
# Check if build watch is running
# If not, run now
if ! ps u | grep js-build/build.js | grep -v grep > /dev/null; then
echo "Running JS build process"
echo
cd $ROOT_DIR
# TEMP: --openssl-legacy-provider avoids a build error in pdf.js
NODE_OPTIONS=--openssl-legacy-provider node js-build/build.js
echo
fi
"$SCRIPT_DIR/dir_build" $PARAMS
fi
PARAMS=""
if [ $SKIP_BUNDLED_FILES -eq 1 ]; then
PARAMS="$PARAMS -ZoteroSkipBundledFiles"
fi
if [ $DEBUGGER -eq 1 ]; then
PARAMS="$PARAMS -jsdebugger"
else
PARAMS="$PARAMS -jsconsole"
fi
if [ "`uname`" = "Darwin" ]; then
command="Zotero.app/Contents/MacOS/zotero"
elif [ "`uname`" = "Linux" ]; then
if [[ "`uname -m`" = "aarch64" ]]; then
command="Zotero_linux-arm64/zotero"
else
command="Zotero_linux-x86_64/zotero"
fi
elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
command="Zotero_win-x64/zotero.exe"
else
echo "Unknown platform" >&2
exit 1
fi
echo
"$APP_ROOT_DIR/staging/$command" "${profile_args[@]}" -ZoteroDebugText $PARAMS "$@"

View file

@ -1,167 +0,0 @@
#!/bin/bash
#
# Builds Zotero and uploads to S3 and deploy server, but does not make the build live.
# To make the build live, run the deploy script on the deploy server (or via SSH).
#
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
cat >&2 <<DONE
Usage: $0 -d SOURCE_DIR -c CHANNEL -p PLATFORMS
-d SOURCE_DIR Source directory to build from
-c CHANNEL Release channel ('release', 'beta', 'dev')
-p PLATFORMS Platforms to build (m=Mac, w=Windows, l=Linux)
-i INCREMENTALS Number of incremental builds to create
DONE
exit 1
}
SOURCE_DIR=""
CHANNEL=""
PLATFORMS=""
while getopts "d:c:p:i:" opt; do
case $opt in
d)
SOURCE_DIR="$OPTARG"
if [ ! -d "$SOURCE_DIR" ]; then
echo "$SOURCE_DIR not found"
exit 1
fi
;;
c)
CHANNEL="$OPTARG"
;;
p)
PLATFORMS="$OPTARG"
;;
i)
NUM_INCREMENTALS="$OPTARG"
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
if [[ -z "$SOURCE_DIR" ]] || [[ -z "$CHANNEL" ]] || [[ -z "$PLATFORMS" ]]; then
usage
fi
"$SCRIPT_DIR"/check_requirements
VERSION="`cat \"$SOURCE_DIR\"/version`"
if [ -z "$VERSION" ]; then
echo "Error getting version from $SOURCE_DIR/version"
exit 1
fi
# Enforce signing unless a test build or macOS, which doesn't start otherwise
if [[ "$CHANNEL" != "test" || $PLATFORMS = "m" ]]; then
SIGN="-e"
else
SIGN=""
fi
# Build Zotero
"$ROOT_DIR/build.sh" -d "$SOURCE_DIR" -p $PLATFORMS -c $CHANNEL $SIGN
BUILD_ID=`cat "$DIST_DIR/build_id"`
if [ -z "$BUILD_ID" ]; then
echo "Error getting build id"
exit 1
fi
TEMP_DIR=`mktemp -d`
# Clean up on exit
function cleanup {
rm -rf "$TEMP_DIR"
}
trap cleanup EXIT
# Build full update
"$ROOT_DIR/update-packaging/build_autoupdate.sh" -f -c $CHANNEL -p $PLATFORMS -l $VERSION
# Build incremental updates for each platform
for i in `seq 0 1 $((${#PLATFORMS}-1))`
do
case ${PLATFORMS:i:1} in
m)
platform=mac
platform_name=Mac
;;
w)
platform=win
platform_name=Windows
;;
l)
platform=linux
platform_name=Linux
;;
*)
echo "$0: Invalid platform option ${PLATFORMS:i:1}"
usage
;;
esac
echo
echo "Getting $platform_name incrementals"
INCREMENTALS="`\"$SCRIPT_DIR/manage_incrementals\" -c $CHANNEL -p ${PLATFORMS:i:1} -n $NUM_INCREMENTALS`"
# Fetch pinned incrementals from deploy server and merge
PINNED_FILE="$DEPLOY_PATH/manifests/$CHANNEL/pinned-incrementals-$platform"
PINNED=""
if ssh $DEPLOY_HOST "test -f $PINNED_FILE"; then
PINNED="`ssh $DEPLOY_HOST cat $PINNED_FILE`"
fi
# Combine and deduplicate
INCREMENTALS="`echo -e "$INCREMENTALS\n$PINNED" | sort -u | grep -v '^$' || true`"
echo "$INCREMENTALS"
echo
if [ -n "$INCREMENTALS" ]; then
for from in $INCREMENTALS; do
echo "Building incremental update for $platform_name from $from to $VERSION"
"$ROOT_DIR/update-packaging/build_autoupdate.sh" -i "$from" -c "$CHANNEL" -p ${PLATFORMS:i:1} -l $VERSION
echo
done
fi
done
# Upload builds to S3
"$SCRIPT_DIR/upload_builds" $CHANNEL $VERSION
# Upload file lists and build info for each platform
channel_deploy_path="$DEPLOY_PATH/manifests/$CHANNEL"
mkdir "$TEMP_DIR/version_info"
chmod g+ws "$TEMP_DIR/version_info"
cp "$DIST_DIR"/files-* "$TEMP_DIR/version_info"
chmod g+w "$TEMP_DIR"/version_info/files-*
# Generate build-{os}.json for each platform
SHORT_VERSION=$(echo "$VERSION" | grep -oE '^[0-9]+\.[0-9]+')
DETAILS_URL="https://www.zotero.org/support/${SHORT_VERSION}_changelog"
for i in `seq 0 1 $((${#PLATFORMS}-1))`
do
case ${PLATFORMS:i:1} in
m) os_name="mac" ;;
w) os_name="win" ;;
l) os_name="linux" ;;
esac
printf '{\n "buildID": "%s",\n "detailsURL": "%s"\n}\n' "$BUILD_ID" "$DETAILS_URL" \
> "$TEMP_DIR/version_info/build-${os_name}.json"
chmod g+w "$TEMP_DIR/version_info/build-${os_name}.json"
done
rsync -rv "$TEMP_DIR/version_info/" $DEPLOY_HOST:"$channel_deploy_path/$VERSION/"
rm -rf "$STAGE_DIR"/*
echo
echo "Build $VERSION uploaded successfully."
echo "To deploy, run on the deploy server:"
echo " ./deploy $CHANNEL $VERSION $PLATFORMS"

View file

@ -1,37 +0,0 @@
#!/bin/bash
set -euo pipefail
# Check that app/ in this checkout matches app/ in the commit being built.
#
# The build-and-deploy scripts run the build scripts and config from the local
# checkout but build source files from the tip of the remote branch, so a stale
# checkout can silently build with the wrong Gecko version or omni patches.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
if [ -z "${1:-}" ]; then
echo "Usage: $0 commit-hash" >&2
exit 1
fi
hash=$1
cd "$ROOT_DIR"
if ! git cat-file -e "$hash^{commit}" 2>/dev/null; then
git fetch -q origin
fi
if ! git cat-file -e "$hash^{commit}" 2>/dev/null; then
echo "Commit $hash not found locally after fetching -- aborting" >&2
exit 1
fi
if ! git diff --quiet HEAD "$hash" -- app/; then
echo >&2
echo "app/ files in this checkout differ from app/ in $hash:" >&2
echo >&2
git --no-pager diff --stat HEAD "$hash" -- app/ >&2
echo >&2
echo "Update this checkout (e.g., 'git pull') and try again" >&2
exit 1
fi

View file

@ -1,158 +0,0 @@
#!/bin/bash
set -uo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$APP_ROOT_DIR")"
. "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
cd "$ROOT_DIR"
platform=`get_current_platform`
FAIL_CMD='echo -e \033[31;1mFAIL\033[0m'
FAIL_CMD_INLINE='echo -n -e \033[31;1mFAIL\033[0m'
FAILED=0
hdr_start=`tput smul`
hdr_stop=`tput rmul`
echo "${hdr_start}Checking build requirements:${hdr_stop}"
echo
echo -n "Checking for Node.js: "
which node || { $FAIL_CMD_INLINE; FAILED=1; }
node_version=$(node -v | cut -c 2-)
echo -n "Checking Node version: $node_version"
if [ $(echo $node_version | cut -d '.' -f1 ) -ge "18" ]; then
echo
else
echo -n " -- must be 18 or later "
{ $FAIL_CMD; FAILED=1; }
fi
echo -n "Checking for Git LFS: "
which git-lfs || { $FAIL_CMD_INLINE; FAILED=1; }
echo "Checking for Git LFS checkouts: "
lfs_files=$(git lfs ls-files -n 2> /dev/null) || { $FAIL_CMD; FAILED=1; }
for lfs_file in $lfs_files; do
echo -n " - $lfs_file: "
file_type=$(file -b $lfs_file)
echo -n $file_type" "
if [ -z "$(echo -n $file_type | grep XZ)" ]; then
{ $FAIL_CMD_INLINE; FAILED=1; echo " -- run 'git lfs install; git lfs pull'"; }
fi
echo
done
echo -n "Checking for perl: "
which perl || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for python3: "
which python3 || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for curl: "
which curl || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for zip: "
which zip || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for unzip: "
which unzip || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for xz: "
which xz || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for awk: "
which awk || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for rsync: "
which rsync || { $FAIL_CMD; FAILED=1; }
if [ $platform = "w" ]; then
echo -n "Checking for 7z: "
which 7z || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for rcedit: "
which rcedit || { $FAIL_CMD; FAILED=1; echo " -- Install with fetch_rcedit"; }
fi
if [ $platform = "w" ]; then
echo
echo "${hdr_start}Checking Windows packaging requirements:${hdr_stop}"
echo
echo -n "Checking for upx: "
which upx || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for uuidgen: "
which uuidgen || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for code-signing script: "
if [ -x "$APP_ROOT_DIR/win/codesign" ]; then
echo "$APP_ROOT_DIR/win/codesign"
else
$FAIL_CMD
FAILED=1
fi
echo -n "Checking for Unicode NSIS: "
if [ -x "`cygpath -u \"${NSIS_DIR}makensis.exe\"`" ]; then
echo "`cygpath -u \"${NSIS_DIR}makensis.exe\"`"
else
$FAIL_CMD
FAILED=1
fi
plugin_path=$(cd "$NSIS_DIR\\Plugins\\x86-unicode" && pwd)
plugins="AppAssocReg ApplicationID InvokeShellVerb nsProcess ShellLink UAC"
echo "Checking for NSIS plugins in $plugin_path"
for i in $plugins; do
echo -n " $i.dll: "
if [ -f "$plugin_path/$i.dll" ]; then
echo OK
else
$FAIL_CMD
FAILED=1
fi
done
fi
if [ $platform = "m" ]; then
echo
echo "${hdr_start}Checking Mac packaging requirements:${hdr_stop}"
echo
echo -n "Checking for codesign: "
which /usr/bin/codesign || { $FAIL_CMD; FAILED=1; }
fi
echo
echo "${hdr_start}Checking distribution requirements:${hdr_stop}"
echo
echo -n "Checking for Mozilla ARchive (MAR) tool: "
which mar || { $FAIL_CMD; FAILED=1; echo " -- Install with fetch_mar_tools"; }
echo -n "Checking for mbsdiff: "
which mbsdiff || { $FAIL_CMD; FAILED=1; echo " -- Install with fetch_mar_tools"; }
echo -n "Checking for rsync: "
which rsync || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for sha512sum/shasum: "
which sha512sum 2>/dev/null || which shasum 2>/dev/null || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for AWS CLI: "
which aws || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for AWS S3 access: "
aws s3 ls $S3_BUCKET/$S3_DIST_PATH | sed 's/^[[:blank:]]*//' || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for deploy host directory access: "
ssh $DEPLOY_HOST ls -d $DEPLOY_PATH || { $FAIL_CMD; FAILED=1; }
exit $FAILED

View file

@ -1,40 +0,0 @@
#!/bin/bash
set -euo pipefail
# Perform ad-hoc code signing of Zotero.app for local usage
#
# Currently we sign only the Word dylib, since that's necessary for Zotero developers to work on
# Word integration on Apple Silicon. If we discover other problems, we can uncomment some of the
# other lines. If you're making a custom build, you can modify this file to sign the entire build
# instead of just the bare minimum needed for development.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Usage: $0 path/to/staging/Zotero.app"
exit 1
fi
APPDIR=$1
DEVELOPER_ID="-"
entitlements_file="$ROOT_DIR/mac/entitlements.xml"
#/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" \
# "$APPDIR/Contents/MacOS/XUL" \
# "$APPDIR/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater"
#find "$APPDIR/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
#find "$APPDIR/Contents" -name '*.app' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
#/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" "$APPDIR/Contents/MacOS/zotero"
# Skip signing of Safari extension, since it's not present for local builds
# Sign final app package
#echo
#/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" "$APPDIR"
# Verify app
#/usr/bin/codesign --verify -vvvv "$APPDIR"
find "$APPDIR/Contents" -name 'libZoteroWordIntegration.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;

View file

@ -1,76 +0,0 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
# Creates the precomplete file containing the remove and rmdir application
# update instructions which is used to remove files and directories that are no
# longer present in a complete update. The current working directory is used for
# the location to enumerate and to create the precomplete file.
import io
import os
def get_build_entries(root_path):
"""Iterates through the root_path, creating a list for each file and
directory. Excludes any file paths ending with channel-prefs.js.
"""
rel_file_path_set = set()
rel_dir_path_set = set()
for root, dirs, files in os.walk(root_path):
for file_name in files:
parent_dir_rel_path = root[len(root_path) + 1 :]
rel_path_file = os.path.join(parent_dir_rel_path, file_name)
rel_path_file = rel_path_file.replace("\\", "/")
if not (
rel_path_file.endswith("channel-prefs.js")
or rel_path_file.endswith("update-settings.ini")
or "/ChannelPrefs.framework/" in rel_path_file
or rel_path_file.startswith("ChannelPrefs.framework/")
or "/UpdateSettings.framework/" in rel_path_file
or rel_path_file.startswith("UpdateSettings.framework/")
or "distribution/" in rel_path_file
):
rel_file_path_set.add(rel_path_file)
for dir_name in dirs:
parent_dir_rel_path = root[len(root_path) + 1 :]
rel_path_dir = os.path.join(parent_dir_rel_path, dir_name)
rel_path_dir = rel_path_dir.replace("\\", "/") + "/"
if rel_path_dir.find("distribution/") == -1:
rel_dir_path_set.add(rel_path_dir)
rel_file_path_list = list(rel_file_path_set)
rel_file_path_list.sort(reverse=True)
rel_dir_path_list = list(rel_dir_path_set)
rel_dir_path_list.sort(reverse=True)
return rel_file_path_list, rel_dir_path_list
def generate_precomplete(root_path):
"""Creates the precomplete file containing the remove and rmdir
application update instructions. The given directory is used
for the location to enumerate and to create the precomplete file.
"""
rel_path_precomplete = "precomplete"
# If inside a Mac bundle use the root of the bundle for the path.
if os.path.basename(root_path) == "Resources":
root_path = os.path.abspath(os.path.join(root_path, "../../"))
rel_path_precomplete = "Contents/Resources/precomplete"
precomplete_file_path = os.path.join(root_path, rel_path_precomplete)
# Open the file so it exists before building the list of files and open it
# in binary mode to prevent OS specific line endings.
precomplete_file = io.open(precomplete_file_path, mode="wt", newline="\n")
rel_file_path_list, rel_dir_path_list = get_build_entries(root_path)
for rel_file_path in rel_file_path_list:
precomplete_file.write('remove "' + rel_file_path + '"\n')
for rel_dir_path in rel_dir_path_list:
precomplete_file.write('rmdir "' + rel_dir_path + '"\n')
precomplete_file.close()
if __name__ == "__main__":
generate_precomplete(os.getcwd())

View file

@ -1,41 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="dev"
BRANCH="main"
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/dev/safari"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
fi
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL -i 1
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS

View file

@ -1,175 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
cat >&2 <<DONE
Usage: $0 [options]
If -p is omitted, platform (and, for Windows/Linux, architecture) default to the host system.
Architecture is ignored for Mac builds, which are always shipped as universal binaries.
Options
-p PLATFORM Platform to build (m=Mac, w=Windows, l=Linux)
-a ARCH Target architecture (arm64, x64, win32) — **Windows/Linux only**
-t add devtools
-f force a full rebuild instead of updating the staged build in place
DONE
exit 1
}
platform=""
arch=""
devtools=0
force_full=0
while getopts "tp:a:f" opt; do
case $opt in
t)
devtools=1
;;
p)
for (( i=0; i<${#OPTARG}; i++ )); do
case ${OPTARG:i:1} in
m) platform="m";;
w) platform="w";;
l) platform="l";;
*)
echo "$0: Invalid platform option ${OPTARG:i:1}" >&2
usage
;;
esac
done
;;
a)
arch="$OPTARG"
;;
f)
force_full=1
;;
\?)
usage
;;
esac
done
# Derive platform from host if not provided
if [[ -z $platform ]]; then
case "$(uname -s)" in
Darwin) platform="m" ;;
Linux) platform="l" ;;
CYGWIN*|MINGW*) platform="w" ;;
esac
fi
# Handle architecture rules
if [[ $platform = "m" ]]; then
# No arch for Mac — ignore anything provided
if [[ -n $arch ]]; then
echo "Ignoring -a $arch for Mac build (universal binary)" >&2
fi
arch=""
else
# Windows / Linux: derive arch if not supplied
if [[ -z $arch ]]; then
# An x64 build of Git for Windows runs emulated on Windows on ARM, where it
# reports x86_64 from `uname -m`, while `uname -s` reports the native
# architecture
if [[ $platform = "w" && "$(uname -s)" == *ARM64* ]]; then
arch="arm64"
else
case "$(uname -m)" in
arm64|aarch64) arch="arm64" ;;
x86_64) arch="x64" ;;
esac
fi
fi
fi
CHANNEL="source"
# Remove stale symlinks left behind in build/ when source files are deleted,
# which would otherwise break the build
if [ -d "$ROOT_DIR/build" ]; then
# With -L, -type l matches only broken symlinks
stale_links=$(find -L "$ROOT_DIR/build" -type l -print)
if [ -n "$stale_links" ]; then
echo "Removing stale symlinks from build/:" >&2
echo "$stale_links" >&2
while IFS= read -r link; do
rm "$link"
done <<< "$stale_links"
fi
fi
# Try to update the staged build in place instead of doing a full rebuild
if [[ $force_full -eq 0 ]]; then
incr_cmd=("$SCRIPT_DIR/incremental_update" -p "$platform")
[[ -n $arch ]] && incr_cmd+=( -a "$arch" )
[[ $devtools -eq 1 ]] && incr_cmd+=( -t )
set +e
"${incr_cmd[@]}"
incr_status=$?
set -e
if [ $incr_status -ne 2 ]; then
exit $incr_status
fi
fi
hash=$(git -C "$ROOT_DIR" rev-parse --short HEAD)
build_dir=$(mktemp -d)
manifest_tmp=""
cleanup() {
rm -rf "$build_dir"
if [ -n "$manifest_tmp" ]; then
rm -f "$manifest_tmp"
fi
}
trap cleanup EXIT
# Snapshot the state of build/ and app/ before the build for later incremental
# updates
app_hash=""
if [ -d "$ROOT_DIR/build" ]; then
manifest_tmp=$(mktemp)
generate_build_manifest "$ROOT_DIR/build" > "$manifest_tmp"
app_hash=$(generate_app_hash "$APP_ROOT_DIR")
fi
"$SCRIPT_DIR/prepare_build" -s "$ROOT_DIR/build" -o "$build_dir" -c "$CHANNEL" -m "$hash"
build_cmd=("$APP_ROOT_DIR/build.sh" -d "$build_dir" -p "$platform" -c "$CHANNEL" -s -q)
[[ -n $arch ]] && build_cmd+=( -a "$arch" )
[[ $devtools -eq 1 ]] && build_cmd+=( -t )
"${build_cmd[@]}"
# Save manifest for future incremental updates
if [ -n "$manifest_tmp" ]; then
if [[ -z "${ZOTERO_TEST:-}" ]] || [[ "${ZOTERO_TEST:-}" == "0" ]]; then
include_tests=0
else
include_tests=1
fi
{
echo "#format=2"
echo "#include_tests=$include_tests"
echo "#devtools=$devtools"
echo "#app_hash=$app_hash"
cat "$manifest_tmp"
} > "$STAGE_DIR/.build-manifest"
fi
# Ad-hoc-sign the Word dylib so it works on Apple Silicon. This only needs to
# happen when the staged build is fully rebuilt -- incremental updates don't
# touch the dylib.
if [[ $platform = "m" ]] && [[ "$(uname -s)" = "Darwin" ]]; then
"$SCRIPT_DIR/codesign_local" "$STAGE_DIR/Zotero.app"
fi
echo Done

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$APP_ROOT_DIR"
mkdir -p "xulrunner/bin"
if [ "`uname`" = "Darwin" ]; then
# Mozilla has Linux executables where the Mac files should be, so supply our own Mac builds
curl -o "xulrunner/bin/mar" https://zotero-download.s3.us-east-1.amazonaws.com/tools/mac/102.11.0esr/mar
curl -o "xulrunner/bin/mbsdiff" https://zotero-download.s3.us-east-1.amazonaws.com/tools/mac/102.11.0esr/mbsdiff
elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
# Mozilla doesn't seem to provide Windows versions via its file server anymore, so supply our own
curl -o "xulrunner/bin/mar.exe" https://zotero-download.s3.us-east-1.amazonaws.com/tools/win/102.11.0esr/mar.exe
curl -o "xulrunner/bin/mbsdiff.exe" https://zotero-download.s3.us-east-1.amazonaws.com/tools/win/102.11.0esr/mbsdiff.exe
else
curl -o "xulrunner/bin/mar" https://ftp.mozilla.org/pub/firefox/nightly/2022/05/2022-05-30-09-39-43-mozilla-central/mar-tools/linux64/mar
curl -o "xulrunner/bin/mbsdiff" https://ftp.mozilla.org/pub/firefox/nightly/2022/05/2022-05-30-09-39-43-mozilla-central/mar-tools/linux64/mbsdiff
fi
chmod 755 xulrunner/bin/mar xulrunner/bin/mbsdiff

View file

@ -1,11 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$ROOT_DIR"
mkdir -p "xulrunner/bin"
curl -L -o "xulrunner/bin/rcedit.exe" https://github.com/electron/rcedit/releases/download/v1.1.1/rcedit-x86.exe
chmod 755 xulrunner/bin/rcedit

View file

@ -1,855 +0,0 @@
#!/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
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|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
# Used by OSKeyStore as the master-key label, visible in macOS Keychain Access.
# Verify that OSKeyStore still derives the label from MOZ_APP_BASENAME, so a
# future Mozilla change to a hardcoded string doesn't silently rebrand the
# keychain entry back to "Firefox Encrypted Storage".
replace_line 'MOZ_APP_BASENAME: "Firefox"' 'MOZ_APP_BASENAME: "Zotero"' modules/AppConstants.sys.mjs
check_line 'STORE_LABEL: AppConstants\.MOZ_APP_BASENAME \+ " Encrypted Storage"' modules/OSKeyStore.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
# Entries in the JSWINDOWACTORS/JSPROCESSACTORS literals:
remove_between '^ AudioPlayback: \{' '^ },' $file
remove_between '^ CookieBanner: \{' '^ },' $file
remove_between '^ Thumbnails: \{' '^ },' $file
remove_between '^ Translations: \{' '^ },' $file
remove_between '^ TranslationsEngine: \{' '^ },' $file
# Entries added conditionally after the literals:
remove_between '^ JSWINDOWACTORS\.AboutTranslations = \{' '^ \};' $file
remove_between '^ JSWINDOWACTORS\.PictureInPictureLauncher = \{' '^ \};' $file
remove_between '^ JSWINDOWACTORS\.PictureInPictureToggle = \{' '^ \};' $file
remove_between '^ JSWINDOWACTORS\.PictureInPicture = \{' '^ \};' $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
# fcntl() is variadic, but js-ctypes declares it with a fixed third argument, which violates
# the ABI on every platform. It's harmless on Linux, where variadic and fixed integer
# arguments are passed in the same registers, but Apple's arm64 ABI passes variadic
# arguments on the stack, so the flag goes into a register the callee never reads and
# fcntl() takes it from an uninitialized stack slot instead. F_SETFD can therefore set
# FD_CLOFORK (bit 1, added in macOS 26) rather than FD_CLOEXEC, closing the pipe in the
# forked child and breaking Subprocess spawns, and F_SETFL can silently fail to set
# O_NONBLOCK. Since the value comes from leftover stack contents, this varies by build and
# call path instead of failing consistently -- don't assume it's fixed just because a given
# build works. This change should stay until this is fixed upstream.
if [[ $platform != win* ]]; then
replace_line 'ctypes\.int \/\* \.\.\. \*\/,' '"...",' modules/subprocess/subprocess_shared_unix.js
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[0], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[1], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFL, LIBC\.O_NONBLOCK\);' 'libc.fcntl(fds[1], LIBC.F_SETFL, ctypes.int(LIBC.O_NONBLOCK));' modules/subprocess/subprocess_unix.worker.js
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFL, LIBC\.O_NONBLOCK\);' 'libc.fcntl(fds[0], LIBC.F_SETFL, ctypes.int(LIBC.O_NONBLOCK));' modules/subprocess/subprocess_unix.sys.mjs
replace_line 'libc\.fcntl\(fds\[0\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[0], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.sys.mjs
replace_line 'libc\.fcntl\(fds\[1\], LIBC\.F_SETFD, LIBC\.FD_CLOEXEC\);' 'libc.fcntl(fds[1], LIBC.F_SETFD, ctypes.int(LIBC.FD_CLOEXEC));' modules/subprocess/subprocess_unix.sys.mjs
fi
# 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 the search bar and the whole sidebar, since we only ever show plugins and the main pane
# is already headed "Manage Your Plugins"
echo '.main-search, #sidebar { display: none; }' >> $file
# Center the content in the window now that it isn't offset by the sidebar
echo '#full { grid-template-columns: 1fr; }' >> $file
echo '#content { max-width: calc(var(--page-main-content-width) + var(--main-margin-start)); margin-inline: auto; }' >> $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-utils.mjs"
# Hide unsigned-addon warning
replace_line 'export function isUnsignedWarningMessageDisabled\(\) \{' \
'export function isUnsignedWarningMessageDisabled() {if (true) return true;' $file
# Use our own localized string for blocked plugin notification
replace_line 'details-notification-hard-blocked-\$\{typeSuffix\}' 'plugins-blocked-plugin' $file
# Hide Recommendations tab in sidebar and recommendations in main pane
replace_line 'function isDiscoverEnabled\(\) \{' 'function isDiscoverEnabled() {return false;' $file
file="chrome/toolkit/content/mozapps/extensions/components/addon-details.mjs"
# Hide Private Browsing setting in addon details
replace_line 'pbRow\.' '\/\/pbRow.' $file
replace_line 'let isAllowed = await isAllowedInPrivateBrowsing' '\/\/let isAllowed = await isAllowedInPrivateBrowsing' $file
# 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>' $file
file="chrome/toolkit/content/mozapps/extensions/components/addon-card.mjs"
# Use our own strings for the removal prompt
replace_line 'let \{ BrowserAddonUI \} = windowRoot.window;' '' $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;' chrome/toolkit/content/mozapps/extensions/components/addon-list.mjs
# 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
# 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 '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
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" \/>' chrome/toolkit/content/global/commonDialog.xhtml
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
# Add the dropmarker for toolbarbuttons with wantdropmarker, which Firefox no longer creates
replace_line 'this.appendChild\(this.constructor.fragment.cloneNode\(true\)\);' \
'this.appendChild(this.constructor.fragment.cloneNode(true));
if (this.hasAttribute("wantdropmarker")) {
let dropmarker = document.createXULElement("dropmarker");
dropmarker.setAttribute("type", "menu");
dropmarker.className = "toolbarbutton-menu-dropmarker";
this.appendChild(dropmarker);
}' chrome/toolkit/content/global/elements/toolbarbutton.js
# Let clicks on the dropmarker pass through to the toolbarbutton, which only opens its menu
# when it's the original event target, as xul.css did when Firefox created the dropmarker
echo '.toolbarbutton-menu-dropmarker { pointer-events: none; }' >> chrome/toolkit/content/global/xul.css
# 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
# Size an autocomplete popup to the input that opened it, or to an ancestor that designates
# itself as the width container. Firefox sizes the popup to its content. (For creator inputs.)
replace_line '\/\/ invalidate\(\) depends on the width attribute' \
'let widthElement = aElement.closest(".autocomplete-popup-width-container") || aElement;
this.style.setProperty("--panel-width", Math.max(widthElement.getBoundingClientRect().width, 100) + "px");
\/\/ invalidate() depends on the width attribute' \
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 -fo 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
tarfile="Firefox $GECKO_VERSION MacOS.tar.xz"
if [[ "$(uname)" = "Darwin" ]]; then
shasum="shasum -a 256"
else
shasum=sha256sum
fi
download_components() {
echo "Downloading custom Firefox components"
echo
curl -fo "$tarfile" "${custom_components_url}mac/$custom_components_hash_mac.tar.xz"
}
verify_components_hash() {
echo "$custom_components_hash_mac $tarfile" | $shasum -c -
}
if [ ! -e "$tarfile" ]; then
download_components
fi
# Verify hash
echo
if ! verify_components_hash; then
echo "SHA hash mismatch. Redownloading custom Firefox components…"
rm -f "$tarfile"
download_components
echo
if ! verify_components_hash; then
echo "Hash still doesn't match. Aborting."
exit 1
fi
fi
echo
tar xvf "$tarfile"
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 -fO "$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)
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 -fO "$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

View file

@ -1,21 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Commit hash not provided"
exit 1
fi
hash="$1"
tmpdir=`mktemp -d`
cd $tmpdir
curl -sS -f -L -o build.zip "https://$S3_BUCKET.s3.amazonaws.com/$S3_CI_ZIP_PATH/$hash.zip" >&2 \
|| (echo "ZIP file not found for commit '$hash'" && exit 1)
unzip build.zip >&2
rm build.zip
echo $tmpdir

View file

@ -1,14 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
if [ -z "${1:-}" ]; then
echo "Usage: $0 branch"
exit 1
fi
branch=$1
git ls-remote --exit-code $SOURCE_REPO_URL $branch | cut -f 1

View file

@ -1,322 +0,0 @@
#!/bin/bash
set -euo pipefail
#
# Incrementally update a staged build in staging/ by zipping changed files from
# the client build/ directory directly into app/omni.ja, based on a manifest
# saved by dir_build after the last full build.
#
# Only files that are copied into omni.ja unmodified by build.sh can be updated
# this way. If any other file changed (or a file was removed), a full rebuild
# is required.
#
# Exit codes:
# 0 -- staged build was updated (or nothing changed)
# 2 -- a full rebuild is required
#
# This is normally run automatically by dir_build, not directly.
#
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$APP_ROOT_DIR")"
. "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
cat >&2 <<DONE
Usage: $0 -p PLATFORM [-a ARCH] [-t]
Options
-p PLATFORM platform of the staged build to update (m=Mac, w=Windows, l=Linux)
-a ARCH target architecture (Windows/Linux only)
-t devtools requested
DONE
exit 1
}
platform=""
arch=""
devtools=0
while getopts "p:a:t" opt; do
case $opt in
p)
platform="$OPTARG"
;;
a)
arch="$OPTARG"
;;
t)
devtools=1
;;
*)
usage
;;
esac
done
if [ -z "$platform" ]; then
usage
fi
BUILD_SRC_DIR="$ROOT_DIR/build"
MANIFEST_FILE="$STAGE_DIR/.build-manifest"
function fall_back {
echo "$1 -- performing full build" >&2
exit 2
}
if [ ! -d "$BUILD_SRC_DIR" ]; then
fall_back "No build/ directory"
fi
if [ ! -f "$MANIFEST_FILE" ]; then
fall_back "No manifest from previous build"
fi
if [[ "$(sed -n 's/^#format=//p' "$MANIFEST_FILE")" != "2" ]]; then
fall_back "Unsupported manifest format"
fi
# Make sure a staged build exists for the requested platform
case $platform in
m)
app_dir="$STAGE_DIR/Zotero.app/Contents/Resources"
;;
w)
app_dir="$STAGE_DIR/Zotero_$(get_canonical_arch w "$arch")"
;;
l)
app_dir="$STAGE_DIR/Zotero_linux-$(get_canonical_arch l "$arch")"
;;
*)
fall_back "Unknown platform '$platform'"
;;
esac
if [ ! -f "$app_dir/app/omni.ja" ]; then
fall_back "No staged build for requested platform"
fi
# If the previous build didn't include tests or devtools and they're requested
# now, files are missing from the staged build
manifest_tests=$(sed -n 's/^#include_tests=//p' "$MANIFEST_FILE")
manifest_tests=${manifest_tests:-0}
manifest_devtools=$(sed -n 's/^#devtools=//p' "$MANIFEST_FILE")
manifest_devtools=${manifest_devtools:-0}
if [[ -z "${ZOTERO_TEST:-}" ]] || [[ "${ZOTERO_TEST:-}" == "0" ]]; then
want_tests=0
else
want_tests=1
fi
if [[ $want_tests -eq 1 ]] && [[ $manifest_tests != "1" ]]; then
fall_back "Tests requested but not included in staged build"
fi
if [[ $devtools -eq 1 ]] && [[ $manifest_devtools != "1" ]]; then
fall_back "Devtools requested but not included in staged build"
fi
# If anything changed in app/ (build scripts, assets, bundled modules, Firefox
# runtime), a full rebuild is required
manifest_app_hash=$(sed -n 's/^#app_hash=//p' "$MANIFEST_FILE")
app_hash=$(generate_app_hash "$APP_ROOT_DIR")
if [[ -z "$manifest_app_hash" ]] || [[ "$app_hash" != "$manifest_app_hash" ]]; then
fall_back "Files in app/ changed"
fi
file_list=$(mktemp)
new_stats=$(mktemp)
carried=$(mktemp)
candidates=$(mktemp)
removed=$(mktemp)
cand_entries=$(mktemp)
trap 'rm -f "$file_list" "$new_stats" "$carried" "$candidates" "$removed" "$cand_entries"' EXIT
cd "$BUILD_SRC_DIR"
# Quickly find files that may have changed based on size/mtime, so that only
# those have to be hashed. Unmodified manifest entries are carried over,
# new/modified files become candidates, and files no longer present are
# considered removed.
build_manifest_file_list > "$file_list"
stat_file_list < "$file_list" > "$new_stats"
awk -F'\t' -v carried="$carried" -v candidates="$candidates" -v removed="$removed" '
NR==FNR {
if (/^#/) next
hash[$3] = $1
stat[$3] = $2
next
}
{
if (!($2 in stat)) {
print > candidates
}
else {
if ($1 == stat[$2]) {
print hash[$2] "\t" $0 > carried
}
else {
print > candidates
}
delete stat[$2]
}
}
END {
for (path in stat) print path > removed
}
' "$MANIFEST_FILE" "$new_stats"
# Hash the candidates and compare to the previous hashes to find real changes
# -- A = added, C = changed, R = removed
if [ -s "$candidates" ]; then
paste \
<(cut -f2 "$candidates" | sed 's,^,./,' | tr '\n' '\0' | hash_file_list | cut -f1) \
"$candidates" \
> "$cand_entries"
fi
changes=$(
if [ -s "$cand_entries" ]; then
awk -F'\t' '
NR==FNR {
if (/^#/) next
old[$3] = $1
next
}
{
if (!($3 in old)) print "A\t" $3
else if (old[$3] != $1) print "C\t" $3
}
' "$MANIFEST_FILE" "$cand_entries"
fi
if [ -s "$removed" ]; then
awk '{print "R\t" $0}' "$removed"
fi
)
# Make sure all changed files can be zipped directly into omni.ja, and skip
# ones that aren't included in the staged build
zip_files=()
ftl_files=()
test_copy_files=()
while IFS=$'\t' read -r status path; do
if [ -z "$status" ]; then
continue
fi
if [ "$status" == "R" ]; then
case "$path" in
test/*)
# Ignore removed test files if tests weren't included
if [[ $manifest_tests != "1" ]]; then
continue
fi
;;
esac
fall_back "$path was removed"
fi
case "$path" in
# Copied into localization/ subdirectories by build.sh
chrome/locale/*/zotero/mozilla/*)
fall_back "Can't update $path in place"
;;
# Pruned by prepare_build
chrome/content/zotero/locale/csl/*)
fall_back "Can't update $path in place"
;;
# Copied to localization/<locale>/ in addition to the chrome path
chrome/locale/*/zotero/*.ftl)
zip_files+=("$path")
ftl_files+=("$path")
;;
chrome/*|components/*|resource/*)
zip_files+=("$path")
;;
# Appended to the main chrome.manifest by build.sh
test/chrome.manifest)
fall_back "Can't update $path in place"
;;
test/*)
# If tests weren't included in the staged build, ignore changes
# to test files
if [[ $manifest_tests == "1" ]]; then
zip_files+=("$path")
case "$path" in
# Also copied to tests/ at the top level of the app
test/tests/*)
test_copy_files+=("$path")
;;
esac
fi
;;
*)
fall_back "Can't update $path in place"
;;
esac
done <<< "$changes"
# Find all staged omni.ja files to update (multiple can exist -- e.g., all
# Windows architectures)
omni_files=()
while IFS= read -r file; do
omni_files+=("$file")
done < <(find "$STAGE_DIR" -path '*/app/omni.ja')
if [ ${#zip_files[@]} -gt 0 ]; then
echo "Changed files:"
for path in "${zip_files[@]}"; do
echo " $path"
done
cd "$BUILD_SRC_DIR"
for omni_file in "${omni_files[@]}"; do
echo "Updating ${omni_file#"$STAGE_DIR"/}"
zip -qX "$omni_file" "${zip_files[@]}"
done
# Copy .ftl files to their localization/ paths
if [ ${#ftl_files[@]} -gt 0 ]; then
l10n_dir=$(mktemp -d)
for path in "${ftl_files[@]}"; do
locale=${path#chrome/locale/}
locale=${locale%%/*}
mkdir -p "$l10n_dir/localization/$locale"
cp -L "$path" "$l10n_dir/localization/$locale/$(basename "$path")"
done
pushd "$l10n_dir" > /dev/null
for omni_file in "${omni_files[@]}"; do
zip -qrX "$omni_file" localization
done
popd > /dev/null
rm -rf "$l10n_dir"
fi
# Copy test files to tests/ directories
if [ ${#test_copy_files[@]} -gt 0 ]; then
for path in "${test_copy_files[@]}"; do
for omni_file in "${omni_files[@]}"; do
dest="${omni_file%/app/omni.ja}/tests/${path#test/tests/}"
mkdir -p "$(dirname "$dest")"
cp -L "$path" "$dest"
done
done
fi
# Bump BuildID so that startup caches are invalidated even without
# -purgecaches
build_id=$(date +%Y%m%d%H%M%S)
for omni_file in "${omni_files[@]}"; do
perl -pi -e "s/^BuildID=.*/BuildID=$build_id/" "$(dirname "$omni_file")/application.ini"
done
echo "Updated staged build with ${#zip_files[@]} changed file(s)"
elif [ -n "$changes" ]; then
echo "No staged files changed"
else
echo "No changes since last build"
fi
{
echo "#format=2"
echo "#include_tests=$manifest_tests"
echo "#devtools=$manifest_devtools"
echo "#app_hash=$app_hash"
LC_ALL=C sort -t$'\t' -k3 "$carried" "$cand_entries"
} > "$MANIFEST_FILE.tmp"
mv "$MANIFEST_FILE.tmp" "$MANIFEST_FILE"

View file

@ -1,88 +0,0 @@
#!/bin/bash
#
# Manage list of deployed version numbers for a channel in order to generate incremental builds
#
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
cat >&2 <<DONE
Usage:
-c CHANNEL Release channel (e.g., 'beta'); defaults to 'release'
-p PLATFORM Platform (m=Mac, w=Windows, l=Linux)
-a VERSION Add version to incrementals list; cannot be used with -n
-n NUM_VERSIONS Number of previous versions to return; cannot be used with -a
DONE
exit 1
}
CHANNEL="release"
PLATFORM=""
VERSION=""
NUM_VERSIONS=""
while getopts "c:p:a:n:" opt; do
case $opt in
c)
CHANNEL="$OPTARG"
;;
p)
case "$OPTARG" in
m) PLATFORM=mac;;
w) PLATFORM=win;;
l) PLATFORM=linux;;
*)
echo "$0: Invalid platform option $OPTARG"
usage
;;
esac
;;
a)
VERSION="$OPTARG"
;;
n)
NUM_VERSIONS="$OPTARG"
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
if [[ -z "$PLATFORM" ]]; then
usage
fi
if [[ -z "$VERSION" ]] && [[ -z "$NUM_VERSIONS" ]]; then
usage
fi
if [[ "$VERSION" ]] && [[ "$NUM_VERSIONS" ]]; then
usage
fi
INCR_FILENAME="incrementals-$PLATFORM"
REMOTE_PATH="$DEPLOY_PATH/manifests/$CHANNEL/$INCR_FILENAME"
INCR_PATH="$DIST_DIR/$INCR_FILENAME"
mkdir -p "$DIST_DIR"
scp $DEPLOY_HOST:"$REMOTE_PATH" "$INCR_PATH" >&2
# Add version to file and reupload
if [ "$VERSION" ]; then
echo "Adding $VERSION to incrementals-$PLATFORM"
echo $VERSION >> "$INCR_PATH"
scp "$INCR_PATH" $DEPLOY_HOST:"$REMOTE_PATH"
# Show last n versions
elif [ "$NUM_VERSIONS" -gt 0 ]; then
# TEMP: Don't include 6.0 versions
#tail -n $NUM_VERSIONS "$INCR_PATH"
set +e
tail -n $NUM_VERSIONS "$INCR_PATH" | grep -v '^6\.0'
set -e
fi
rm "$INCR_PATH"

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
echo "Usage: $0 id"
exit 1
}
id=${1:-}
if [[ -z "$id" ]]; then
usage
fi
prepare_notary_auth
xcrun notarytool log "$id" "${notary_auth[@]}" notary_log.json
cat notary_log.json

View file

@ -1,19 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
echo "Usage: $0 file"
exit 1
}
file=${1:-}
if [[ -z "$file" ]]; then
usage
fi
echo "Stapling $file"
xcrun stapler staple $file

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage {
echo "Usage: $0 file"
exit 1
}
file=${1:-}
if [[ -z "$file" ]]; then
usage
fi
prepare_notary_auth
echo "Uploading ${file##*/} to Apple for notarization" >&2
xcrun notarytool submit $file "${notary_auth[@]}" --wait

View file

@ -1,376 +0,0 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Taras Glek <tglek@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import sys, os, subprocess, struct, re
local_file_header = [
("signature", "uint32"),
("min_version", "uint16"),
("general_flag", "uint16"),
("compression", "uint16"),
("lastmod_time", "uint16"),
("lastmod_date", "uint16"),
("crc32", "uint32"),
("compressed_size", "uint32"),
("uncompressed_size", "uint32"),
("filename_size", "uint16"),
("extra_field_size", "uint16"),
("filename", "filename_size"),
("extra_field", "extra_field_size"),
("data", "compressed_size")
]
cdir_entry = [
("signature", "uint32"),
("creator_version", "uint16"),
("min_version", "uint16"),
("general_flag", "uint16"),
("compression", "uint16"),
("lastmod_time", "uint16"),
("lastmod_date", "uint16"),
("crc32", "uint32"),
("compressed_size", "uint32"),
("uncompressed_size", "uint32"),
("filename_size", "uint16"),
("extrafield_size", "uint16"),
("filecomment_size", "uint16"),
("disknum", "uint16"),
("internal_attr", "uint16"),
("external_attr", "uint32"),
("offset", "uint32"),
("filename", "filename_size"),
("extrafield", "extrafield_size"),
("filecomment", "filecomment_size"),
]
cdir_end = [
("signature", "uint32"),
("disk_num", "uint16"),
("cdir_disk", "uint16"),
("disk_entries", "uint16"),
("cdir_entries", "uint16"),
("cdir_size", "uint32"),
("cdir_offset", "uint32"),
("comment_size", "uint16"),
]
type_mapping = { "uint32":"I", "uint16":"H"}
def format_struct (format):
string_fields = {}
fmt = "<"
for (name,value) in iter(format):
try:
fmt += type_mapping[value][0]
except KeyError:
string_fields[name] = value
return (fmt, string_fields)
def size_of(format):
return struct.calcsize(format_struct(format)[0])
class MyStruct:
def __init__(self, format, string_fields):
self.__dict__["struct_members"] = {}
self.__dict__["format"] = format
self.__dict__["string_fields"] = string_fields
def addMember(self, name, value):
self.__dict__["struct_members"][name] = value
def __getattr__(self, item):
try:
return self.__dict__["struct_members"][item]
except:
pass
print("no %s" %item)
print(self.__dict__["struct_members"])
raise AttributeError
def __setattr__(self, item, value):
if item in self.__dict__["struct_members"]:
self.__dict__["struct_members"][item] = value
else:
raise AttributeError
def pack(self):
extra_data = b""
values = []
string_fields = self.__dict__["string_fields"]
struct_members = self.__dict__["struct_members"]
format = self.__dict__["format"]
for (name,_) in format:
if name in string_fields:
if not isinstance(struct_members[name], bytes):
struct_members[name] = struct_members[name].encode('utf-8')
extra_data = extra_data + struct_members[name]
else:
values.append(struct_members[name]);
return struct.pack(format_struct(format)[0], *values) + extra_data
ENDSIG = 0x06054b50
def assert_true(cond, msg):
if not cond:
raise Exception(msg)
exit(1)
class BinaryBlob:
def __init__(self, f):
self.data = open(f, "rb").read()
self.offset = 0
self.length = len(self.data)
def readAt(self, pos, length):
self.offset = pos + length
return self.data[pos:self.offset]
def read_struct (self, format, offset = None):
if offset == None:
offset = self.offset
(fstr, string_fields) = format_struct(format)
size = struct.calcsize(fstr)
data = self.readAt(offset, size)
ret = struct.unpack(fstr, data)
retstruct = MyStruct(format, string_fields)
i = 0
for (name,_) in iter(format):
member_desc = None
if not name in string_fields:
member_data = ret[i]
i = i + 1
else:
# zip has data fields which are described by other struct fields, this does
# additional reads to fill em in
member_desc = string_fields[name]
member_data = self.readAt(self.offset, retstruct.__getattr__(member_desc))
retstruct.addMember(name, member_data)
# sanity check serialization code
data = self.readAt(offset, self.offset - offset)
out_data = retstruct.pack()
assert_true(out_data == data, "Serialization fail %d !=%d"% (len(out_data), len(data)))
return retstruct
def optimizejar(jar, outjar, inlog = None):
if inlog is not None:
inlog = open(inlog).read().rstrip()
# in the case of an empty log still move the index forward
if len(inlog) == 0:
inlog = []
else:
inlog = inlog.split("\n")
outlog = []
jarblob = BinaryBlob(jar)
dirend = jarblob.read_struct(cdir_end, jarblob.length - size_of(cdir_end))
assert_true(dirend.signature == ENDSIG, "no signature in the end");
cdir_offset = dirend.cdir_offset
readahead = 0
if inlog is None and cdir_offset == 4:
readahead = struct.unpack("<I", jarblob.readAt(0, 4))[0]
print("%s: startup data ends at byte %d" % (outjar, readahead));
total_stripped = 0;
jarblob.offset = cdir_offset
central_directory = []
for i in range(0, dirend.cdir_entries):
entry = jarblob.read_struct(cdir_entry)
if entry.filename[-1:] == "/":
total_stripped += len(entry.pack())
else:
total_stripped += entry.extrafield_size
central_directory.append(entry)
reordered_count = 0
if inlog is not None:
dup_guard = set()
for ordered_name in inlog:
if ordered_name in dup_guard:
continue
else:
dup_guard.add(ordered_name)
found = False
for i in range(reordered_count, len(central_directory)):
if central_directory[i].filename == ordered_name:
# swap the cdir entries
tmp = central_directory[i]
central_directory[i] = central_directory[reordered_count]
central_directory[reordered_count] = tmp
reordered_count = reordered_count + 1
found = True
break
if not found:
print( "Can't find '%s' in %s" % (ordered_name, jar))
outfd = open(outjar, "wb")
out_offset = 0
if inlog is not None:
# have to put central directory at offset 4 cos 0 confuses some tools.
# This also lets us specify how many entries should be preread
dirend.cdir_offset = 4
# make room for central dir + end of dir + 4 extra bytes at front
out_offset = dirend.cdir_offset + dirend.cdir_size + size_of(cdir_end) - total_stripped
outfd.seek(out_offset)
cdir_data = b""
written_count = 0
crc_mapping = {}
dups_found = 0
dupe_bytes = 0
# store number of bytes suggested for readahead
for entry in central_directory:
# read in the header twice..first for comparison, second time for convenience when writing out
jarfile = jarblob.read_struct(local_file_header, entry.offset)
assert_true(jarfile.filename == entry.filename, "Directory/Localheader mismatch")
# drop directory entries
if entry.filename[-1:] == "/":
total_stripped += len(jarfile.pack())
dirend.cdir_entries -= 1
continue
# drop extra field data
else:
total_stripped += jarfile.extra_field_size;
entry.extrafield = jarfile.extra_field = ""
entry.extrafield_size = jarfile.extra_field_size = 0
# January 1st, 2010
entry.lastmod_date = jarfile.lastmod_date = ((2010 - 1980) << 9) | (1 << 5) | 1
entry.lastmod_time = jarfile.lastmod_time = 0
data = jarfile.pack()
outfd.write(data)
old_entry_offset = entry.offset
entry.offset = out_offset
out_offset = out_offset + len(data)
entry_data = entry.pack()
cdir_data += entry_data
expected_len = entry.filename_size + entry.extrafield_size + entry.filecomment_size
assert_true(len(entry_data) != expected_len,
"%s entry size - expected:%d got:%d" % (entry.filename, len(entry_data), expected_len))
written_count += 1
if entry.crc32 in crc_mapping:
dups_found += 1
dupe_bytes += entry.compressed_size + len(data) + len(entry_data)
print("%s\n\tis a duplicate of\n%s\n---"%(entry.filename, crc_mapping[entry.crc32]))
else:
crc_mapping[entry.crc32] = entry.filename;
if inlog is not None:
if written_count == reordered_count:
readahead = out_offset
print("%s: startup data ends at byte %d"%( outjar, readahead));
elif written_count < reordered_count:
pass
#print("%s @ %d" % (entry.filename, out_offset))
elif readahead >= old_entry_offset + len(data):
outlog.append(entry.filename)
reordered_count += 1
if inlog is None:
dirend.cdir_offset = out_offset
if dups_found > 0:
print("WARNING: Found %d duplicate files taking %d bytes"%(dups_found, dupe_bytes))
dirend.cdir_size = len(cdir_data)
dirend.disk_entries = dirend.cdir_entries
dirend_data = dirend.pack()
assert_true(size_of(cdir_end) == len(dirend_data), "Failed to serialize directory end correctly. Serialized size;%d, expected:%d"%(len(dirend_data), size_of(cdir_end)));
outfd.seek(dirend.cdir_offset)
outfd.write(cdir_data)
outfd.write(dirend_data)
# for ordered jars the central directory is written in the begining of the file, so a second central-directory
# entry has to be written in the end of the file
if inlog is not None:
outfd.seek(0)
outfd.write(struct.pack("<I", readahead));
outfd.seek(out_offset)
outfd.write(dirend_data)
print("Stripped %d bytes" % total_stripped)
print("%s %d/%d in %s" % (("Ordered" if inlog is not None else "Deoptimized"),
reordered_count, len(central_directory), outjar))
outfd.close()
return outlog
if len(sys.argv) != 5:
print("Usage: --optimize|--deoptimize %s JAR_LOG_DIR IN_JAR_DIR OUT_JAR_DIR" % sys.argv[0])
exit(1)
jar_regex = re.compile("\\.jar?$")
def optimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR):
ls = os.listdir(IN_JAR_DIR)
for jarfile in ls:
if not re.search(jar_regex, jarfile):
continue
injarfile = os.path.join(IN_JAR_DIR, jarfile)
outjarfile = os.path.join(OUT_JAR_DIR, jarfile)
logfile = os.path.join(JAR_LOG_DIR, jarfile + ".log")
if not os.path.isfile(logfile):
logfile = None
optimizejar(injarfile, outjarfile, logfile)
def deoptimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR):
if not os.path.exists(JAR_LOG_DIR):
os.makedirs(JAR_LOG_DIR)
ls = os.listdir(IN_JAR_DIR)
for jarfile in ls:
if not re.search(jar_regex, jarfile):
continue
injarfile = os.path.join(IN_JAR_DIR, jarfile)
outjarfile = os.path.join(OUT_JAR_DIR, jarfile)
logfile = os.path.join(JAR_LOG_DIR, jarfile + ".log")
log = str(optimizejar(injarfile, outjarfile, None))
open(logfile, "wb").write("\n".join(log).encode('utf-8'))
def main():
MODE = sys.argv[1]
JAR_LOG_DIR = sys.argv[2]
IN_JAR_DIR = sys.argv[3]
OUT_JAR_DIR = sys.argv[4]
if MODE == "--optimize":
optimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR)
elif MODE == "--deoptimize":
deoptimize(JAR_LOG_DIR, IN_JAR_DIR, OUT_JAR_DIR)
else:
print("Unknown mode %s" % MODE)
exit(1)
if __name__ == '__main__':
main()

View file

@ -1,218 +0,0 @@
#!/usr/bin/env python3
import sys
import os
import argparse
import tempfile
import shutil
import subprocess
import re
import fileinput
from collections import OrderedDict
import json
import traceback
# Hack to combine two argparse formatters
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description='Prepare build/ files for the app build process',
formatter_class=CustomFormatter)
parser.add_argument('--source-dir', '-s', required=True, metavar='BUILD_DIR', help='Directory to build from')
parser.add_argument('--output-dir', '-o', required=True, metavar='OUTPUT_DIR', help='Directory to write files to')
parser.add_argument('-c', '--channel', default='source', help='channel to add to dev build version number (e.g., "beta" for "5.0-beta.3+a5f28ca8"), or "release" or "source" to skip')
parser.add_argument('--commit-hash', '-m', metavar='HASH', help='Commit hash (required for non-release builds)')
args = parser.parse_args()
def main():
try:
app_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
lastrev_dir = os.path.join(app_root_dir, 'lastrev')
if not os.path.exists(lastrev_dir):
os.mkdir(lastrev_dir)
tmp_dir = os.path.join(app_root_dir, 'tmp')
if args.commit_hash:
commit_hash = args.commit_hash[0:9]
elif args.channel != "release":
raise Exception("--commit-hash must be specified for non-release builds")
src_dir = args.source_dir
if not os.path.isdir(src_dir):
raise Exception(src_dir + " is not a directory")
output_dir = args.output_dir
if not os.path.isdir(output_dir):
raise Exception(output_dir + " is not a directory")
if os.listdir(output_dir):
raise Exception(output_dir + " is not empty")
log("Using source directory of " + src_dir)
os.chdir(src_dir)
if not os.path.exists('version'):
raise FileNotFoundError("version file not found in {0}".format(src_dir))
# Extract version number from version file
with open('version') as f:
rdf = f.read()
m = re.search('([0-9].+)\\.SOURCE', rdf)
if not m:
raise Exception("Version number not found in version file")
version = m.group(1)
# Remove tmp build directory if it already exists
if os.path.exists(tmp_dir):
shutil.rmtree(tmp_dir)
os.mkdir(tmp_dir)
tmp_src_dir = os.path.join(tmp_dir, 'zotero')
# Export a clean copy of the source tree -- with Python on Windows, where rsync,
# if present at all, doesn't handle Windows-style paths
if os.name == 'nt' or not shutil.which('rsync'):
shutil.copytree(
'.',
tmp_src_dir,
ignore=shutil.ignore_patterns('.*', '#*', 'package.json', 'package-lock.json')
)
else:
subprocess.check_call([
'rsync',
'-aL',
# Exclude hidden files
'--exclude', '.*',
'--exclude', '#*',
'--exclude', 'package.json',
'--exclude', 'package-lock.json',
'.' + os.sep,
tmp_src_dir + os.sep
])
# Make sure the copy worked
d = os.path.join(tmp_src_dir, 'chrome')
if not os.path.isdir(d):
raise FileNotFoundError(d + " not found")
# Delete CSL locale support files
csl_dir = os.path.normpath(tmp_src_dir + '/chrome/content/zotero/locale/csl/')
for root, dirs, files in os.walk(csl_dir, topdown=False):
for name in files:
if not name.endswith('.xml') and name != 'locales.json':
os.remove(os.path.join(root, name))
for name in dirs:
d = os.path.join(root, name)
if not os.listdir(d):
os.rmdir(d)
# Delete styles build script
os.remove(os.path.join(tmp_src_dir, 'styles', 'update'))
translators_dir = os.path.join(tmp_src_dir, 'translators')
# Move deleted.txt out of translators directory
f = os.path.join(translators_dir, 'deleted.txt')
if os.path.exists(f):
shutil.move(f, tmp_src_dir)
# Build translator index
index = OrderedDict()
for fn in sorted((fn for fn in os.listdir(translators_dir)), key=str.lower):
if not fn.endswith('.js'):
continue
with open(os.path.join(translators_dir, fn), 'r', encoding='utf-8') as f:
contents = f.read()
# Parse out the JSON metadata block
m = re.match(r'^\s*{[\S\s]*?}\s*?[\r\n]', contents)
if not m:
raise Exception("Metadata block not found in " + f.name)
metadata = json.loads(m.group(0))
index[metadata["translatorID"]] = {
"fileName": fn,
"label": metadata["label"],
"lastUpdated": metadata["lastUpdated"]
}
# Write translator index as JSON file
with open(os.path.join(tmp_src_dir, 'translators.json'), 'w', encoding='utf-8') as f:
json.dump(index, f, indent=True, ensure_ascii=False)
version_file = os.path.join(tmp_src_dir, 'version')
log('')
log_line()
log('Original version:\n')
dump_file(version_file)
# Modify version as necessary
# The dev build revision number is stored in lastrev/{version}-{channel}.
#
# If we're including it, get the current version number and increment it.
if args.channel not in ["release", "source"]:
lastrev_file = os.path.join(
lastrev_dir, '{0}-{1}'.format(version, args.channel)
)
if not os.path.exists(lastrev_file):
with open(lastrev_file, 'w') as f:
f.write("0")
rev = 1
else:
with open(lastrev_file, 'r') as f:
rev = f.read()
rev = int(rev if rev else 0) + 1
if args.channel == "release":
rev_sub_str = ""
elif args.channel == "source":
rev_sub_str = ".SOURCE.{0}".format(commit_hash)
else:
rev_sub_str = "-{0}.{1}+{2}".format(args.channel, str(rev), commit_hash)
# Update version
for line in fileinput.FileInput(version_file, inplace=1):
line = line.replace('.SOURCE', rev_sub_str)
print(line, file=sys.stdout, end='')
log('Modified version:\n')
dump_file(version_file)
log('')
log_line()
# Move source files to output directory
os.rmdir(output_dir)
shutil.move(tmp_src_dir, output_dir)
# Update lastrev file with new revision number
if args.channel not in ["release", "source"]:
with open(lastrev_file, 'w') as f:
f.write(str(rev))
return 0
except Exception as err:
sys.stderr.write("\n" + traceback.format_exc())
return 1
# Clean up
finally:
if 'tmp_src_dir' in locals() and os.path.exists(tmp_src_dir):
shutil.rmtree(tmp_src_dir)
def dump_file(f):
with open(f, 'r') as f:
log(f.read())
def log(msg):
print(msg, file=sys.stdout)
def log_line():
log('======================================================\n\n')
if __name__ == '__main__':
sys.exit(main())

View file

@ -1,46 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
. "$APP_ROOT_DIR/config.sh"
CHANNEL="test"
BRANCH="main"
if [ "`uname`" = "Darwin" ]; then
export SAFARI_APPEX="$ROOT_DIR/../safari-web-extension-builds/ZoteroSafariExtension.appex"
export SAFARI_EXT_RESOURCES="$ROOT_DIR/../safari-web-extension-builds/test/safari"
export SAFARI_APP_EXTENSION="$ROOT_DIR/../safari-app-extension-builds/test/ZoteroSafariExtension.appex"
if [ ! -d "$SAFARI_APPEX" ]; then
echo "Safari extension stub not found at $SAFARI_APPEX -- aborting" >&2
exit 1
fi
if [ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]; then
echo "Safari web extension not found in $SAFARI_EXT_RESOURCES -- aborting" >&2
exit 1
fi
if [ ! -d "$SAFARI_APP_EXTENSION" ]; then
echo "Safari App Extension not found at $SAFARI_APP_EXTENSION -- aborting" >&2
exit 1
fi
fi
cd "$SCRIPT_DIR"
./check_requirements
hash=`./get_repo_branch_hash $BRANCH`
./check_app_matches_commit $hash
source_dir=`./get_commit_files $hash`
build_dir=`mktemp -d`
function cleanup {
rm -rf "$source_dir"
rm -rf "$build_dir"
}
trap cleanup EXIT
./prepare_build -s "$source_dir" -o "$build_dir" -c $CHANNEL -m $hash
VERSION="`cat \"$build_dir/version\"`"
./build_for_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL -i 1
ssh $DEPLOY_HOST "$DEPLOY_PATH/deploy" $CHANNEL "$VERSION" $BUILD_PLATFORMS

View file

@ -1,28 +0,0 @@
#!/bin/bash
#
# Upload build archives from 'dist' to S3 with the specified channel and version
#
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
function usage {
echo Usage: $0 CHANNEL VERSION >&2
exit 1
}
CHANNEL="${1:-}"
VERSION="${2:-}"
if [[ -z "$CHANNEL" ]] || [[ -z "$VERSION" ]]; then
usage
fi
source_dir="$DIST_DIR"
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
source_dir=$(cygpath -w "$source_dir")
fi
url="s3://$S3_BUCKET/$S3_DIST_PATH/$CHANNEL/$VERSION/"
aws s3 sync --exclude "files-*" --exclude build_id "$source_dir" $url

View file

@ -1,187 +0,0 @@
get_current_platform() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo l
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo m
elif [[ "$OSTYPE" == "cygwin" ]]; then
echo w
elif [[ "$OSTYPE" == "msys" ]]; then
echo w
# Unknown, so probably Unix-y
else
echo l
fi
}
get_canonical_arch() {
local _platform=$1
local _arch=$2
case $_platform in
w)
case $_arch in
x64) _arch="win-x64" ;;
arm64) _arch="win-arm64" ;;
win32|win-x64|win-arm64) ;;
*) echo "Invalid Windows archicture: $_arch" >&2;;
esac
echo $_arch
;;
l)
[[ $_arch == x64 ]] && _arch="x86_64"
echo $_arch
;;
*)
echo "Invalid platform '$platform'" 2>&1
exit 1
esac
}
# Print a sorted NUL-separated list of the files in the current directory to
# include in the build manifest. Follows symlinks, like the rsync calls in
# prepare_build and build.sh.
function build_manifest_file_list {
# Exclusions match the rsync call in prepare_build
find -L . -mindepth 1 \( -name '.*' -o -name '#*' \) -prune \
-o -type f ! -name 'package.json' ! -name 'package-lock.json' -print0 \
| LC_ALL=C sort -z
}
# Read a NUL-separated file list on stdin and print "size|mtime<TAB>path" for
# each file
function stat_file_list {
if [[ "$OSTYPE" == "darwin"* ]]; then
xargs -0 stat -L -f '%z|%Fm|%N'
else
xargs -0 stat -L -c '%s|%y|%n'
fi | sed -E 's,^([0-9]+[|][^|]+)[|](\./)?,\1'$'\t'','
}
# Read a NUL-separated file list on stdin and print "hash<TAB>path" for each
# file
function hash_file_list {
local md5_cmd
if command -v md5sum > /dev/null 2>&1; then
md5_cmd="md5sum"
else
md5_cmd="md5 -r"
fi
xargs -0 $md5_cmd | sed -E 's,^([0-9a-f]{32}) ?(\./)?,\1'$'\t'','
}
# Generate a manifest of a build directory for incremental updates, with one
# "hash<TAB>size|mtime<TAB>path" line per file, sorted by path
function generate_build_manifest {
local dir=$1
(
cd "$dir"
local file_list
file_list=$(mktemp)
build_manifest_file_list > "$file_list"
paste \
<(hash_file_list < "$file_list" | cut -f1) \
<(stat_file_list < "$file_list")
rm -f "$file_list"
)
}
# Generate a hash of the files in app/ that affect build output, for detecting
# when a staged build can't be updated incrementally. Since some of the files
# are large binaries, use file sizes and modification times rather than
# contents. The xulrunner runtimes are covered by the hash-* files written by
# fetch_xulrunner.
function generate_app_hash {
local app_dir=$1
local md5_cmd
if command -v md5sum > /dev/null 2>&1; then
md5_cmd="md5sum"
else
md5_cmd="md5 -r"
fi
(
cd "$app_dir"
local paths=(assets build.sh config.sh scripts mac win linux modules update-packaging)
if [ -f config-custom.sh ]; then
paths+=(config-custom.sh)
fi
{
if [[ "$OSTYPE" == "darwin"* ]]; then
find -L "${paths[@]}" -name '.*' -prune -o -type f -print0 | xargs -0 stat -L -f '%N|%z|%m'
else
find -L "${paths[@]}" -name '.*' -prune -o -type f -print0 | xargs -0 stat -L -c '%n|%s|%Y'
fi
cat xulrunner/hash-* 2> /dev/null || true
} | LC_ALL=C sort | $md5_cmd | awk '{print $1}'
)
}
function check_line {
pattern=$1
file=$2
if ! grep -E -q "$pattern" "$file"; then
echo "$pattern" not found in "$file" -- aborting 2>&1
exit 1
fi
}
function replace_line {
pattern=$1
replacement=$2
file=$3
if grep -E -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 grep -E -q "$pattern" "$file"; then
grep -E -v "$pattern" "$file" > "$file.tmp"
mv "$file.tmp" "$file"
else
echo "$pattern" not found in "$file" -- aborting 2>&1
exit 1
fi
}
function remove_between {
start_pattern=$1
end_pattern=$2
file=$3
if grep -E -q "$start_pattern" "$file" && grep -E -q "$end_pattern" "$file"; then
perl -ni -e '
if (/'"$start_pattern"'/) { $skip = 1; next; }
elsif ($skip && /'"$end_pattern"'/) { $skip = 0; next; }
print unless $skip;' "$file"
else
echo "$start_pattern" and "$end_pattern" not found in "$file" -- aborting 2>&1
exit 1
fi
}
# Set $notary_auth to notarytool authentication arguments: the keychain profile in
# NOTARIZATION_PROFILE if set, or else the Apple ID and app-specific password
function prepare_notary_auth {
if [[ -n "${NOTARIZATION_PROFILE:-}" ]]; then
notary_auth=(--keychain-profile "$NOTARIZATION_PROFILE")
if [[ -n "$KEYCHAIN" ]]; then
keychain_path="$HOME/Library/Keychains/$KEYCHAIN.keychain-db"
# The keychain may have auto-locked since signing
if [[ -n "$KEYCHAIN_PASSWORD" ]]; then
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
fi
notary_auth+=(--keychain "$keychain_path")
fi
else
notary_auth=(--apple-id "$NOTARIZATION_USER" --team-id "$NOTARIZATION_TEAM_ID" --password "$NOTARIZATION_PASSWORD")
fi
}

View file

@ -1,95 +0,0 @@
#!/bin/bash
# Calculates a hash for the processed Firefox package for a given platform and architecture
#
# Usage:
# xulrunner_hash.sh -p <m|w|l> [-a ARCH]
#
# * **Mac (m)** ‑a is disallowed (universal build).
# * **Windows (w)** ‑a is **required**. Accepted values:
# win32 | x64 | arm64
# * **Linux (l)** ‑a is **required**. Accepted values:
# x86_64 | arm64 (alias x64 → x86_64)
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
cd "$APP_ROOT_DIR"
platform=""; arch=""
usage() {
cat >&2 <<'DONE'
Usage: xulrunner_hash.sh -p <m|w|l> [-a ARCH]
-p PLATFORM m (mac), w (windows), l (linux) [required]
-a ARCH *required* for Windows & Linux builds
Windows: x64 | arm64 | win32
Linux: x64 (or x86_64) | arm64
Mac: disallowed – always universal
DONE
exit 1
}
while getopts ':p:a:' opt; do
case $opt in
p) platform="$OPTARG";;
a) arch="$OPTARG";;
:) echo "Option -$OPTARG requires an argument" >&2; usage;;
? ) echo "Invalid option: -$OPTARG" >&2; echo; usage;;
esac
shift $((OPTIND-1)); OPTIND=1
done
[[ -z $platform ]] && { echo "-p is required" >&2; echo; usage; }
case $platform in
m)
[[ -n $arch ]] && { echo "-a is not allowed for Mac (universal build)" >&2; echo; usage; }
arch="universal"
GECKO_VERSION="$GECKO_VERSION_MAC"
;;
w)
[[ -z $arch ]] && { echo "-a is required for Windows builds" >&2; echo; usage; }
# Map CLI-friendly aliases → canonical form
case $arch in
x64) arch="win-x64" ;;
arm64) arch="win-arm64" ;;
win32|win-x64|win-arm64) ;; # already canonical
*) echo "Invalid Windows arch: $arch" >&2; echo; usage;;
esac
GECKO_VERSION="$GECKO_VERSION_WIN"
;;
l)
[[ -z $arch ]] && { echo "-a is required for Linux builds" >&2; echo; usage; }
[[ $arch == x64 ]] && arch="x86_64"
case $arch in
x86_64|arm64) ;;
*) echo "Invalid Linux arch: $arch" >&2; echo; usage;;
esac
GECKO_VERSION="$GECKO_VERSION_LINUX"
;;
*)
echo "Invalid platform: $platform" >&2; echo; usage;;
esac
# Concatenate scripts that affect the build
xulrunner_content=$(< "$SCRIPT_DIR/fetch_xulrunner")
xulrunner_content+=$(< "$SCRIPT_DIR/utils.sh")
xulrunner_content+=$(< "$APP_ROOT_DIR/assets/multilocale.txt")
# Get custom components hash
if [[ $platform = "m" ]]; then
components_hash="$custom_components_hash_mac"
else
hash_arch_name="${arch/-/_}"
arch_var_name="custom_components_hash_${hash_arch_name}"
components_hash=${!arch_var_name:-}
fi
input_string="${platform}-${arch}-${components_hash}|${GECKO_VERSION}|${xulrunner_content}"
printf "%s" "$input_string" | openssl dgst -sha256 | awk '{print $NF}'

View file

@ -1,411 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
UPDATE_STAGE_DIR="$SCRIPT_DIR/staging"
function usage {
cat >&2 <<DONE
Usage: $0 -c CHANNEL [-f] [-i FROM_VERSION] [-p PLATFORMS] [-l] VERSION
Options
-c CHANNEL Release channel ('release', 'beta')
-f Perform full build
-i FROM_VERSION Perform incremental build
-p PLATFORMS Platforms to build (m=Mac, w=Windows, l=Linux)
-l Use local TO directory instead of downloading TO files from S3
DONE
exit 1
}
# From https://gist.github.com/cdown/1163649#gistcomment-1639097
urlencode() {
local LANG=C
local length="${#1}"
for (( i = 0; i < length; i++ )); do
local c="${1:i:1}"
case $c in
[a-zA-Z0-9.~_-]) printf "$c" ;;
*) printf '%%%02X' "'$c" ;;
esac
done
}
BUILD_FULL=0
BUILD_INCREMENTAL=0
FROM=""
CHANNEL=""
BUILD_MAC=0
BUILD_WIN=0
BUILD_LINUX=0
USE_LOCAL_TO=0
while getopts "i:c:p:fl" opt; do
case $opt in
i)
FROM="$OPTARG"
BUILD_INCREMENTAL=1
;;
c)
CHANNEL="$OPTARG"
;;
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
;;
f)
BUILD_FULL=1
;;
l)
USE_LOCAL_TO=1
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
shift $(($OPTIND - 1))
TO=${1:-}
if [ -z "$TO" ]; then
usage
fi
if [ -z "$FROM" ] && [ $BUILD_FULL -eq 0 ]; then
usage
fi
if [[ -z "$CHANNEL" ]]; then
echo "Channel not provided" >&2
exit 1
fi
# Require at least one platform
if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; then
usage
fi
TO_MAJOR_VERSION="${TO%%.*}"
rm -rf "$UPDATE_STAGE_DIR"
mkdir "$UPDATE_STAGE_DIR"
INCREMENTALS_FOUND=0
for version in "$FROM" "$TO"; do
if [[ $version == "$TO" ]] && [[ $INCREMENTALS_FOUND == 0 ]] && [[ $BUILD_FULL == 0 ]]; then
exit
fi
if [ -z "$version" ]; then
continue
fi
echo "Getting Zotero version $version"
versiondir="$UPDATE_STAGE_DIR/$version"
#
# If -l passed, use main build script's staging directory for TO files rather than downloading
# the given version.
#
# The caller must ensure that the files in ../staging match the platforms and version given.
if [[ $version == $TO && $USE_LOCAL_TO == "1" ]]; then
if [ ! -d "$STAGE_DIR" ]; then
echo "Can't find local TO dir $STAGE_DIR"
exit 1
fi
echo "Using files from $STAGE_DIR"
ln -s "$STAGE_DIR" "$versiondir"
continue
fi
#
# Otherwise, download version from S3
#
mkdir -p "$versiondir"
cd "$versiondir"
# Determine Linux archive extension by major Zotero version
MAJOR_VERSION="${version%%.*}"
if [ "$MAJOR_VERSION" -lt 8 ]; then
LINUX_EXT="tar.bz2"
else
LINUX_EXT="tar.xz"
fi
MAC_ARCHIVE="Zotero-${version}.dmg"
WIN32_ARCHIVE="Zotero-${version}_win32.zip"
WIN_X64_ARCHIVE="Zotero-${version}_win-x64.zip"
WIN_ARM64_ARCHIVE="Zotero-${version}_win-arm64.zip"
LINUX_X86_ARCHIVE="Zotero-${version}_linux-i686.${LINUX_EXT}"
LINUX_X86_64_ARCHIVE="Zotero-${version}_linux-x86_64.${LINUX_EXT}"
LINUX_ARM64_ARCHIVE="Zotero-${version}_linux-arm64.${LINUX_EXT}"
CACHE_DIR="$ROOT_DIR/cache"
if [ ! -e "$CACHE_DIR" ]; then
mkdir "$CACHE_DIR"
fi
# Build archive list (no Linux arm64 before Zotero 8, no Linux i686 from Zotero 11)
ARCHIVES=(
"$MAC_ARCHIVE"
"$WIN32_ARCHIVE"
"$WIN_X64_ARCHIVE"
"$WIN_ARM64_ARCHIVE"
"$LINUX_X86_64_ARCHIVE"
)
if [ "$TO_MAJOR_VERSION" -lt 11 ]; then
ARCHIVES+=("$LINUX_X86_ARCHIVE")
fi
if [ "$MAJOR_VERSION" -ge 8 ]; then
ARCHIVES+=("$LINUX_ARM64_ARCHIVE")
fi
for archive in "${ARCHIVES[@]}"; do
if [[ $archive = "$MAC_ARCHIVE" ]] && [[ $BUILD_MAC != 1 ]]; then
continue
fi
if [[ $archive = "$WIN32_ARCHIVE" ]] && [[ $BUILD_WIN != 1 ]]; then
continue
fi
if [[ $archive = "$WIN_X64_ARCHIVE" ]] && [[ $BUILD_WIN != 1 ]]; then
continue
fi
if [[ $archive = "$WIN_ARM64_ARCHIVE" ]] && [[ $BUILD_WIN != 1 ]]; then
continue
fi
if [[ $archive = "$LINUX_X86_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue
fi
if [[ $archive = "$LINUX_X86_64_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue
fi
if [[ $archive = "$LINUX_ARM64_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue
fi
ETAG_FILE="$CACHE_DIR/$archive.etag"
# Check cache for archive
if [[ -f "$CACHE_DIR/$archive" ]] && [[ -f "$CACHE_DIR/$archive.etag" ]]; then
ETAG="`cat $ETAG_FILE | tr '\n' ' '`"
else
ETAG=""
fi
rm -f $archive
# URL-encode '+' in beta version numbers
ENCODED_VERSION=`urlencode $version`
ENCODED_ARCHIVE=`urlencode $archive`
URL="https://$S3_BUCKET.s3.amazonaws.com/$S3_DIST_PATH/$CHANNEL/$ENCODED_VERSION/$ENCODED_ARCHIVE"
echo "Fetching $URL"
set +e
# Cached version is available
if [ -n "$ETAG" ]; then
HTTP_CODE=$(curl -sS -L -D headers.txt -o $archive -w '%{http_code}' -H "If-None-Match: $ETAG" "$URL")
NEW_ETAG=$(awk 'tolower($1) == "etag:" {print $2}' headers.txt | tr -d '\r' | tail -n 1)
rm -f headers.txt
# If ETag didn't match, cache newly downloaded version
if [ "$HTTP_CODE" = 200 ]; then
echo "ETag for $archive didn't match! -- using new version"
rm -f "$CACHE_DIR/$archive.etag"
cp $archive "$CACHE_DIR/"
echo "$NEW_ETAG" > "$CACHE_DIR/$archive.etag"
# If ETag matched (or there was another error), use cached version
else
echo "Using cached $archive"
cp "$CACHE_DIR/$archive" .
fi
else
curl -sS -f -L -D headers.txt -o $archive "$URL"
NEW_ETAG=$(awk 'tolower($1) == "etag:" {print $2}' headers.txt | tr -d '\r' | tail -n 1)
rm -f headers.txt
# Save archive to cache
rm -f "$CACHE_DIR/$archive.etag"
cp $archive "$CACHE_DIR/"
echo "$NEW_ETAG" > "$CACHE_DIR/$archive.etag"
fi
set -e
done
# Delete cached archives older than 14 days
find "$CACHE_DIR" -maxdepth 1 -type f -ctime +14 -delete
# Delete cached diffs and compressed files not created or used in 60 days
# (cache hits touch the entries)
if [ -d "$CACHE_DIR/update-packaging" ]; then
find "$CACHE_DIR/update-packaging" -type f -ctime +60 -delete
fi
# Unpack Zotero.app
if [ $BUILD_MAC == 1 ]; then
if [ -f "$MAC_ARCHIVE" ]; then
set +e
hdiutil detach -quiet /Volumes/Zotero 2>/dev/null
set -e
hdiutil attach -quiet "$MAC_ARCHIVE"
cp -R /Volumes/Zotero/Zotero.app "$versiondir"
rm "$MAC_ARCHIVE"
hdiutil detach -quiet /Volumes/Zotero
INCREMENTALS_FOUND=1
else
echo "$MAC_ARCHIVE not found"
fi
fi
# Unpack Windows zips
if [ $BUILD_WIN == 1 ]; then
if [[ -f "$WIN32_ARCHIVE" ]] && [[ -f "$WIN_X64_ARCHIVE" ]] && [[ -f "$WIN_ARM64_ARCHIVE" ]]; then
# Unpack architectures in parallel
UNPACK_PIDS=""
for build in "$WIN32_ARCHIVE" "$WIN_X64_ARCHIVE" "$WIN_ARM64_ARCHIVE"; do
(unzip -q "$build" && rm "$build") &
UNPACK_PIDS="$UNPACK_PIDS $!"
done
for pid in $UNPACK_PIDS; do
wait $pid
done
INCREMENTALS_FOUND=1
else
echo "$WIN32_ARCHIVE/$WIN_X64_ARCHIVE/$WIN_ARM64_ARCHIVE not found"
fi
fi
# Unpack Linux tarballs
if [ $BUILD_LINUX == 1 ]; then
# Zotero 7 has no Linux arm64 build; Zotero 11 has no Linux i686 build
LINUX_BUILDS_TO_UNPACK=("$LINUX_X86_64_ARCHIVE")
if [ "$TO_MAJOR_VERSION" -lt 11 ]; then
LINUX_BUILDS_TO_UNPACK+=("$LINUX_X86_ARCHIVE")
fi
if [ "$MAJOR_VERSION" -ge 8 ]; then
LINUX_BUILDS_TO_UNPACK+=("$LINUX_ARM64_ARCHIVE")
fi
MISSING=0
for build in "${LINUX_BUILDS_TO_UNPACK[@]}"; do
if [ ! -f "$build" ]; then
MISSING=1
break
fi
done
if [ "$MISSING" -eq 0 ]; then
# Unpack architectures in parallel
UNPACK_PIDS=""
for build in "${LINUX_BUILDS_TO_UNPACK[@]}"; do
(tar -xf "$build" && rm "$build") &
UNPACK_PIDS="$UNPACK_PIDS $!"
done
for pid in $UNPACK_PIDS; do
wait $pid
done
INCREMENTALS_FOUND=1
else
echo "Linux tarballs not found: ${LINUX_BUILDS_TO_UNPACK[*]}"
fi
fi
echo
done
# Set variables for mar command in make_(incremental|full)_update.sh
export MOZ_PRODUCT_VERSION="$TO"
export MAR_CHANNEL_ID="$CHANNEL"
# Reuse diffs and compressed files across incremental builds, since FROM
# versions often share files with each other and with the TO version. Entries
# that haven't been used recently are cleaned up above.
export UPDATE_CACHE_DIR="$ROOT_DIR/cache/update-packaging"
CHANGES_MADE=0
for build in "mac" "win32" "win-x64" "win-arm64" "linux-i686" "linux-x86_64" "linux-arm64"; do
# Zotero 7 has no Linux arm64 builds
if [[ $build == "linux-arm64" ]] && [[ $FROM == 7.* ]]; then
continue
fi
# Zotero 11 has no Linux i686 builds, so there's nothing to update to
if [[ $build == "linux-i686" ]] && [[ $TO_MAJOR_VERSION -ge 11 ]]; then
continue
fi
if [[ $build == "mac" ]]; then
if [[ $BUILD_MAC == 0 ]]; then
continue
fi
dir="Zotero.app"
else
if ([[ $build == "win32" ]] || [[ $build == "win-x64" ]] || [[ $build == "win-arm64" ]]) && [[ $BUILD_WIN == 0 ]]; then
continue
fi
if ([[ $build == "linux-i686" ]] || [[ $build == "linux-x86_64" ]] || [[ $build == "linux-arm64" ]]) && [[ $BUILD_LINUX == 0 ]]; then
continue
fi
dir="Zotero_$build"
fi
if [[ $BUILD_INCREMENTAL == 1 ]] && [[ -d "$UPDATE_STAGE_DIR/$FROM/$dir" ]]; then
echo
echo "Building incremental $build update from $FROM to $TO"
"$SCRIPT_DIR/make_incremental_update.sh" "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar" "$UPDATE_STAGE_DIR/$FROM/$dir" "$UPDATE_STAGE_DIR/$TO/$dir"
CHANGES_MADE=1
# If it's an incremental patch from a 6.0 build, use bzip instead of xz
if [[ $FROM = 6.0* ]]; then
echo "Building bzip2 version of incremental $build update from $FROM to $TO"
"$SCRIPT_DIR/xz_to_bzip" "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar" "$DIST_DIR/Zotero-${TO}-${FROM}_${build}_bz.mar"
rm "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar"
mv "$DIST_DIR/Zotero-${TO}-${FROM}_${build}_bz.mar" "$DIST_DIR/Zotero-${TO}-${FROM}_$build.mar"
fi
fi
if [[ $BUILD_FULL == 1 ]]; then
echo
echo "Building full $build update for $TO"
"$SCRIPT_DIR/make_full_update.sh" "$DIST_DIR/Zotero-${TO}-full_$build.mar" "$UPDATE_STAGE_DIR/$TO/$dir"
CHANGES_MADE=1
# Make a bzip version of all complete patches for serving to <7.0 builds. Don't bother with
# architectures that didn't exist in Z6.
#
# We can stop this once we do a waterfall build that all older versions get updated to.
if [[ $build != "win-x64" ]] && [[ $build != "win-arm64" ]]; then
echo "Building bzip2 version of full $build update for $TO"
"$SCRIPT_DIR/xz_to_bzip" "$DIST_DIR/Zotero-${TO}-full_$build.mar" "$DIST_DIR/Zotero-${TO}-full_bz_${build}.mar"
fi
fi
done
rm -rf "$UPDATE_STAGE_DIR"
# Update file manifests
if [ $CHANGES_MADE -eq 1 ]; then
# Cygwin has sha512sum, macOS has shasum, Linux has both
if [[ -n "`which sha512sum 2> /dev/null`" ]]; then
SHACMD="sha512sum"
else
SHACMD="shasum -a 512"
fi
cd "$DIST_DIR"
for platform in "mac" "win" "linux"; do
file=files-$platform
rm -f $file
for fn in `find . -name "*$platform*.mar" -exec basename {} \;`; do
size=`wc -c "$fn" | awk '{print $1}'`
hash=`$SHACMD "$fn" | awk '{print $1}'`
echo $fn $hash $size >> $file
done
done
fi

View file

@ -1,450 +0,0 @@
#!/bin/bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Code shared by update packaging scripts.
# Author: Darin Fisher
#
# -----------------------------------------------------------------------------
# Preserve a value set by the parent so it survives in the child processes
# spawned by run_parallel_tasks()
QUIET=${QUIET:-0}
# By default just assume that these tools exist on our path
MAR=${MAR:-mar}
MBSDIFF=${MBSDIFF:-mbsdiff}
XZ=${XZ:-xz}
$XZ --version > /dev/null 2>&1
if [ $? -ne 0 ]; then
# If $XZ is not set and not found on the path then this is probably
# running on a windows buildbot. Some of the Windows build systems have
# xz.exe in topsrcdir/xz/. Look in the places this would be in both a
# mozilla-central and comm-central build.
XZ="$(dirname "$(dirname "$(dirname "$0")")")/xz/xz.exe"
$XZ --version > /dev/null 2>&1
if [ $? -ne 0 ]; then
XZ="$(dirname "$(dirname "$(dirname "$(dirname "$0")")")")/xz/xz.exe"
$XZ --version > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "xz was not found on this system!"
echo "exiting"
exit 1
fi
fi
fi
# Ensure that we're always using the right compression settings.
# -T1 (single-threaded) is required: multi-threaded xz produces non-deterministic
# output, which would break both the reproducibility of MARs and the content-hash
# cache in process_update_task(). See get_cache_tag() for how changes here are
# accounted for in the cache.
export XZ_OPT="-T1 -7e"
# -----------------------------------------------------------------------------
# Helper routines
notice() {
echo "$*" 1>&2
}
verbose_notice() {
if [ $QUIET -eq 0 ]; then
notice "$*"
fi
}
get_file_size() {
info=($(ls -ln "$1"))
echo ${info[4]}
}
copy_perm() {
reference="$1"
target="$2"
if [ -x "$reference" ]; then
chmod 0755 "$target"
else
chmod 0644 "$target"
fi
}
make_add_instruction() {
f="$1"
filev3="$2"
# Used to log to the console
if [ $4 ]; then
forced=" (forced)"
else
forced=
fi
# Changed by Zotero for -e
is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') || true
if [ $is_extension = "1" ]; then
# Use the subdirectory of the extensions folder as the file to test
# before performing this add instruction.
testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/')
verbose_notice " add-if \"$testdir\" \"$f\""
echo "add-if \"$testdir\" \"$f\"" >> "$filev3"
else
verbose_notice " add \"$f\"$forced"
echo "add \"$f\"" >> "$filev3"
fi
}
check_for_add_if_not_update() {
add_if_not_file_chk="$1"
if [[ "$(basename "$add_if_not_file_chk")" = "channel-prefs.js" || \
"$add_if_not_file_chk" =~ (^|/)ChannelPrefs\.framework/ || \
"$(basename "$add_if_not_file_chk")" = "update-settings.ini" || \
"$add_if_not_file_chk" =~ (^|/)UpdateSettings\.framework/ ]]; then
## "true"
return 0;
fi
## "false"
return 1;
}
make_add_if_not_instruction() {
f="$1"
filev3="$2"
verbose_notice " add-if-not \"$f\" \"$f\""
echo "add-if-not \"$f\" \"$f\"" >> "$filev3"
}
make_patch_instruction() {
f="$1"
filev3="$2"
# Changed by Zotero for -e
is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') || true
if [ $is_extension = "1" ]; then
# Use the subdirectory of the extensions folder as the file to test
# before performing this add instruction.
testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/')
verbose_notice " patch-if \"$testdir\" \"$f.patch\" \"$f\""
echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> "$filev3"
else
verbose_notice " patch \"$f.patch\" \"$f\""
echo "patch \"$f.patch\" \"$f\"" >> "$filev3"
fi
}
append_remove_instructions() {
dir="$1"
filev3="$2"
if [ -f "$dir/removed-files" ]; then
listfile="$dir/removed-files"
elif [ -f "$dir/Contents/Resources/removed-files" ]; then
listfile="$dir/Contents/Resources/removed-files"
fi
if [ -n "$listfile" ]; then
# Changed by Zotero: Use subshell and disable filename globbing to prevent bash from expanding
# entries in removed-files with paths from the root (e.g., 'xulrunner/*')
(
set -f
# Map spaces to pipes so that we correctly handle filenames with spaces.
files=($(cat "$listfile" | tr " " "|" | sort -r))
num_files=${#files[*]}
for ((i=0; $i<$num_files; i=$i+1)); do
# Map pipes back to whitespace and remove carriage returns
f=$(echo ${files[$i]} | tr "|" " " | tr -d '\r')
# Trim whitespace
f=$(echo $f)
# Exclude blank lines.
if [ -n "$f" ]; then
# Exclude comments
if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then
if [ $(echo "$f" | grep -c '/$') = 1 ]; then
verbose_notice " rmdir \"$f\""
echo "rmdir \"$f\"" >> "$filev3"
elif [ $(echo "$f" | grep -c '/\*$') = 1 ]; then
# Remove the *
f=$(echo "$f" | sed -e 's:\*$::')
verbose_notice " rmrfdir \"$f\""
echo "rmrfdir \"$f\"" >> "$filev3"
else
verbose_notice " remove \"$f\""
echo "remove \"$f\"" >> "$filev3"
fi
fi
fi
done
)
fi
}
# Return the number of parallel jobs to use for diffing/compression, from
# UPDATE_PACKAGING_JOBS or the number of cores
get_parallel_jobs() {
if [ -n "${UPDATE_PACKAGING_JOBS:-}" ]; then
echo "$UPDATE_PACKAGING_JOBS"
return 0
fi
nproc 2>/dev/null && return 0
sysctl -n hw.ncpu 2>/dev/null && return 0
echo 1
}
# Run per-file tasks from a task file in parallel by re-invoking the calling
# script with --run-task for each line. Tasks are ordered largest-file-first so
# that long-running jobs (e.g., diffing xul) start as early as possible.
#
# $1 - task file, with one "<type><TAB><relative path>" task per line
# $2 - directory to measure file sizes against for job ordering
# $3 - script to re-invoke (callers pass "$0" and handle --run-task before
# option parsing by calling process_update_task)
#
# Callers must export the variables needed by process_update_task().
run_parallel_tasks() {
local taskfile="$1"
local basedir="$2"
local script="$3"
local jobs
if [ ! -s "$taskfile" ]; then
return 0
fi
# xargs requires a resolvable command path
script="$(cd "$(dirname "$script")" && pwd)/$(basename "$script")"
# Compute the cache tag once here and export it so the per-file child
# processes inherit it rather than recomputing it for every file
if [ -n "${UPDATE_CACHE_DIR:-}" ] && [ -z "${UPDATE_CACHE_TAG:-}" ]; then
UPDATE_CACHE_TAG="$(get_cache_tag)"
export UPDATE_CACHE_TAG
fi
jobs=$(get_parallel_jobs)
notice "Processing $(wc -l < "$taskfile" | tr -d ' ') files with $jobs parallel jobs"
(cd "$basedir" && du -ak .) > "$taskfile.sizes"
awk -F'\t' 'NR==FNR { sizes[substr($2, 3)] = $1; next } { print sizes[$2] "\t" $0 }' \
"$taskfile.sizes" "$taskfile" \
| sort -rn \
| cut -f 2- \
| tr '\n' '\000' \
| xargs -0 -n 1 -P "$jobs" "$script" --run-task
}
# -----------------------------------------------------------------------------
# Diff and compression cache
#
# Building updates repeats the same expensive work: the same file contents get
# xz-compressed once per architecture, per FROM version, and per release, and
# the same before/after file pair gets binary-diffed again and again. When
# UPDATE_CACHE_DIR is set, we cache the results by content hash and reuse them
# across builds. Two kinds of entry are stored:
#
# full-<tag>-<hash>.xz A single file compressed with xz, keyed
# on the file's contents. Reused whenever
# the same content shows up again, in any
# architecture, version, or release.
#
# patch-<tag>-<oldhash>-<newhash>.xz The compressed binary diff between a
# file's old and new contents, keyed on
# both. Reused whenever the same
# before/after pair recurs (e.g., a file
# unchanged across several FROM versions
# being patched to the same TO).
#
# Both entries are written even when only one is ultimately packaged, so a file
# that loses the patch-vs-full size comparison in one build still seeds the
# cache for the next. <tag> is get_cache_tag(), so a toolchain/settings change
# starts a fresh namespace rather than reusing stale output. Entries are touched
# on use and expire via the time-based cleanup in build_autoupdate.sh.
# -----------------------------------------------------------------------------
# Print a SHA-256 content hash to use as a cache key
hash_file() {
if command -v sha256sum > /dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
else
shasum -a 256 "$1" | awk '{print $1}'
fi
}
# Print a SHA-256 hash of stdin
hash_stdin() {
if command -v sha256sum > /dev/null 2>&1; then
sha256sum | awk '{print $1}'
else
shasum -a 256 | awk '{print $1}'
fi
}
# Print a short tag identifying the toolchain and options that produce cached
# files, so that entries are invalidated when any of them change. The cache
# keys files by input content only, on the assumption that the same inputs
# always compress/diff to the same output; this tag makes that assumption hold
# across xz/mbsdiff upgrades and XZ_OPT/BCJ_OPTIONS changes. (A change to any
# input invalidates both the full and patch caches, which is more than strictly
# necessary but keeps this simple; a stale build is just a cold rebuild.)
# Stale-tag entries are removed by the time-based cleanup in build_autoupdate.sh.
get_cache_tag() {
local mbsdiff_bin
mbsdiff_bin="$(command -v "$MBSDIFF" 2>/dev/null)"
{
$XZ --version 2>/dev/null | head -1
echo "XZ_OPT=$XZ_OPT"
echo "BCJ_OPTIONS=${BCJ_OPTIONS:-}"
# mbsdiff has no --version, so identify it by its binary contents
if [ -n "$mbsdiff_bin" ]; then
hash_file "$mbsdiff_bin"
else
echo "MBSDIFF=$MBSDIFF"
fi
} | hash_stdin | cut -c 1-16
}
# Atomically store file $2 in the cache as $1
cache_store() {
mkdir -p "$(dirname "$1")"
cp "$2" "$1.tmp.$$" && mv -f "$1.tmp.$$" "$1"
}
# xz-compress $1 to $2, reusing a cached result when one exists (see the cache
# overview above)
compress_full_file() {
local src="$1"
local dest="$2"
local key=""
if [ -n "${UPDATE_CACHE_DIR:-}" ]; then
key="$UPDATE_CACHE_DIR/full-${UPDATE_CACHE_TAG:-}-$(hash_file "$src").xz"
if [ -f "$key" ]; then
# Keep entries that are still in use from expiring
touch -c "$key" 2>/dev/null || true
cp "$key" "$dest"
return 0
fi
fi
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$src" > "$dest"
if [ -n "$key" ]; then
cache_store "$key" "$dest"
fi
}
# Process a single task line from run_parallel_tasks():
#
# full - compress the new file into the work directory
# diff - if the file changed, generate a binary diff and keep the smaller of
# the compressed patch ($f.patch) and the compressed file ($f)
#
# Expects newdir and workdir in the environment, plus olddir for diff tasks.
# The results are assembled into the manifest serially by the caller based on
# which files exist in the work directory. Diffs and compressed files are
# cached when enabled -- see the cache overview above.
process_update_task() {
local task="$1"
local tab=$(printf '\t')
local type="${task%%"$tab"*}"
local f="${task#*"$tab"}"
local oldfile_path newfile_path patch_path patchsize fullsize full_pid patchkey
if [ "$type" = "diff" ]; then
if diff "$olddir/$f" "$newdir/$f" > /dev/null; then
# Unchanged
return 0
fi
mkdir -p "$(dirname "$workdir/$f")"
verbose_notice "diffing \"$f\""
# Compress the full new file in the background while diffing, since both
# are needed to decide which to package
compress_full_file "$newdir/$f" "$workdir/$f" &
full_pid=$!
patchkey=""
if [ -n "${UPDATE_CACHE_DIR:-}" ]; then
patchkey="$UPDATE_CACHE_DIR/patch-${UPDATE_CACHE_TAG:-}-$(hash_file "$olddir/$f")-$(hash_file "$newdir/$f").xz"
fi
if [ -n "$patchkey" ] && [ -f "$patchkey" ]; then
# Keep entries that are still in use from expiring
touch -c "$patchkey" 2>/dev/null || true
cp "$patchkey" "$workdir/$f.patch.xz"
else
# mbsdiff doesn't like POSIX paths on Windows
if [ ${WIN_NATIVE:-0} -eq 1 ]; then
oldfile_path=$(cygpath -m "$olddir/$f")
newfile_path=$(cygpath -m "$newdir/$f")
patch_path=$(cygpath -m "$workdir/$f.patch")
else
oldfile_path="$olddir/$f"
newfile_path="$newdir/$f"
patch_path="$workdir/$f.patch"
fi
$MBSDIFF "$oldfile_path" "$newfile_path" "$patch_path"
$XZ $XZ_OPT --compress --lzma2 --format=xz --check=crc64 --force "$workdir/$f.patch"
if [ -n "$patchkey" ]; then
cache_store "$patchkey" "$workdir/$f.patch.xz"
fi
fi
wait $full_pid
copy_perm "$newdir/$f" "$workdir/$f"
patchsize=$(get_file_size "$workdir/$f.patch.xz")
fullsize=$(get_file_size "$workdir/$f")
if [ $patchsize -lt $fullsize ]; then
mv -f "$workdir/$f.patch.xz" "$workdir/$f.patch"
rm -f "$workdir/$f"
else
rm -f "$workdir/$f.patch.xz"
fi
else
mkdir -p "$(dirname "$workdir/$f")"
compress_full_file "$newdir/$f" "$workdir/$f"
copy_perm "$newdir/$f" "$workdir/$f"
fi
}
# List all files in the current directory, stripping leading "./"
# Pass a variable name and it will be filled as an array.
list_files() {
count=0
temp_filelist=$(mktemp)
find . -type f \
! -name "update.manifest" \
! -name "updatev2.manifest" \
! -name "updatev3.manifest" \
| sed 's/\.\/\(.*\)/\1/' \
| sort -r > "${temp_filelist}"
while read file; do
eval "${1}[$count]=\"$file\""
# Changed for Zotero to avoid eval as 1
#(( count++ ))
(( ++count ))
done < "${temp_filelist}"
rm "${temp_filelist}"
}
# List all directories in the current directory, stripping leading "./"
list_dirs() {
count=0
temp_dirlist=$(mktemp)
find . -type d \
! -name "." \
! -name ".." \
| sed 's/\.\/\(.*\)/\1/' \
| sort -r > "${temp_dirlist}"
while read dir; do
eval "${1}[$count]=\"$dir\""
# Changed for Zotero
#(( count++ ))
(( ++count ))
done < "${temp_dirlist}"
rm "${temp_dirlist}"
}

View file

@ -1,145 +0,0 @@
#!/bin/bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This tool generates full update packages for the update system.
# Author: Darin Fisher
#
# Added for Zotero
set -eo pipefail
. $(dirname "$0")/common.sh
# Process a single file task when invoked from run_parallel_tasks()
if [ "${1:-}" = "--run-task" ]; then
process_update_task "$2"
exit 0
fi
# -----------------------------------------------------------------------------
print_usage() {
notice "Usage: $(basename $0) [OPTIONS] ARCHIVE DIRECTORY"
}
if [ $# = 0 ]; then
print_usage
exit 1
fi
if [ $1 = -h ]; then
print_usage
notice ""
notice "The contents of DIRECTORY will be stored in ARCHIVE."
notice ""
notice "Options:"
notice " -h show this help text"
notice " -q be less verbose"
notice ""
exit 1
fi
if [ $1 = -q ]; then
QUIET=1
export QUIET
shift
fi
# -----------------------------------------------------------------------------
mar_command="$MAR -V ${MOZ_PRODUCT_VERSION:?} -H ${MAR_CHANNEL_ID:?}"
archive="$1"
targetdir="$2"
# Prevent the workdir from being inside the targetdir so it isn't included in
# the update mar.
if [ $(echo "$targetdir" | grep -c '/$') = 1 ]; then
# Remove the /
targetdir=$(echo "$targetdir" | sed -e 's:\/$::')
fi
workdir="$targetdir.work"
updatemanifestv3="$workdir/updatev3.manifest"
targetfiles="updatev3.manifest"
mkdir -p "$workdir"
# Generate a list of all files in the target directory.
pushd "$targetdir"
if test $? -ne 0 ; then
exit 1
fi
if [ ! -f "precomplete" ]; then
if [ ! -f "Contents/Resources/precomplete" ]; then
notice "precomplete file is missing!"
exit 1
fi
fi
list_files files
popd
# Add the type of update to the beginning of the update manifests.
> "$updatemanifestv3"
notice ""
notice "Adding type instruction to update manifests"
notice " type complete"
echo "type \"complete\"" >> "$updatemanifestv3"
notice ""
notice "Adding file add instructions to update manifests"
num_files=${#files[*]}
# Compress the files in parallel via run_parallel_tasks() in
# common.sh, and then assemble the manifest and archive list serially in the
# original order
tmpdir="$(mktemp -d)"
taskfile="$tmpdir/tasks"
TAB="$(printf '\t')"
> "$taskfile"
for ((i=0; $i<$num_files; i=$i+1)); do
echo "full$TAB${files[$i]}" >> "$taskfile"
done
newdir="$targetdir"
export newdir workdir BCJ_OPTIONS QUIET
run_parallel_tasks "$taskfile" "$targetdir" "$0"
rm -rf "$tmpdir"
for ((i=0; $i<$num_files; i=$i+1)); do
f="${files[$i]}"
if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3"
else
make_add_instruction "$f" "$updatemanifestv3"
fi
targetfiles="$targetfiles \"$f\""
done
# Append remove instructions for any dead files.
notice ""
notice "Adding file and directory remove instructions from file 'removed-files'"
append_remove_instructions "$targetdir" "$updatemanifestv3"
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force "$updatemanifestv3" && mv -f "$updatemanifestv3.xz" "$updatemanifestv3"
# Changed for Zotero -- -C is unreliable
pushd "$workdir" > /dev/null
mar_command="$mar_command -c output.mar"
eval "$mar_command $targetfiles"
popd > /dev/null
mv -f "$workdir/output.mar" "$archive"
# cleanup
rm -fr "$workdir"
notice ""
notice "Finished"
notice ""

View file

@ -1,301 +0,0 @@
#!/bin/bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This tool generates incremental update packages for the update system.
# Author: Darin Fisher
#
# Added for Zotero
set -eo pipefail
. $(dirname "$0")/common.sh
# Process a single file task when invoked from run_parallel_tasks()
if [ "${1:-}" = "--run-task" ]; then
process_update_task "$2"
exit 0
fi
# -----------------------------------------------------------------------------
print_usage() {
notice "Usage: $(basename $0) [OPTIONS] ARCHIVE FROMDIR TODIR"
notice ""
notice "The differences between FROMDIR and TODIR will be stored in ARCHIVE."
notice ""
notice "Options:"
notice " -h show this help text"
notice " -f clobber this file in the installation"
notice " Must be a path to a file to clobber in the partial update."
notice " -q be less verbose"
notice ""
}
check_for_forced_update() {
force_list="$1"
forced_file_chk="$2"
local f
if [ "$forced_file_chk" = "precomplete" ]; then
## "true" *giggle*
return 0;
fi
if [ "$forced_file_chk" = "Contents/Resources/precomplete" ]; then
## "true" *giggle*
return 0;
fi
if [ "$forced_file_chk" = "removed-files" ]; then
## "true" *giggle*
return 0;
fi
if [ "$forced_file_chk" = "Contents/Resources/removed-files" ]; then
## "true" *giggle*
return 0;
fi
# notarization ticket
if [ "$forced_file_chk" = "Contents/CodeResources" ]; then
## "true" *giggle*
return 0;
fi
if [ "${forced_file_chk##*.}" = "chk" ]; then
## "true" *giggle*
return 0;
fi
for f in $force_list; do
#echo comparing $forced_file_chk to $f
if [ "$forced_file_chk" = "$f" ]; then
## "true" *giggle*
return 0;
fi
done
## 'false'... because this is bash. Oh yay!
return 1;
}
if [ $# = 0 ]; then
print_usage
exit 1
fi
requested_forced_updates='Contents/MacOS/firefox'
while getopts "hqf:" flag
do
case "$flag" in
h) print_usage; exit 0
;;
q) QUIET=1
;;
f) requested_forced_updates="$requested_forced_updates $OPTARG"
;;
?) print_usage; exit 1
;;
esac
done
# -----------------------------------------------------------------------------
mar_command="$MAR -V ${MOZ_PRODUCT_VERSION:?} -H ${MAR_CHANNEL_ID:?}"
# Added for -e for Zotero
set +e
let arg_start=$OPTIND-1
shift $arg_start
set -e
archive="$1"
olddir="$2"
newdir="$3"
# Prevent the workdir from being inside the targetdir so it isn't included in
# the update mar.
if [ $(echo "$newdir" | grep -c '/$') = 1 ]; then
# Remove the /
newdir=$(echo "$newdir" | sed -e 's:\/$::')
fi
workdir="$(mktemp -d)"
updatemanifestv3="$workdir/updatev3.manifest"
archivefiles="updatev3.manifest"
mkdir -p "$workdir"
# Generate a list of all files in the target directory.
pushd "$olddir"
if test $? -ne 0 ; then
exit 1
fi
list_files oldfiles
list_dirs olddirs
popd
pushd "$newdir"
if test $? -ne 0 ; then
exit 1
fi
if [ ! -f "precomplete" ]; then
if [ ! -f "Contents/Resources/precomplete" ]; then
notice "precomplete file is missing!"
exit 1
fi
fi
list_dirs newdirs
list_files newfiles
popd
# Add the type of update to the beginning of the update manifests.
notice ""
notice "Adding type instruction to update manifests"
> $updatemanifestv3
notice " type partial"
echo "type \"partial\"" >> $updatemanifestv3
notice ""
notice "Adding file patch and add instructions to update manifests"
num_oldfiles=${#oldfiles[*]}
remove_array=
num_removes=0
# The main file loop is split into three passes so that the
# CPU-heavy work can run in parallel: classify each file into a task list, run
# the diffing/compression tasks in parallel via run_parallel_tasks() in
# common.sh, and then assemble the manifest and archive list serially in the
# original order based on the files the tasks left in the work directory.
#
# (This also drops the unused MBSDIFF_HOOK/funsize path from the upstream
# script -- the diff behavior itself is unchanged and now lives in
# process_update_task() in common.sh.)
tmpdir="$(mktemp -d)"
taskfile="$tmpdir/tasks"
TAB="$(printf '\t')"
> "$taskfile"
for ((i=0; $i<$num_oldfiles; i=$i+1)); do
f="${oldfiles[$i]}"
# If this file exists in the new directory as well, then check if it differs.
if [ -f "$newdir/$f" ]; then
if check_for_add_if_not_update "$f"; then
echo "full$TAB$f" >> "$taskfile"
elif check_for_forced_update "$requested_forced_updates" "$f"; then
echo "full$TAB$f" >> "$taskfile"
else
echo "diff$TAB$f" >> "$taskfile"
fi
fi
done
# Newly added files -- in the new directory but not the old one. (This replaces
# the O(n^2) nested skip loop from the upstream script.)
printf '%s\n' "${oldfiles[@]}" | sort > "$tmpdir/oldlist"
printf '%s\n' "${newfiles[@]}" | sort > "$tmpdir/newlist"
comm -13 "$tmpdir/oldlist" "$tmpdir/newlist" | sed '/^$/d' | sort -r > "$tmpdir/newonly"
while IFS= read -r f; do
echo "full$TAB$f" >> "$taskfile"
done < "$tmpdir/newonly"
export olddir newdir workdir MBSDIFF BCJ_OPTIONS QUIET
run_parallel_tasks "$taskfile" "$newdir" "$0"
# Assemble the manifest and archive list in the original order. A diffed file
# left $f.patch in the work directory if the patch was smaller, $f if the
# compressed file was smaller, and neither if the file was unchanged.
for ((i=0; $i<$num_oldfiles; i=$i+1)); do
f="${oldfiles[$i]}"
if [ -f "$newdir/$f" ]; then
if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3"
archivefiles="$archivefiles \"$f\""
elif check_for_forced_update "$requested_forced_updates" "$f"; then
make_add_instruction "$f" "$updatemanifestv3" 1
archivefiles="$archivefiles \"$f\""
elif [ -f "$workdir/$f.patch" ]; then
make_patch_instruction "$f" "$updatemanifestv3"
archivefiles="$archivefiles \"$f.patch\""
elif [ -f "$workdir/$f" ]; then
make_add_instruction "$f" "$updatemanifestv3"
archivefiles="$archivefiles \"$f\""
fi
else
# remove instructions are added after add / patch instructions for
# consistency with make_incremental_updates.py
remove_array[$num_removes]=$f
# Changed by Zotero for -e
#(( num_removes++ ))
(( ++num_removes ))
fi
done
# Newly added files
notice ""
notice "Adding file add instructions to update manifests"
while IFS= read -r f; do
if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3"
else
make_add_instruction "$f" "$updatemanifestv3"
fi
archivefiles="$archivefiles \"$f\""
done < "$tmpdir/newonly"
rm -rf "$tmpdir"
notice ""
notice "Adding file remove instructions to update manifests"
for ((i=0; $i<$num_removes; i=$i+1)); do
f="${remove_array[$i]}"
verbose_notice " remove \"$f\""
echo "remove \"$f\"" >> $updatemanifestv3
done
# Add remove instructions for any dead files.
notice ""
notice "Adding file and directory remove instructions from file 'removed-files'"
append_remove_instructions "$newdir" "$updatemanifestv3"
notice ""
notice "Adding directory remove instructions for directories that no longer exist"
num_olddirs=${#olddirs[*]}
for ((i=0; $i<$num_olddirs; i=$i+1)); do
f="${olddirs[$i]}"
# If this dir doesn't exist in the new directory remove it.
if [ ! -d "$newdir/$f" ]; then
verbose_notice " rmdir $f/"
echo "rmdir \"$f/\"" >> $updatemanifestv3
fi
done
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force "$updatemanifestv3" && mv -f "$updatemanifestv3.xz" "$updatemanifestv3"
# Changed for Zotero -- -C is unreliable
pushd "$workdir" > /dev/null
mar_command="$mar_command -c output.mar"
eval "$mar_command $archivefiles"
popd > /dev/null
mv -f "$workdir/output.mar" "$archive"
# cleanup
rm -fr "$workdir"
notice ""
notice "Finished"
notice ""

View file

@ -1,24 +0,0 @@
application.ini
chrome.manifest
chrome/*
components/*
defaults/*
deleted.txt
dictionaries/*
extensions/*
fonts/EmojiOneMozilla.ttf
gtk2/*
icudt58l.dat
install.rdf
libnssdbm3.chk
libnssdbm3.so
libsoftokn3.chk
pingsender
resource/*
run-zotero.sh
styles.zip
test/*
translators.index
translators.zip
xulrunner/*
zotero.jar

View file

@ -1,67 +0,0 @@
Contents/Frameworks/XUL.framework/
Contents/MacOS/Info.plist
Contents/MacOS/LICENSE
Contents/MacOS/README.xulrunner
Contents/MacOS/Resources/*
Contents/MacOS/active-update.xml
Contents/MacOS/chrome.manifest
Contents/MacOS/components/
Contents/MacOS/crashreporter.app/*
Contents/MacOS/defaults/*
Contents/MacOS/dependentlibs.list
Contents/MacOS/dictionaries/
Contents/MacOS/gmp-fake/*
Contents/MacOS/js-gdb.py
Contents/MacOS/libc++.1.dylib
Contents/MacOS/libfreebl.chk
Contents/MacOS/libmozsqlite3.dylib
Contents/MacOS/libnspr4.dylib
Contents/MacOS/libnssdbm3.chk
Contents/MacOS/libnssdbm3.dylib
Contents/MacOS/libnssutil3.dylib
Contents/MacOS/libplc4.dylib
Contents/MacOS/libplds4.dylib
Contents/MacOS/libplugin_child_interpose.dylib
Contents/MacOS/libsmime3.dylib
Contents/MacOS/libsoftokn3.chk
Contents/MacOS/libssl3.dylib
Contents/MacOS/libxpcom.dylib
Contents/MacOS/omni.ja
Contents/MacOS/pdfinfo
Contents/MacOS/pdftotext
Contents/MacOS/pingsender
Contents/MacOS/platform.ini
Contents/MacOS/precomplete
Contents/MacOS/res/*
Contents/MacOS/update-settings.ini
Contents/MacOS/updater.app/Contents/MacOS/updater-bin
Contents/MacOS/updates.xml
Contents/MacOS/updates/*
Contents/MacOS/zotero-bin
Contents/Resources/CONTRIBUTING.md
Contents/Resources/COPYING
Contents/Resources/README.md
Contents/Resources/application.ini
Contents/Resources/chrome.manifest
Contents/Resources/chrome/*
Contents/Resources/components/*
Contents/Resources/defaults/*
Contents/Resources/deleted.txt
Contents/Resources/devtools.jar
Contents/Resources/dictionaries/*
Contents/Resources/extensions/*
Contents/Resources/gmp-clearkey/0.1/clearkey.info
Contents/Resources/icudt56l.dat
Contents/Resources/install.rdf
Contents/Resources/poppler-data/*
Contents/Resources/resource/*
Contents/Resources/styles.zip
Contents/Resources/test/*
Contents/Resources/translators.index
Contents/Resources/translators.zip
Contents/Resources/voucher.bin
Contents/Resources/zotero.jar
Contents/XPCServices/*
Contents/_CodeSignature/CodeDirectory
Contents/_CodeSignature/CodeRequirements
Contents/_CodeSignature/CodeSignature

View file

@ -1,58 +0,0 @@
Accessible.tlb
AccessibleHandler.dll
IA2Marshal.dll
Microsoft.VC80.CRT.manifest
api-ms-win-core-console-l1-1-0.dll
api-ms-win-core-datetime-l1-1-0.dll
api-ms-win-core-debug-l1-1-0.dll
api-ms-win-core-errorhandling-l1-1-0.dll
api-ms-win-core-file-l1-1-0.dll
api-ms-win-core-handle-l1-1-0.dll
api-ms-win-core-heap-l1-1-0.dll
api-ms-win-core-interlocked-l1-1-0.dll
api-ms-win-core-libraryloader-l1-1-0.dll
api-ms-win-core-memory-l1-1-0.dll
api-ms-win-core-namedpipe-l1-1-0.dll
api-ms-win-core-processenvironment-l1-1-0.dll
api-ms-win-core-processthreads-l1-1-0.dll
api-ms-win-core-profile-l1-1-0.dll
api-ms-win-core-rtlsupport-l1-1-0.dll
api-ms-win-core-string-l1-1-0.dll
api-ms-win-core-synch-l1-1-0.dll
api-ms-win-core-sysinfo-l1-1-0.dll
api-ms-win-core-util-l1-1-0.dll
application.ini
chrome.manifest
chrome/*
components/*
defaults/*
deleted.txt
dictionaries/*
extensions/*
firefox.*
fonts/EmojiOneMozilla.ttf
freebl3.chk
gkmedias.dll
install.rdf
minidump-analyzer.exe
mozcrt19.dll
mozutils.dll
msvcp80.dll
msvcp120.dll
msvcr80.dll
msvcr120.dll
nssdbm3.chk
nssdbm3.dll
pdfinfo.exe
pdftotext.exe
pingsender.exe
plugin-hang-ui.exe
poppler-data/*
resource/*
softokn3.chk
styles.zip
test/*
translators.index
translators.zip
xulrunner/*
zotero.jar

View file

@ -1,66 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
infile=${1:-}
outfile=${2:-}
if [[ -z "$infile" ]] || [[ -z "$infile" ]]; then
echo "Usage: $0 infile.mar outfile.mar"
exit 1
fi
mar="$APP_ROOT_DIR/xulrunner/bin/mar"
if [ ! -f "$mar" ]; then
echo "$mar not found"
exit 1
fi
tmp_dir=`mktemp -d`
function cleanup {
rm -rf $tmp_dir
}
trap cleanup EXIT
cd "$tmp_dir"
mkdir in
cd in
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
infile=$(cygpath -w "$infile")
outfile=$(cygpath -w "$outfile")
fi
echo "Extracting MAR"
$mar -x "$infile"
echo "Renaming all files to .xz"
find . -type f -exec mv {} {}.xz \;
echo "Uncompressing files"
find . -type f -exec unxz {} \;
echo "Recompressing files with bzip2"
find . -type f -exec bzip2 {} \;
echo "Removing .bz extension"
find . -type f | while read f; do mv "$f" "${f%.bz2}"; done
channel=$($mar -T "$infile" | sed -n -r 's/.+MAR channel name: ([^\s]+)/\1/p')
version=$($mar -T "$infile" | sed -n -r 's/.+Product version: ([^\s]+)/\1/p')
if [ -z "$channel" ]; then
echo "Could not detect channel"
exit 1
fi
if [ -z "$version" ]; then
echo "Could not detect version"
exit 1
fi
echo "MAR channel name: $channel"
echo "Product version: $version"
echo "Creating new MAR"
find . -type f -print0 | xargs -0 $mar -V $version -H $channel -c "$outfile"
echo
$mar -T "$outfile"

Binary file not shown.

Binary file not shown.

View file

@ -1,59 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh"
if [ $# -lt 2 ]; then
echo "Usage: $0 /path/to/src/firefox x64|arm64|win32" >&2
exit 1
fi
gecko_path=$1
arch=$2
if [ ! -d "$gecko_path" ]; then
echo "$gecko_path is not a directory" >&2
exit 1
fi
# Install required Rust version
rustup toolchain install $RUST_VERSION
if [ $arch = "x64" ]; then
rust_target=x86_64
elif [ $arch = "arm64" ]; then
rust_target=aarch64
elif [ $arch = "win32" ]; then
rust_target=i686
else
echo "Unknown architecture $arch" >&2
exit 1
fi
rustup default $RUST_VERSION
rustup target add $rust_target-pc-windows-msvc
cp "$SCRIPT_DIR/mozconfig" "$gecko_path"
if [[ $arch == "arm64" || $arch == "win32" ]]; then
export Z_ARCH=$arch
export RUST_TARGET=$rust_target-pc-windows-msvc
fi
cd "$gecko_path"
./mach build
./mach package
# Copy the packaged ZIP back to app/win/ for launcher/xul.dll extraction
case $arch in
x64) zip_suffix=win64 ;;
arm64) zip_suffix=win64-aarch64 ;;
win32) zip_suffix=win32 ;;
esac
zip_file=$(ls "$gecko_path"/obj-*/dist/firefox-*.en-US.${zip_suffix}.zip 2>/dev/null | head -1) || true
if [ -z "$zip_file" ] || [ ! -f "$zip_file" ]; then
echo "Could not find packaged zip for $arch in $gecko_path/obj-*/dist/" >&2
exit 1
fi
cp "$zip_file" "$SCRIPT_DIR/"
echo "Copied $(basename "$zip_file") to $SCRIPT_DIR"

View file

@ -1,59 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh"
#
# Turns out we need older versions from Mozilla, not the official ZIPs
#
#plugins=$(cat <<EOF
#http://nsis.sourceforge.net/mediawiki/images/d/d4/AppAssocReg-0.4.zip
#https://github.com/connectiblutz/NSIS-ApplicationID/releases/download/1.1/NSIS-ApplicationID.zip
#http://nsis.sourceforge.net/mediawiki/images/c/ca/InvokeShellVerb-1.0.zip
#http://nsis.sourceforge.net/mediawiki/images/6/6c/Shelllink.zip
#http://nsis.sourceforge.net/mediawiki/images/8/8f/UAC.zip
#EOF
#)
#
#mkdir -p nsis
#cd nsis
#for plugin in $plugins; do
# curl -fsSLO $plugin
#done
#
#unzip_cmd="unzip -d Plugins -j"
#for zip in AppAssocReg*.zip InvokeShellVerb*.zip; do
# $unzip_cmd $zip 'Plugins/Unicode/*.dll'
#done
#$unzip_cmd Shelllink*.zip Unicode/Plugins/ShellLink.dll
#$unzip_cmd NSIS-ApplicationID*.zip 'ReleaseUnicode/ApplicationID*'
#$unzip_cmd UAC.zip Plugins/x86-unicode/UAC.dll
#
#rm *.zip
#
#echo
#echo
#echo "Files extracted to ./nsis/Plugins -- move to ${NSIS_DIR}Plugins"
#echo
#ls -la Plugins
mkdir -p Plugins
cd Plugins
plugins="AccessControl AppAssocReg ApplicationID InvokeShellVerb ShellLink UAC"
for plugin in $plugins; do
curl https://hg.mozilla.org/mozilla-central/raw-file/052d53200cf8/other-licenses/nsis/Plugins/$plugin.dll > $plugin.dll
done
# From https://nsis.sourceforge.io/NsProcess_plugin
curl https://download.zotero.org/dev/win/NsProcess-1.6.zip > NsProcess.zip
7z e NsProcess.zip "Plugin/nsProcessW.dll"
mv nsProcessW.dll nsProcess.dll
rm NsProcess.zip
echo
echo
echo "Files downloaded to ./Plugins -- move to ${NSIS_DIR}Plugins"
echo
ls -la

View file

@ -1,84 +0,0 @@
#!/bin/bash
#
# Extract xul.dll from custom Firefox build ZIPs in current folder and create a
# ZIP for each platform with its own SHA256 hash in the filename. The files
# should then be uploaded and their hashes updated in config.sh so that
# fetch_xulrunner will download and use the custom xul.dll.
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"
if [ $# -ne 1 ]; then
echo "Usage: $0 <version>"
exit 1
fi
version="$1"
# Input ZIP filenames
files=(
"firefox-$version.en-US.win32.zip"
"firefox-$version.en-US.win64-aarch64.zip"
"firefox-$version.en-US.win64.zip"
)
# Corresponding architecture labels and output names
arches=(
"win32"
"arm64"
"x64"
)
# Platform-specific SHA command
if [[ "$(uname)" = "Darwin" ]]; then
shasum_cmd="shasum -a 256"
else
shasum_cmd="sha256sum"
fi
# Clean up any old artifacts
rm -f "$version"-*.zip
# Process whichever arch ZIPs are present
processed=0
for i in "${!files[@]}"; do
file="${files[$i]}"
arch="${arches[$i]}"
temp_zip="xul-temp-$arch.zip"
if [ ! -f "$file" ]; then
continue
fi
dlls=(xul.dll)
# TEMP: Fix startup error on win32 due to missing export numSamples@RLBoxSoundTouch
if [[ $arch == "win32" ]]; then
dlls+=(lgpllibs.dll)
fi
echo "Extracting ${dlls[*]} from $file"
for dll in "${dlls[@]}"; do
unzip -p "$file" "firefox/$dll" > "$dll"
done
zip -q "$temp_zip" "${dlls[@]}"
# Calculate hash
sum=$($shasum_cmd "$temp_zip" | awk '{print $1}')
# Rename to final output
final_zip="$version-$arch-$sum.zip"
mv "$temp_zip" "$final_zip"
rm -f xul.dll lgpllibs.dll
echo "Created $final_zip"
processed=$((processed + 1))
done
if [ $processed -eq 0 ]; then
echo "No firefox-$version.en-US.*.zip files found in $SCRIPT_DIR" >&2
exit 1
fi

View file

@ -1,3 +0,0 @@
// CompressionMethod.cpp
#include "StdAfx.h"

Some files were not shown because too many files have changed in this diff Show more