mirror of
https://github.com/zotero/zotero.git
synced 2026-10-12 03:48:16 +00:00
Compare commits
No commits in common. "main" and "7.0.3" have entirely different histories.
1939 changed files with 100009 additions and 241380 deletions
17
.eslintignore
Normal file
17
.eslintignore
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
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/tinymce/*
|
||||
test/resource/*
|
||||
translators/*
|
||||
63
.eslintrc
Normal file
63
.eslintrc
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": {
|
||||
"Zotero": false,
|
||||
"ZOTERO_CONFIG": false,
|
||||
"AddonManager": false,
|
||||
"Cc": false,
|
||||
"Ci": false,
|
||||
"ChromeUtils": false,
|
||||
"Components": false,
|
||||
"ConcurrentCaller": false,
|
||||
"ctypes": false,
|
||||
"OS": false,
|
||||
"MozXULElement": false,
|
||||
"PluralForm": false,
|
||||
"Services": false,
|
||||
"windowUtils": false,
|
||||
"XPCOMUtils": false,
|
||||
"XRegExp": false,
|
||||
"XULElement": false,
|
||||
"XULElementBase": false,
|
||||
"ItemPaneSectionElementBase": false,
|
||||
"Cu": false,
|
||||
"ChromeWorker": false,
|
||||
"Localization": false,
|
||||
"L10nFileSource": false,
|
||||
"L10nRegistry": false,
|
||||
"ZoteroPane_Local": false,
|
||||
"ZoteroPane": false,
|
||||
"Zotero_Tabs": false,
|
||||
"ZoteroContextPane": false,
|
||||
"IOUtils": false,
|
||||
"NetUtil": false,
|
||||
"FileUtils": false,
|
||||
"globalThis": false
|
||||
},
|
||||
"extends": [
|
||||
"@zotero",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"@babel"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "17.0"
|
||||
}
|
||||
},
|
||||
"rules": {}
|
||||
}
|
||||
9
.github/ISSUE_TEMPLATE.md
vendored
Normal file
9
.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
**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.
|
||||
|
||||
Development questions involving code, APIs, or other technical topics can be posted to the zotero-dev mailing list at http://groups.google.com/group/zotero-dev.
|
||||
|
||||
See https://www.zotero.org/support/zotero_support for more information on how Zotero support works.
|
||||
416
.github/workflows/ci.yml
vendored
416
.github/workflows/ci.yml
vendored
|
|
@ -1,364 +1,88 @@
|
|||
name: CI
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Monthly, to catch runner image and OS changes
|
||||
- cron: '0 6 1 * *'
|
||||
on: [push, pull_request]
|
||||
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
|
||||
build:
|
||||
name: Build, Upload, Test
|
||||
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
|
||||
node-version: 18
|
||||
#cache: npm
|
||||
|
||||
|
||||
# On GitHub
|
||||
- name: Install xvfb
|
||||
if: env.ACT != 'true'
|
||||
run: sudo apt update && sudo apt install -y xvfb
|
||||
|
||||
# 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') }}
|
||||
|
||||
key: node-modules-${{ 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
|
||||
# Currently necessary for pdf-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
|
||||
|
||||
- name: Upload deployment ZIP
|
||||
if: |
|
||||
env.ACT != 'true'
|
||||
&& github.repository == 'zotero/zotero'
|
||||
&& github.event_name == 'push'
|
||||
&& (github.ref == 'refs/heads/main' || endsWith(github.ref, '-hotfix'))
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
run: |
|
||||
mkdir build-zip
|
||||
cd build
|
||||
zip -r ../build.zip *
|
||||
zip -r ../build-zip/$GITHUB_SHA.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
|
||||
|
||||
sudo gem install --no-document dpl dpl-s3
|
||||
dpl --provider=s3 --bucket=zotero-download --local-dir=build-zip --upload-dir=ci/client --acl=public-read --skip_cleanup=true
|
||||
|
||||
- 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
|
||||
run: xvfb-run test/runtests.sh -f
|
||||
|
||||
- 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') }}
|
||||
key: utilities-node-modules-${{ hashFiles('chrome/content/zotero/xpcom/utilities/package-lock.json') }}
|
||||
|
||||
- name: Install utilities Node modules
|
||||
if: steps.utilities-node-cache.outputs.cache-hit != 'true'
|
||||
|
|
@ -367,73 +91,3 @@ jobs:
|
|||
- 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
|
||||
|
|
|
|||
8
.gitmodules
vendored
8
.gitmodules
vendored
|
|
@ -18,6 +18,10 @@
|
|||
path = test/resource/mocha
|
||||
url = https://github.com/mochajs/mocha.git
|
||||
branch = master
|
||||
[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/schema/global"]
|
||||
path = resource/schema/global
|
||||
url = https://github.com/zotero/zotero-schema.git
|
||||
|
|
@ -30,8 +34,8 @@
|
|||
url = https://github.com/zotero/reader.git
|
||||
branch = master
|
||||
[submodule "pdf-worker"]
|
||||
path = document-worker
|
||||
url = https://github.com/zotero/document-worker.git
|
||||
path = pdf-worker
|
||||
url = https://github.com/zotero/pdf-worker.git
|
||||
branch = master
|
||||
[submodule "note-editor"]
|
||||
path = note-editor
|
||||
|
|
|
|||
110
CLAUDE.md
110
CLAUDE.md
|
|
@ -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.
|
||||
|
|
@ -3,12 +3,12 @@ Vendor=Zotero
|
|||
Name=Zotero
|
||||
Version={{VERSION}}
|
||||
BuildID={{BUILDID}}
|
||||
Copyright=Copyright (c) 2006-2025 Contributors
|
||||
Copyright=Copyright (c) 2006-2022 Contributors
|
||||
ID=zotero@zotero.org
|
||||
|
||||
[Gecko]
|
||||
MinVersion=153.0
|
||||
MaxVersion=153.99.*
|
||||
MinVersion=115.0
|
||||
MaxVersion=115.99.*
|
||||
|
||||
[XRE]
|
||||
EnableExtensionManager=1
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@
|
|||
-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.
|
||||
|
|
|
|||
|
|
@ -50,10 +50,8 @@ if (processTestOptions) {
|
|||
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,
|
||||
|
|
@ -70,8 +68,8 @@ if (cmdLine.handleFlag("debugger", false)) {
|
|||
try {
|
||||
let portOrPath = Services.prefs.getBranch('').getIntPref('devtools.debugger.remote-port');
|
||||
|
||||
const { DevToolsLoader } = ChromeUtils.importESModule(
|
||||
"resource://devtools/shared/loader/Loader.sys.mjs"
|
||||
const { DevToolsLoader } = ChromeUtils.import(
|
||||
"resource://devtools/shared/loader/Loader.jsm"
|
||||
);
|
||||
const loader = new DevToolsLoader({
|
||||
freshCompartment: true,
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ caption {
|
|||
|
||||
/* styles for the link elements copied from .text-link in global.css */
|
||||
.inline-link {
|
||||
color: LinkText;
|
||||
color: -moz-nativehyperlinktext;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@ 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);
|
||||
|
|
@ -54,14 +50,6 @@ pref("dom.disable_open_during_load", true);
|
|||
// 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);
|
||||
|
|
@ -167,6 +155,3 @@ 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);
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ radio[pane=paneSync] {
|
|||
|
||||
/* styles for the link elements copied from .text-link in global.css */
|
||||
.inline-link {
|
||||
color: LinkText;
|
||||
color: -moz-nativehyperlinktext;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ radio[pane=paneSync] {
|
|||
|
||||
/* styles for the link elements copied from .text-link in global.css */
|
||||
.inline-link {
|
||||
color: LinkText;
|
||||
color: -moz-nativehyperlinktext;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
|
|
|||
526
app/build.sh
526
app/build.sh
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
CALLDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
. "$CALLDIR/config.sh"
|
||||
. "$CALLDIR/scripts/utils.sh"
|
||||
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
MAC_NATIVE=1
|
||||
|
|
@ -35,16 +34,12 @@ fi
|
|||
|
||||
function usage {
|
||||
cat >&2 <<DONE
|
||||
Usage: $0 [-d DIR] [-f FILE] -p PLATFORMS [-a ARCH] [-c CHANNEL] [options]
|
||||
|
||||
Usage: $0 [-d DIR] [-f FILE] -p PLATFORMS [-c CHANNEL] [-d]
|
||||
Options
|
||||
-d DIR build directory to build from (from build_xpi; cannot be used with -f)
|
||||
-f FILE ZIP file to build from (cannot be used with -d)
|
||||
-t add devtools
|
||||
-p PLATFORMS build for platforms PLATFORMS (m=Mac, w=Windows, l=Linux)
|
||||
-a ARCH architecture to build (arm64, x64, win32)
|
||||
* Ignored for Mac (always universal)
|
||||
* If omitted on Windows/Linux, all standard archs are built
|
||||
-c CHANNEL use update channel CHANNEL
|
||||
-e enforce signing
|
||||
-s don't package; only build binaries in staging/ directory
|
||||
|
|
@ -59,14 +54,16 @@ function cleanup {
|
|||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Copy the contents of a directory into another directory, which may already exist,
|
||||
# using rsync if it's available (not on Windows)
|
||||
function copy_dir {
|
||||
if command -v rsync > /dev/null; then
|
||||
rsync -a "$1/" "$2/"
|
||||
function replace_line {
|
||||
pattern=$1
|
||||
replacement=$2
|
||||
file=$3
|
||||
|
||||
if egrep -q "$pattern" "$file"; then
|
||||
perl -pi -e "s/$pattern/$replacement/" "$file"
|
||||
else
|
||||
mkdir -p "$2"
|
||||
cp -a "$1/." "$2/"
|
||||
echo "$pattern" not found in "$file" -- aborting 2>&1
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -89,8 +86,7 @@ BUILD_LINUX=0
|
|||
PACKAGE=1
|
||||
DEVTOOLS=0
|
||||
quick_build=0
|
||||
arch=""
|
||||
while getopts "d:f:p:a:c:tseq" opt; do
|
||||
while getopts "d:f:p:c:tseq" opt; do
|
||||
case $opt in
|
||||
d)
|
||||
SOURCE_DIR="$OPTARG"
|
||||
|
|
@ -99,21 +95,19 @@ while getopts "d:f:p:a:c:tseq" opt; do
|
|||
ZIP_FILE="$OPTARG"
|
||||
;;
|
||||
p)
|
||||
for (( i=0; i<${#OPTARG}; i++ )); do
|
||||
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 ;;
|
||||
m) BUILD_MAC=1;;
|
||||
w) BUILD_WIN=1;;
|
||||
l) BUILD_LINUX=1;;
|
||||
*)
|
||||
echo "$0: Invalid platform option ${OPTARG:i:1}" >&2
|
||||
echo "$0: Invalid platform option ${OPTARG:i:1}"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
a)
|
||||
arch="$OPTARG"
|
||||
;;
|
||||
c)
|
||||
UPDATE_CHANNEL="$OPTARG"
|
||||
;;
|
||||
|
|
@ -136,56 +130,31 @@ while getopts "d:f:p:a:c:tseq" opt; do
|
|||
shift $((OPTIND-1)); OPTIND=1
|
||||
done
|
||||
|
||||
xul_hash_file() {
|
||||
local _platform=$1
|
||||
local _arch=$2
|
||||
|
||||
case $_platform in
|
||||
m)
|
||||
echo "hash-mac";;
|
||||
|
||||
w)
|
||||
[[ $_arch == x64 ]] && _arch="win-x64"
|
||||
[[ $_arch == arm64 ]] && _arch="win-arm64"
|
||||
echo "hash-${_arch}";;
|
||||
|
||||
l)
|
||||
[[ $_arch == x64 ]] && _arch="x86_64"
|
||||
echo "hash-linux-${_arch}";;
|
||||
|
||||
*)
|
||||
echo "Invalid platform '$_platform'" >&2
|
||||
exit 1
|
||||
esac
|
||||
}
|
||||
|
||||
function check_xulrunner_hash {
|
||||
local _platform=$1
|
||||
local _arch=$2
|
||||
|
||||
local platform_hash_file=$(xul_hash_file "$_platform" "${_arch:-universal}")
|
||||
|
||||
# TEMP: If previous Windows hash file without architecture is present, force a refresh,
|
||||
# because the Firefox folder might have been changed without the new hash file being
|
||||
# updated, resulting in a broken build (e.g, fx140 using fx128). We'll want to keep this
|
||||
# as long as we're switching back to <=fx128. This isn't an issue on macOS because there's
|
||||
# no architecture and isn't an issue on Linux because the runtime folder names changed.
|
||||
if [[ $_platform = 'w' ]] && [[ -e "$CALLDIR/xulrunner/hash-win" ]]; then
|
||||
echo "Found old hash-win -- forcing redownload"
|
||||
rm "$CALLDIR"/xulrunner/hash-win*
|
||||
fi
|
||||
|
||||
if [ ! -e "$CALLDIR/xulrunner/$platform_hash_file" ]; then
|
||||
echo "$platform_hash_file not found -- downloading"
|
||||
echo
|
||||
$CALLDIR/scripts/fetch_xulrunner -p $_platform ${_arch:+-a $_arch}
|
||||
platform=$1
|
||||
|
||||
if [ $platform == "m" ]; then
|
||||
platform_hash_file="hash-mac"
|
||||
elif [ $platform == "w" ]; then
|
||||
platform_hash_file="hash-win"
|
||||
elif [ $platform == "l" ]; then
|
||||
platform_hash_file="hash-linux"
|
||||
else
|
||||
recalculated_xulrunner_hash=$("$CALLDIR/scripts/xulrunner_hash" -p $_platform ${_arch:+-a $_arch})
|
||||
echo "Platform parameter incorrect. Acceptable values: m/w/l"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -e "$CALLDIR/xulrunner/$platform_hash_file" ]; then
|
||||
echo "xulrunner not found -- downloading"
|
||||
echo
|
||||
$CALLDIR/scripts/fetch_xulrunner -p $platform
|
||||
else
|
||||
recalculated_xulrunner_hash=$("$CALLDIR/scripts/xulrunner_hash" -p $platform)
|
||||
current_xulrunner_hash=$(< "$CALLDIR/xulrunner/$platform_hash_file")
|
||||
if [ "$current_xulrunner_hash" != "$recalculated_xulrunner_hash" ]; then
|
||||
if [ "$current_xulrunner_hash" != "$recalculated_xulrunner_hash" ]; then
|
||||
echo "xulrunner hashes don't match -- redownloading"
|
||||
echo
|
||||
"$CALLDIR/scripts/fetch_xulrunner" -p $_platform ${_arch:+-a $_arch}
|
||||
"$CALLDIR/scripts/fetch_xulrunner" -p $platform
|
||||
current_xulrunner_hash=$(< "$CALLDIR/xulrunner/$platform_hash_file")
|
||||
if [ "$current_xulrunner_hash" != "$recalculated_xulrunner_hash" ]; then
|
||||
echo "xulrunner hashes don't match after running fetch_xulrunner!"
|
||||
|
|
@ -195,36 +164,15 @@ function check_xulrunner_hash {
|
|||
fi
|
||||
}
|
||||
|
||||
# Derive platform flags from host if none provided
|
||||
if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; then
|
||||
case "$(uname -s)" in
|
||||
Darwin) BUILD_MAC=1 ;;
|
||||
Linux) BUILD_LINUX=1 ;;
|
||||
CYGWIN*|MINGW*) BUILD_WIN=1 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Check xulrunner hashes for requested builds
|
||||
#Check if xulrunner and GECKO_VERSION for each platform match
|
||||
if [ $BUILD_MAC == 1 ]; then
|
||||
check_xulrunner_hash m
|
||||
fi
|
||||
if [ $BUILD_WIN == 1 ]; then
|
||||
if [[ -n $arch ]]; then
|
||||
check_xulrunner_hash w $(get_canonical_arch w $arch)
|
||||
else
|
||||
for _a in win-x64 win-arm64 win32; do
|
||||
check_xulrunner_hash w "$_a"
|
||||
done
|
||||
fi
|
||||
check_xulrunner_hash w
|
||||
fi
|
||||
if [ $BUILD_LINUX == 1 ]; then
|
||||
if [[ -n $arch ]]; then
|
||||
check_xulrunner_hash l $(get_canonical_arch l $arch)
|
||||
else
|
||||
for _a in x64 arm64; do
|
||||
check_xulrunner_hash l "$_a"
|
||||
done
|
||||
fi
|
||||
check_xulrunner_hash l
|
||||
fi
|
||||
|
||||
|
||||
|
|
@ -257,7 +205,7 @@ BUILD_ID=`date +%Y%m%d%H%M%S`
|
|||
# Paths to Gecko runtimes
|
||||
MAC_RUNTIME_PATH="$CALLDIR/xulrunner/Firefox.app"
|
||||
WIN_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-"
|
||||
LINUX_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-linux-"
|
||||
LINUX_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-"
|
||||
|
||||
base_dir="$BUILD_DIR/base"
|
||||
app_dir="$BUILD_DIR/base/app"
|
||||
|
|
@ -285,96 +233,17 @@ set +e
|
|||
if [ $BUILD_MAC == 1 ]; then
|
||||
cp -Rp "$MAC_RUNTIME_PATH"/Contents/Resources/browser/omni "$app_dir"
|
||||
unzip -qj "$MAC_RUNTIME_PATH"/Contents/Resources/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/
|
||||
|
||||
elif [[ $BUILD_WIN == 1 ]]; then
|
||||
# These are non-arch-specific files, so just find a runtime we fetched:
|
||||
# - If the caller specified -a, that arch is guaranteed present.
|
||||
# - Otherwise prefer win-x64 → win-arm64 → win32, falling back to the first
|
||||
# directory that exists under xulrunner/.
|
||||
if [[ -n $arch ]]; then
|
||||
omni_arch=$(get_canonical_arch w $arch)
|
||||
else
|
||||
for cand in win-x64 win-arm64 win32; do
|
||||
[[ -d "${WIN_RUNTIME_PATH_PREFIX}${cand}" ]] && { omni_arch="$cand"; break; }
|
||||
done
|
||||
fi
|
||||
cp -Rp "${WIN_RUNTIME_PATH_PREFIX}${omni_arch}"/browser/omni "$app_dir"
|
||||
unzip -qj "${WIN_RUNTIME_PATH_PREFIX}${omni_arch}"/omni.ja "hyphenation/*" \
|
||||
-d "$app_dir"/hyphenation/
|
||||
|
||||
elif [[ $BUILD_LINUX == 1 ]]; then
|
||||
# Same as above
|
||||
if [[ -n $arch ]]; then
|
||||
omni_arch=$(get_canonical_arch l $arch)
|
||||
else
|
||||
for cand in x86_64 arm64; do
|
||||
[[ -d "${LINUX_RUNTIME_PATH_PREFIX}${cand}" ]] && { omni_arch="$cand"; break; }
|
||||
done
|
||||
fi
|
||||
cp -Rp "${LINUX_RUNTIME_PATH_PREFIX}${omni_arch}"/browser/omni "$app_dir"
|
||||
unzip -qj "${LINUX_RUNTIME_PATH_PREFIX}${omni_arch}"/omni.ja "hyphenation/*" \
|
||||
-d "$app_dir"/hyphenation/
|
||||
elif [ $BUILD_WIN == 1 ]; then
|
||||
# Non-arch-specific files, so just use 64-bit version
|
||||
cp -Rp "${WIN_RUNTIME_PATH_PREFIX}win-x64"/browser/omni "$app_dir"
|
||||
unzip -qj "${WIN_RUNTIME_PATH_PREFIX}win-x64"/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/
|
||||
elif [ $BUILD_LINUX == 1 ]; then
|
||||
# Non-arch-specific files, so just use 64-bit version
|
||||
cp -Rp "${LINUX_RUNTIME_PATH_PREFIX}x86_64"/browser/omni "$app_dir"
|
||||
unzip -qj "${LINUX_RUNTIME_PATH_PREFIX}x86_64"/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/
|
||||
fi
|
||||
set -e
|
||||
cd $omni_dir
|
||||
|
||||
# Remove Firefox files that we don't need
|
||||
rm actors/AboutLogins{Parent,Child}.sys.mjs
|
||||
rm actors/AboutMessagePreview{Parent,Child}.sys.mjs
|
||||
rm actors/AboutNewTab{Parent,Child}.sys.mjs
|
||||
rm actors/AboutPrivateBrowsing{Parent,Child}.sys.mjs
|
||||
rm actors/AboutProtections{Parent,Child}.sys.mjs
|
||||
rm actors/AboutReader{Parent,Child}.sys.mjs
|
||||
rm actors/AboutTabCrashed{Parent,Child}.sys.mjs
|
||||
rm actors/AboutWelcome{Parent,Child}.sys.mjs
|
||||
rm actors/ASRouter{Parent,Child}.sys.mjs
|
||||
rm actors/BackupUI{Parent,Child}.sys.mjs
|
||||
rm actors/BlockedSite{Parent,Child}.sys.mjs
|
||||
rm actors/BrowserProcessChild.sys.mjs
|
||||
rm actors/BrowserTabChild.sys.mjs
|
||||
rm actors/ClickHandler{Parent,Child}.sys.mjs
|
||||
rm actors/ContentSearch{Parent,Child}.sys.mjs
|
||||
rm actors/DecoderDoctor{Parent,Child}.sys.mjs
|
||||
rm actors/DOMFullscreen{Parent,Child}.sys.mjs
|
||||
rm actors/EncryptedMedia{Parent,Child}.sys.mjs
|
||||
rm actors/FormValidation{Parent,Child}.sys.mjs
|
||||
rm actors/LinkHandler{Parent,Child}.sys.mjs
|
||||
rm actors/MigrationWizard{Parent,Child}.sys.mjs
|
||||
rm actors/PageData{Parent,Child}.sys.mjs # Not our PageData actor
|
||||
rm actors/PageInfoChild.sys.mjs
|
||||
rm actors/PageStyle{Parent,Child}.sys.mjs
|
||||
rm actors/Plugin{Parent,Child}.sys.mjs # GMP plugins, which we remove
|
||||
rm actors/PointerLock{Parent,Child}.sys.mjs
|
||||
#rm actors/RFPHelper{Parent,Child}.sys.mjs
|
||||
rm actors/ScreenshotsComponentChild.sys.mjs
|
||||
#rm actors/SearchSERPTelemetry{Parent,Child}.sys.mjs
|
||||
#rm actors/ShoppingSidebar{Parent,Child}.sys.mjs
|
||||
rm actors/SpeechDispatcher{Parent,Child}.sys.mjs
|
||||
rm actors/WebRTC{Parent,Child}.sys.mjs
|
||||
|
||||
# Keep some essential files from chrome/browser/
|
||||
mkdir chrome/browser-fx
|
||||
browser_keep=(
|
||||
content/browser/license.html
|
||||
# For web-ext
|
||||
content/browser/parent/ext-browser.js
|
||||
# For spellchecking
|
||||
content/browser/built_in_addons.json
|
||||
# Statically imported by BackupService, which SelectableProfileService pulls in when it's
|
||||
# instantiated as a command-line handler at startup
|
||||
content/browser/backup/backup-constants.mjs
|
||||
)
|
||||
for file in "${browser_keep[@]}"; do
|
||||
mkdir -p "$(dirname "chrome/browser-fx/$file")"
|
||||
mv "chrome/browser/$file" "chrome/browser-fx/$file"
|
||||
done
|
||||
# Delete everything else
|
||||
rm -r chrome/browser
|
||||
# Move essential files back
|
||||
mv chrome/browser-fx chrome/browser
|
||||
|
||||
#rm modules/SearchSERPTelemetry.sys.mjs
|
||||
|
||||
# Move some Firefox files that would be overwritten out of the way
|
||||
mv chrome.manifest chrome.manifest-fx
|
||||
mv defaults defaults-fx
|
||||
|
|
@ -385,18 +254,11 @@ if [ -n "$ZIP_FILE" ]; then
|
|||
echo "Building from $ZIP_FILE"
|
||||
unzip -q $ZIP_FILE -d "$omni_dir"
|
||||
else
|
||||
if command -v rsync > /dev/null; then
|
||||
rsync_params=""
|
||||
if [ $include_tests -eq 0 ]; then
|
||||
rsync_params="--exclude /test"
|
||||
fi
|
||||
rsync -a $rsync_params "$SOURCE_DIR/" ./
|
||||
else
|
||||
copy_dir "$SOURCE_DIR" .
|
||||
if [ $include_tests -eq 0 ]; then
|
||||
rm -rf ./test
|
||||
fi
|
||||
rsync_params=""
|
||||
if [ $include_tests -eq 0 ]; then
|
||||
rsync_params="--exclude /test"
|
||||
fi
|
||||
rsync -a $rsync_params "$SOURCE_DIR/" ./
|
||||
fi
|
||||
|
||||
mv defaults defaults-z
|
||||
|
|
@ -408,7 +270,7 @@ prefs_file=defaults/preferences/zotero.js
|
|||
# - network.captive-portal-service.enabled
|
||||
# Disable the captive portal check against Mozilla servers
|
||||
# - extensions.systemAddon.update.url
|
||||
grep -E -v '(network.captive-portal-service.enabled|extensions.systemAddon.update.url)' defaults/preferences/firefox.js > $prefs_file
|
||||
egrep -v '(network.captive-portal-service.enabled|extensions.systemAddon.update.url)' defaults/preferences/firefox.js > $prefs_file
|
||||
rm defaults/preferences/firefox.js
|
||||
|
||||
# Combine app and "extension" Zotero prefs
|
||||
|
|
@ -422,8 +284,6 @@ echo "" >> $prefs_file
|
|||
echo "# Zotero extension prefs" >> $prefs_file
|
||||
echo "" >> $prefs_file
|
||||
cat defaults-z/preferences/zotero.js >> $prefs_file
|
||||
# Babel strips the trailing newline, so anything appended below would share a line
|
||||
echo "" >> $prefs_file
|
||||
|
||||
rm -rf defaults-z
|
||||
|
||||
|
|
@ -445,7 +305,7 @@ elif [ $BUILD_LINUX == 1 ]; then
|
|||
perl -pi -e 's/pref\("general\.autoScroll", false\);/pref\("general.autoScroll", true);/' $prefs_file
|
||||
fi
|
||||
|
||||
# Clear list of built-in add-ons, but leave the en-US dictionary
|
||||
# Clear list of built-in add-ons
|
||||
echo '{"dictionaries": {"en-US": "dictionaries/en-US.dic"}, "system": []}' > chrome/browser/content/browser/built_in_addons.json
|
||||
|
||||
# chrome.manifest
|
||||
|
|
@ -476,58 +336,6 @@ if [ $DEVTOOLS -eq 1 ]; then
|
|||
replace_line 'let command = Services.dirsvc.get\("XREExeF", Ci.nsIFile\).path;' \
|
||||
'let command = Services.dirsvc.get("XREExeF", Ci.nsIFile).path; command = command.replace("zotero-bin", "zotero");' \
|
||||
chrome/devtools/modules/devtools/client/framework/browser-toolbox/Launcher.sys.mjs
|
||||
|
||||
# Fix source display breaking when switching windows in the Browser Toolbox.
|
||||
# When the user switches which chrome window is being inspected (via the frame
|
||||
# picker), the thread actor's _onWindowReady handler clears all debuggees and
|
||||
# source actors, then re-adds debuggees. But it never calls addAllSources() to
|
||||
# re-emit the existing scripts — it only does that for BFCache navigations.
|
||||
# Since the scripts already exist (they're not new), onNewScript won't fire,
|
||||
# so the debugger's source list stays empty. Fix: also call addAllSources()
|
||||
# after frame switching, just like BFCache.
|
||||
replace_line '_onWindowReady\(\{ isTopLevel, isBFCache \}\)' \
|
||||
'_onWindowReady({ isTopLevel, isBFCache, isFrameSwitching })' \
|
||||
chrome/devtools/modules/devtools/server/actors/thread.js
|
||||
replace_line 'if \(isBFCache\) {' \
|
||||
'if (isBFCache || isFrameSwitching) {' \
|
||||
chrome/devtools/modules/devtools/server/actors/thread.js
|
||||
|
||||
# Suppress "Failed to get the active browserId" error when profile recording starts
|
||||
replace_line 'console\.error\(' \
|
||||
'if (false) console.error(' \
|
||||
chrome/devtools/modules/devtools/shared/performance-new/recording-utils.sys.mjs
|
||||
|
||||
# Stub out profile favicon lookup since we don't have PlacesUtils
|
||||
replace_line 'return getPageFavicons\(pageUrls\);' \
|
||||
'return pageUrls ? pageUrls.map(() => null) : [];' \
|
||||
chrome/devtools/modules/devtools/client/performance-new/shared/background.sys.mjs
|
||||
|
||||
# Use our profilerViewer.xhtml to display profiles.
|
||||
# We can't use basicViewer because that loads Zotero scripts, and profiling
|
||||
# runs in the Browser Toolbox child process, where a second Zotero instance
|
||||
# would collide with the locked database.
|
||||
replace_line 'const contentBrowser = await new Promise\(resolveOnContentBrowserCreated =>' \
|
||||
'if (typeof win.openWebLinkIn !== "function") {
|
||||
const ww = Cc["\@mozilla.org\/embedcomp\/window-watcher;1"].getService(
|
||||
Ci.nsIWindowWatcher
|
||||
);
|
||||
const arg = { uri: urlToLoad };
|
||||
arg.wrappedJSObject = arg;
|
||||
const viewerWin = ww.openWindow(
|
||||
null,
|
||||
"chrome:\/\/zotero\/content\/standalone\/profilerViewer.xhtml",
|
||||
null,
|
||||
"chrome,dialog=no,resizable,centerscreen,scrollbars",
|
||||
arg
|
||||
);
|
||||
return await new Promise(resolve => {
|
||||
viewerWin.addEventListener("load", () => {
|
||||
resolve(viewerWin.document.getElementById("content"));
|
||||
}, { once: true });
|
||||
});
|
||||
}
|
||||
const contentBrowser = await new Promise(resolveOnContentBrowserCreated =>' \
|
||||
chrome/devtools/modules/devtools/client/performance-new/shared/browser.js
|
||||
fi
|
||||
|
||||
# 5.0.96.3 / 5.0.97-beta.37+ddc7be75c
|
||||
|
|
@ -555,7 +363,6 @@ for locale in `ls chrome/locale/`; do
|
|||
cp "$CALLDIR/assets/branding/locale/brand.ftl" localization/$locale/branding/brand.ftl
|
||||
|
||||
mkdir -p localization/$locale/toolkit/global
|
||||
cp chrome/locale/$locale/zotero/mozilla/arrowscrollbox.ftl localization/$locale/toolkit/global
|
||||
cp chrome/locale/$locale/zotero/mozilla/textActions.ftl localization/$locale/toolkit/global
|
||||
cp chrome/locale/$locale/zotero/mozilla/wizard.ftl localization/$locale/toolkit/global
|
||||
|
||||
|
|
@ -580,9 +387,6 @@ replace_line 'handle: function bch_handle\(cmdLine\) {' 'handle: function bch_ha
|
|||
' modules/BrowserContentHandler.sys.mjs
|
||||
export CALLDIR && perl -pi -e 'BEGIN { local $/; open $fh, "$ENV{CALLDIR}/assets/commandLineHandler.js"; $replacement = <$fh>; close $fh; } s/\/\/ TEST_OPTIONS_PLACEHOLDER/$replacement/' modules/BrowserContentHandler.sys.mjs
|
||||
|
||||
# Don't try to initialize Places, since we've removed its files
|
||||
#replace_line 'this._placesInitialized = true;' 'if (true) return; this._placesInitialized = true;' components/places/PlacesBrowserStartup.sys.mjs
|
||||
|
||||
# Move test files to root directory
|
||||
if [ $include_tests -eq 1 ]; then
|
||||
cat test/chrome.manifest >> chrome.manifest
|
||||
|
|
@ -592,11 +396,11 @@ fi
|
|||
|
||||
# Copy platform-specific assets
|
||||
if [ $BUILD_MAC == 1 ]; then
|
||||
copy_dir "$CALLDIR/assets/mac" .
|
||||
rsync -a "$CALLDIR/assets/mac/" ./
|
||||
elif [ $BUILD_WIN == 1 ]; then
|
||||
copy_dir "$CALLDIR/assets/win" .
|
||||
rsync -a "$CALLDIR/assets/win/" ./
|
||||
elif [ $BUILD_LINUX == 1 ]; then
|
||||
copy_dir "$CALLDIR/assets/unix" .
|
||||
rsync -a "$CALLDIR/assets/unix/" ./
|
||||
fi
|
||||
|
||||
# Add word processor plug-ins
|
||||
|
|
@ -694,25 +498,16 @@ if [ $BUILD_MAC == 1 ]; then
|
|||
|
||||
# Merge relevant assets from Firefox
|
||||
mkdir "$CONTENTSDIR/MacOS"
|
||||
cp -r "$MAC_RUNTIME_PATH/Contents/MacOS/"!(firefox|firefox-bin|crashhelper|crashreporter.app|minidump-analyzer|nmhproxy|pingsender|updater.app) "$CONTENTSDIR/MacOS"
|
||||
cp -r "$MAC_RUNTIME_PATH/Contents/Resources/"!(application.ini|Assets.car|browser|defaults|precomplete|removed-files|updater.ini|update-settings.ini|webapprt*|*.icns|*.lproj) "$CONTENTSDIR/Resources"
|
||||
|
||||
# Add our custom ChannelPrefs.framework and change channel if not a source build
|
||||
mkdir "$CONTENTSDIR/Frameworks"
|
||||
tar xvf "$CALLDIR/mac/ChannelPrefs.framework.tar.xz" -C "$CONTENTSDIR/Frameworks"
|
||||
if [ "$UPDATE_CHANNEL" != "source" ]; then
|
||||
"$CALLDIR/mac/set-channel-prefs-channel" "$CONTENTSDIR/Frameworks/ChannelPrefs.framework/ChannelPrefs" source $UPDATE_CHANNEL
|
||||
fi
|
||||
|
||||
cp -r "$MAC_RUNTIME_PATH/Contents/MacOS/"!(firefox|firefox-bin|crashreporter.app|pingsender|updater.app) "$CONTENTSDIR/MacOS"
|
||||
cp -r "$MAC_RUNTIME_PATH/Contents/Resources/"!(application.ini|browser|defaults|precomplete|removed-files|updater.ini|update-settings.ini|webapprt*|*.icns|*.lproj) "$CONTENTSDIR/Resources"
|
||||
|
||||
# Use our own launcher
|
||||
check_lfs_file "$CALLDIR/mac/zotero.xz"
|
||||
xz -d --stdout "$CALLDIR/mac/zotero.xz" > "$CONTENTSDIR/MacOS/zotero"
|
||||
chmod 755 "$CONTENTSDIR/MacOS/zotero"
|
||||
|
||||
# Overlay custom Firefox components (e.g., XUL or libmozglue.dylib)
|
||||
if [ -n "$custom_components_hash_mac" ]; then
|
||||
cp -R "$MAC_RUNTIME_PATH/../MacOS/." "$CONTENTSDIR/MacOS/"
|
||||
fi
|
||||
# TEMP: Custom version of XUL with some backported Mozilla bug fixes
|
||||
cp "$MAC_RUNTIME_PATH/../MacOS/XUL" "$CONTENTSDIR/MacOS/"
|
||||
|
||||
# Use our own updater, because Mozilla's requires updates signed by Mozilla
|
||||
cd "$CONTENTSDIR/MacOS"
|
||||
|
|
@ -739,7 +534,7 @@ if [ $BUILD_MAC == 1 ]; then
|
|||
echo
|
||||
|
||||
# Copy app files
|
||||
copy_dir "$base_dir" "$CONTENTSDIR/Resources"
|
||||
rsync -a "$base_dir/" "$CONTENTSDIR/Resources/"
|
||||
|
||||
# Add word processor plug-ins
|
||||
mkdir "$CONTENTSDIR/Resources/integration"
|
||||
|
|
@ -750,66 +545,12 @@ if [ $BUILD_MAC == 1 ]; then
|
|||
find "$CONTENTSDIR" -depth -type d -name .git -exec rm -rf {} \;
|
||||
find "$CONTENTSDIR" \( -name .DS_Store -or -name update.rdf \) -exec rm -f {} \;
|
||||
|
||||
# Add Safari extensions -- this depends on signing but needs to be done before generating
|
||||
# Add Safari App Extension -- this depends on signing but needs to be done before generating
|
||||
# the precomplete file
|
||||
#
|
||||
# $SAFARI_APPEX is a stub appex built from the safari-web-extension wrapper project. The web
|
||||
# extension itself comes from $SAFARI_EXT_RESOURCES (a zotero-connectors build/safari
|
||||
# directory), which replaces the stub's placeholder resources here before signing.
|
||||
#
|
||||
# $SAFARI_APP_EXTENSION is an optional prebuilt legacy Safari App Extension, embedded
|
||||
# alongside the web extension for Safari versions that can't load Developer ID web
|
||||
# extensions (supported in Safari 18.4 and later). On Safari versions that can load the
|
||||
# web extension, the SFSafariAppExtensionBundleIdentifiersToReplace key causes it to
|
||||
# replace the App Extension.
|
||||
if [[ $SIGN == 1 ]] && [[ -n "$SAFARI_APPEX" ]] && [[ -d "$SAFARI_APPEX" ]]; then
|
||||
if [[ -z "${SAFARI_EXT_RESOURCES:-}" ]] || [[ ! -f "$SAFARI_EXT_RESOURCES/manifest.json" ]]; then
|
||||
echo "SAFARI_EXT_RESOURCES doesn't contain a web extension -- aborting" 2>&1
|
||||
exit 1
|
||||
fi
|
||||
bundle_identifier=$(/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "$APPDIR/Contents/Info.plist")
|
||||
mkdir "$APPDIR/Contents/PlugIns"
|
||||
|
||||
webext_appex="$APPDIR/Contents/PlugIns/ZoteroSafariWebExtension.appex"
|
||||
cp -R "$SAFARI_APPEX" "$webext_appex"
|
||||
appex_resources="$webext_appex/Contents/Resources"
|
||||
rm -rf "$appex_resources"
|
||||
mkdir "$appex_resources"
|
||||
cp -R "$SAFARI_EXT_RESOURCES/." "$appex_resources/"
|
||||
|
||||
# Show the connector version in Safari
|
||||
connector_version=$(python3 -c "import json, sys; print(json.load(open(sys.argv[1]))['version'])" "$appex_resources/manifest.json")
|
||||
if [[ $connector_version == *999* ]] && [ "$UPDATE_CHANNEL" != "test" ]; then
|
||||
echo "Placeholder connector version $connector_version not allowed for '$UPDATE_CHANNEL' channel -- aborting" 2>&1
|
||||
exit 1
|
||||
fi
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $connector_version" \
|
||||
"$webext_appex/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleVersion $connector_version" \
|
||||
"$webext_appex/Contents/Info.plist"
|
||||
|
||||
# Give the appex the same bundle identifier prefix as the parent app
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleIdentifier $bundle_identifier.SafariWebExtension" \
|
||||
"$webext_appex/Contents/Info.plist"
|
||||
|
||||
# Replace the legacy App Extension on Safari versions that can load the web extension
|
||||
/usr/libexec/PlistBuddy -c "Add :NSExtension:SFSafariAppExtensionBundleIdentifiersToReplace array" \
|
||||
"$webext_appex/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Add :NSExtension:SFSafariAppExtensionBundleIdentifiersToReplace:0 string $bundle_identifier.SafariExtension" \
|
||||
"$webext_appex/Contents/Info.plist"
|
||||
|
||||
# Add legacy Safari App Extension
|
||||
if [[ -n "${SAFARI_APP_EXTENSION:-}" ]]; then
|
||||
if [[ ! -d "$SAFARI_APP_EXTENSION" ]]; then
|
||||
echo "SAFARI_APP_EXTENSION not found at $SAFARI_APP_EXTENSION -- aborting" 2>&1
|
||||
exit 1
|
||||
fi
|
||||
appext_appex="$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
|
||||
cp -R "$SAFARI_APP_EXTENSION" "$appext_appex"
|
||||
rm -rf "$appext_appex/Contents/Resources/safari/test"
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleIdentifier $bundle_identifier.SafariExtension" \
|
||||
"$appext_appex/Contents/Info.plist"
|
||||
fi
|
||||
cp -R $SAFARI_APPEX "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
|
||||
rm -rf "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/test/"
|
||||
fi
|
||||
|
||||
# Copy over removed-files and make a precomplete file
|
||||
|
|
@ -831,50 +572,30 @@ if [ $BUILD_MAC == 1 ]; then
|
|||
# Clear extended attributes, which can cause codesign to fail
|
||||
/usr/bin/xattr -cr "$APPDIR"
|
||||
|
||||
# Sign libraries and updater without entitlements
|
||||
/usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" \
|
||||
"$APPDIR/Contents/MacOS/XUL" \
|
||||
"$APPDIR/Contents/MacOS/updater.app/Contents/Frameworks/UpdateSettings.framework" \
|
||||
"$APPDIR/Contents/MacOS/updater.app" \
|
||||
"$APPDIR/Contents/Frameworks/ChannelPrefs.framework"
|
||||
find "$APPDIR/Contents" -name '*.dylib' -print0 | xargs -0 /usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID"
|
||||
|
||||
# Sign bundled apps and main app bundle
|
||||
# Sign app
|
||||
entitlements_file="$CALLDIR/mac/entitlements.xml"
|
||||
find "$APPDIR/Contents" -name '*.app' -not -name "updater.app" -print0 | xargs -0 /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/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"
|
||||
|
||||
# Sign .jnilib (Java native shared library) within LibreOffice extension, since notarization
|
||||
# started failing without this. The .jnilib is within a .jar within the .oxt, so we have to
|
||||
# extract both, sign the libraries, and then update each ZIP.
|
||||
# Sign Safari App Extension
|
||||
#
|
||||
# TODO: Remove this block once the plugin ships a jna.jar without the macOS native
|
||||
# libraries, which are never loaded (JNA is used only on Windows)
|
||||
pushd "$BUILD_DIR"
|
||||
mkdir libreoffice-repack
|
||||
cd libreoffice-repack
|
||||
unzip -q "$APPDIR/Contents/Resources/integration/libreoffice/Zotero_LibreOffice_Integration.oxt" external_jars/jna.jar
|
||||
unzip -q external_jars/jna.jar 'com/sun/jna/darwin*/libjnidispatch.jnilib'
|
||||
/usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" com/sun/jna/darwin*/libjnidispatch.jnilib
|
||||
zip -u external_jars/jna.jar com/sun/jna/darwin*/libjnidispatch.jnilib
|
||||
zip -u "$APPDIR/Contents/Resources/integration/libreoffice/Zotero_LibreOffice_Integration.oxt" external_jars/jna.jar
|
||||
cd ..
|
||||
rm -rf libreoffice-repack
|
||||
popd
|
||||
|
||||
# Sign Safari extensions
|
||||
#
|
||||
# Even though they're signed by Xcode, we sign them again to make sure they match the parent app signature
|
||||
for appex in "$APPDIR"/Contents/PlugIns/*.appex; do
|
||||
if [ ! -d "$appex" ]; then
|
||||
continue
|
||||
fi
|
||||
# Even though it's signed by Xcode, we sign it again to make sure it matches the parent app signature
|
||||
if [ -d "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex" ]; then
|
||||
echo
|
||||
# Extract entitlements, which differ from parent app
|
||||
/usr/bin/codesign -d --entitlements "$BUILD_DIR/safari-entitlements.plist" --xml "$appex"
|
||||
/usr/bin/codesign -d --entitlements "$BUILD_DIR/safari-entitlements.plist" --xml "$SAFARI_APPEX"
|
||||
|
||||
find "$appex/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
|
||||
/usr/bin/codesign --force --options runtime --entitlements "$BUILD_DIR/safari-entitlements.plist" --sign "$DEVELOPER_ID" "$appex"
|
||||
done
|
||||
# Add suffix to appex bundle identifier
|
||||
if [ $UPDATE_CHANNEL == "beta" ] || [ $UPDATE_CHANNEL == "dev" ] || [ $UPDATE_CHANNEL == "source" ]; then
|
||||
perl -pi -e "s/org\.zotero\.SafariExtensionApp\.SafariExtension/org.zotero.SafariExtensionApp.SafariExtension-$UPDATE_CHANNEL/" "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Info.plist"
|
||||
fi
|
||||
find "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
|
||||
/usr/bin/codesign --force --options runtime --entitlements "$BUILD_DIR/safari-entitlements.plist" --sign "$DEVELOPER_ID" "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
|
||||
fi
|
||||
|
||||
# Sign final app package
|
||||
echo
|
||||
|
|
@ -882,21 +603,11 @@ if [ $BUILD_MAC == 1 ]; then
|
|||
|
||||
# Verify app
|
||||
/usr/bin/codesign --verify -vvvv "$APPDIR"
|
||||
# Verify Safari extensions
|
||||
for appex in "$APPDIR"/Contents/PlugIns/*.appex; do
|
||||
if [ ! -d "$appex" ]; then
|
||||
continue
|
||||
fi
|
||||
# Verify Safari App Extension
|
||||
if [[ -n "$SAFARI_APPEX" ]] && [[ -d "$SAFARI_APPEX" ]]; then
|
||||
echo
|
||||
/usr/bin/codesign --verify -vvvv "$appex"
|
||||
done
|
||||
elif [ -n "$custom_components_hash_mac" ]; then
|
||||
# Mozilla's helper apps (plugin-container, GPU helper, etc.) are signed with Mozilla's Team ID
|
||||
# and the hardened runtime, so without our signature they can't load custom components such as
|
||||
# libmozglue.dylib and fail to launch. Re-sign them ad hoc, which removes both. (Signed builds
|
||||
# re-sign everything with our Developer ID above.)
|
||||
find "$APPDIR/Contents/MacOS" -maxdepth 1 -name '*.app' -not -name "updater.app" -print0 \
|
||||
| xargs -0 /usr/bin/codesign --force --sign -
|
||||
/usr/bin/codesign --verify -vvvv "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build and notarize disk image
|
||||
|
|
@ -951,15 +662,10 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
|
||||
fi
|
||||
|
||||
if [[ -n $arch ]]; then
|
||||
archs=("$(get_canonical_arch w $arch)");
|
||||
else
|
||||
archs=(win-x64 win-arm64 win32);
|
||||
fi
|
||||
for arch in "${archs[@]}"; do
|
||||
for arch in win32 win-x64 win-arm64; do
|
||||
echo "Building Zotero_$arch"
|
||||
|
||||
runtime_path="${WIN_RUNTIME_PATH_PREFIX}$arch"
|
||||
runtime_path="${WIN_RUNTIME_PATH_PREFIX}${arch}"
|
||||
|
||||
# Set up directory
|
||||
APPDIR="$STAGE_DIR/Zotero_$arch"
|
||||
|
|
@ -969,7 +675,7 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
#
|
||||
# 'i686' is a huge directory containing x86 versions of xul.dll and other files in
|
||||
# Firefox ARM64 builds for use with the EME DRM plugins
|
||||
cp -R "$runtime_path"/!(application.ini|browser|crashhelper.exe|crashreporter*|default-browser-agent.exe|defaultagent*|defaults|desktop-launcher|devtools-files|firefox*|i686|maintenanceservice*|minidump-analyzer.exe|nmhproxy.exe|pingsender.exe|private_browsing*|precomplete|removed-files|uninstall|update*) "$APPDIR"
|
||||
cp -R "$runtime_path"/!(application.ini|browser|crashreporter*|default-browser-agent.exe|defaultagent*|defaults|devtools-files|firefox*|i686|maintenanceservice*|minidump-analyzer.exe|pingsender.exe|private_browsing*|precomplete|removed-files|uninstall|update*) "$APPDIR"
|
||||
|
||||
# Copy zotero.exe, which is built directly from Firefox source and then modified by
|
||||
# ResourceHacker to add icons
|
||||
|
|
@ -993,12 +699,17 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
--set-product-version "$VERSION"
|
||||
fi
|
||||
|
||||
# Sign updater
|
||||
if [ $SIGN -eq 1 ]; then
|
||||
"$CALLDIR/win/codesign" "$APPDIR/updater.exe" "$SIGNATURE_DESC Updater"
|
||||
fi
|
||||
|
||||
# Copy app files
|
||||
copy_dir "$base_dir" "$APPDIR"
|
||||
rsync -a "$base_dir/" "$APPDIR/"
|
||||
#mv "$APPDIR/app/application.ini" "$APPDIR/"
|
||||
|
||||
# Copy in common files
|
||||
copy_dir "$COMMON_APPDIR" "$APPDIR"
|
||||
rsync -a "$COMMON_APPDIR/" "$APPDIR/"
|
||||
|
||||
cat "$CALLDIR/win/installer/updater_append.ini" >> "$APPDIR/updater.ini"
|
||||
|
||||
|
|
@ -1040,17 +751,6 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
find "$APPDIR" \( -name .DS_Store -or -name '.git*' -or -name '.travis.yml' -or -name update.rdf -or -name '*.bak' \) -exec rm -f {} \;
|
||||
find "$APPDIR" \( -name '*.exe' -or -name '*.dll' \) -exec chmod 755 {} \;
|
||||
|
||||
if [[ $PACKAGE -eq 1 ]] && [[ $SIGN -eq 1 ]]; then
|
||||
"$CALLDIR/win/codesign" "$APPDIR/zotero.exe" "$SIGNATURE_DESC"
|
||||
sleep $SIGNTOOL_DELAY
|
||||
"$CALLDIR/win/codesign" "$APPDIR/updater.exe" "$SIGNATURE_DESC Updater"
|
||||
sleep $SIGNTOOL_DELAY
|
||||
# Re-sign modified xul.dll
|
||||
"$CALLDIR/win/codesign" "$APPDIR/xul.dll" "$SIGNATURE_DESC"
|
||||
sleep $SIGNTOOL_DELAY
|
||||
"$CALLDIR/win/codesign" "$APPDIR/integration/word-for-windows/libzoteroWinWordIntegration.dll" "$SIGNATURE_DESC Word Plugin"
|
||||
fi
|
||||
|
||||
# Copy over removed-files and make a precomplete file
|
||||
pushd "$APPDIR"
|
||||
cp "$CALLDIR/update-packaging/removed-files_$arch" removed-files
|
||||
|
|
@ -1080,6 +780,7 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
sleep $SIGNTOOL_DELAY
|
||||
fi
|
||||
|
||||
|
||||
if [ "$arch" = "win32" ]; then
|
||||
INSTALLER_PATH="$DIST_DIR/Zotero-${VERSION}_win32_setup.exe"
|
||||
elif [ "$arch" = "win-x64" ]; then
|
||||
|
|
@ -1088,6 +789,11 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
INSTALLER_PATH="$DIST_DIR/Zotero-${VERSION}_arm64_setup.exe"
|
||||
fi
|
||||
|
||||
if [ $SIGN -eq 1 ]; then
|
||||
"$CALLDIR/win/codesign" "$APPDIR/zotero.exe" "$SIGNATURE_DESC"
|
||||
sleep $SIGNTOOL_DELAY
|
||||
fi
|
||||
|
||||
# Stage installer
|
||||
INSTALLER_STAGE_DIR="$BUILD_DIR/win_installer/staging"
|
||||
rm -rf "$INSTALLER_STAGE_DIR"
|
||||
|
|
@ -1137,14 +843,14 @@ fi
|
|||
|
||||
# Linux
|
||||
if [ $BUILD_LINUX == 1 ]; then
|
||||
if [[ -n $arch ]]; then
|
||||
archs=("$(get_canonical_arch l $arch)")
|
||||
# Skip 32-bit build in tests
|
||||
if [[ "${ZOTERO_TEST:-}" = "1" ]] || [[ "${SKIP_32:-}" = "1" ]]; then
|
||||
archs="x86_64"
|
||||
else
|
||||
archs=(x64 arm64)
|
||||
archs="i686 x86_64"
|
||||
fi
|
||||
for arch in "${archs[@]}"; do
|
||||
[[ $arch == x64 ]] && arch="x86_64"
|
||||
|
||||
|
||||
for arch in $archs; do
|
||||
runtime_path="${LINUX_RUNTIME_PATH_PREFIX}${arch}"
|
||||
|
||||
# Set up directory
|
||||
|
|
@ -1154,7 +860,7 @@ if [ $BUILD_LINUX == 1 ]; then
|
|||
mkdir "$APPDIR"
|
||||
|
||||
# Merge relevant assets from Firefox
|
||||
cp -r "$runtime_path/"!(application.ini|browser|crashhelper|crashreporter|crashreporter.ini|defaults|devtools-files|firefox|pingsender|precomplete|removed-files|run-mozilla.sh|update-settings.ini|updater|updater.ini) "$APPDIR"
|
||||
cp -r "$runtime_path/"!(application.ini|browser|defaults|devtools-files|crashreporter|crashreporter.ini|firefox|pingsender|precomplete|removed-files|run-mozilla.sh|update-settings.ini|updater|updater.ini) "$APPDIR"
|
||||
|
||||
# Use our own launcher that calls the original Firefox executable with -app
|
||||
mv "$APPDIR"/firefox-bin "$APPDIR"/zotero-bin
|
||||
|
|
@ -1170,14 +876,12 @@ if [ $BUILD_LINUX == 1 ]; then
|
|||
chmod 755 "$APPDIR/updater"
|
||||
|
||||
# Copy app files
|
||||
copy_dir "$base_dir" "$APPDIR"
|
||||
rsync -a "$base_dir/" "$APPDIR/"
|
||||
|
||||
# Add word processor plug-ins
|
||||
mkdir "$APPDIR/integration"
|
||||
cp -RH "$CALLDIR/modules/zotero-libreoffice-integration/install" "$APPDIR/integration/libreoffice"
|
||||
|
||||
# Firefox includes an 'icons' folder with updater.png, but some Linux distro builds omit it
|
||||
mkdir -p "$APPDIR/icons"
|
||||
# Copy icons
|
||||
cp "$CALLDIR/linux/icons/icon32.png" "$APPDIR/icons/"
|
||||
cp "$CALLDIR/linux/icons/icon64.png" "$APPDIR/icons/"
|
||||
|
|
@ -1200,9 +904,9 @@ if [ $BUILD_LINUX == 1 ]; then
|
|||
|
||||
if [ $PACKAGE == 1 ]; then
|
||||
# Create tar
|
||||
rm -f "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.xz"
|
||||
rm -f "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.bz2"
|
||||
cd "$STAGE_DIR"
|
||||
tar -cJf "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.xz" "Zotero_linux-$arch"
|
||||
tar -cjf "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.bz2" "Zotero_linux-$arch"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
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
|
||||
GECKO_VERSION_MAC="115.9.1esr"
|
||||
GECKO_VERSION_LINUX="115.9.1esr"
|
||||
GECKO_VERSION_WIN="115.14.0esr"
|
||||
RUST_VERSION=1.69.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"
|
||||
custom_components_hash_mac="bec8e3adebf8d5021f1f35fd2c65d752d4979839cbdd1ee1aa4b1d3d5ba0953b"
|
||||
|
||||
APP_NAME="Zotero"
|
||||
APP_ID="zotero\@zotero.org"
|
||||
|
|
@ -28,9 +25,6 @@ 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\'
|
||||
|
|
@ -48,7 +42,8 @@ S3_CI_ZIP_PATH="ci/client"
|
|||
S3_DIST_PATH="client"
|
||||
|
||||
DEPLOY_HOST="deploy.zotero"
|
||||
DEPLOY_PATH="www/www-production/public/download/client"
|
||||
DEPLOY_PATH="www/www-production/public/download/client/manifests"
|
||||
DEPLOY_CMD="ssh $DEPLOY_HOST update-site-files"
|
||||
|
||||
BUILD_PLATFORMS=""
|
||||
NUM_INCREMENTALS=6
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
# Uncomment to build 32-bit
|
||||
#ac_add_options --target=i686
|
||||
|
||||
ac_add_options --enable-bootstrap
|
||||
|
||||
mk_add_options AUTOCLOBBER=1
|
||||
|
|
@ -12,8 +15,7 @@ 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
|
||||
ac_add_options --disable-eme
|
||||
|
||||
export MOZILLA_OFFICIAL=1
|
||||
export RELEASE_OR_BETA=1
|
||||
|
|
@ -22,4 +24,4 @@ MOZ_REQUIRE_SIGNING=
|
|||
ac_add_options --enable-official-branding
|
||||
|
||||
# Build updater without MAR signature verification
|
||||
ac_add_options --enable-unverified-updates
|
||||
ac_add_options --disable-verify-mar
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a1f368bb36408d61d4b8f9dda561738686e626513346e138d55a0ef58055a6fc
|
||||
size 123776
|
||||
oid sha256:d7e9c81d2f86ec0f70d75107a022649292889807bc8b0b7eaf1bdfd5c1030bf1
|
||||
size 74516
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -161,8 +161,8 @@
|
|||
<string>zotero</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>Zotero {{VERSION}}, © 2006-2018 Contributors</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>zotero</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.zotero.zotero</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
|
|
|
|||
Binary file not shown.
BIN
app/mac/Contents/Resources/zotero.icns
Normal file
BIN
app/mac/Contents/Resources/zotero.icns
Normal file
Binary file not shown.
|
|
@ -9,10 +9,10 @@ fx_app_name=Firefox.app
|
|||
|
||||
# Get Mozilla source directory from command line
|
||||
if [ -z "${1:-}" ]; then
|
||||
echo "Usage: $0 /path/to/firefox" >&2
|
||||
echo "Usage: $0 /path/to/mozilla-unified" >&2
|
||||
exit 1
|
||||
fi
|
||||
GECKO_PATH="$(cd "$1" && pwd)"
|
||||
GECKO_PATH=$1
|
||||
mach=$GECKO_PATH/mach
|
||||
|
||||
BUILD_DIR=`mktemp -d`
|
||||
|
|
@ -27,7 +27,8 @@ 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 target add aarch64-apple-darwin
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup default $RUST_VERSION
|
||||
|
||||
cp "$SCRIPT_DIR/mozconfig" "$GECKO_PATH"
|
||||
|
|
@ -55,36 +56,8 @@ 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
|
||||
# TEMP: Copy a unified XUL for now
|
||||
cp x64/$fx_app_name/Contents/MacOS/XUL "$APP_ROOT_DIR/mac/XUL"
|
||||
|
||||
# Uncomment to build updater
|
||||
#cd x64/$fx_app_name/Contents/MacOS/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ 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
|
||||
ac_add_options --with-macos-sdk=$HOME/tmp/MacOSX13.3.sdk
|
||||
|
||||
mk_add_options AUTOCLOBBER=1
|
||||
|
||||
|
|
@ -24,11 +24,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -303,7 +303,6 @@ my(@gCleanup, %gConfig, $gDarwinMajor, $gDryRun, $gVerbosity);
|
|||
'cmd_chmod' => 'chmod',
|
||||
'cmd_diskutil' => 'diskutil',
|
||||
'cmd_du' => 'du',
|
||||
'cmd_find' => 'find',
|
||||
'cmd_hdid' => 'hdid',
|
||||
'cmd_hdiutil' => 'hdiutil',
|
||||
'cmd_mkdir' => 'mkdir',
|
||||
|
|
@ -312,7 +311,6 @@ my(@gCleanup, %gConfig, $gDarwinMajor, $gDryRun, $gVerbosity);
|
|||
'cmd_rm' => 'rm',
|
||||
'cmd_rsync' => 'rsync',
|
||||
'cmd_SetFile' => '/Developer/Tools/SetFile',
|
||||
'cmd_xattr' => 'xattr',
|
||||
|
||||
# create_directly indicates whether hdiutil create supports
|
||||
# -srcfolder and -srcdevice. It does on >= 10.3 (Panther).
|
||||
|
|
@ -998,65 +996,6 @@ sub diskImageMaker($$$$$$$$) {
|
|||
cleanupDie('unlink hybridImage failed: '.$!);
|
||||
}
|
||||
}
|
||||
|
||||
# hdiutil makehybrid gives every file on the image non-empty
|
||||
# Finder info (it sets an icon location), which appears as a
|
||||
# com.apple.FinderInfo extended attribute that "codesign
|
||||
# --verify --strict" rejects as detritus and that can cause
|
||||
# Safari to silently ignore a bundled web extension (observed
|
||||
# with Safari 17.6 on macOS 12; the exact trigger conditions
|
||||
# are unknown). Mount a read-write copy of the image and strip
|
||||
# the attributes before compressing. Note that Finder flags set
|
||||
# with --attribute are stored in the same Finder info, so if
|
||||
# --attribute is ever used, the affected files would need to be
|
||||
# excluded from the strip.
|
||||
if($uncompressedImage eq $hybridImage) {
|
||||
my($udrwImage);
|
||||
$udrwImage = giveExtension($tempDir.'/udrw', '.dmg');
|
||||
|
||||
if(command($gConfig{'cmd_hdiutil'}, 'convert', '-format', 'UDRW',
|
||||
'-ov', $hybridImage, '-o', $udrwImage) != 0) {
|
||||
cleanupDie('hdiutil convert to UDRW failed');
|
||||
}
|
||||
|
||||
push(@gCleanup,
|
||||
sub {commandInternalVerbosity(0, 'unlink', $udrwImage);});
|
||||
|
||||
$uncompressedImage = $udrwImage;
|
||||
|
||||
# $hybridImage is no longer needed. Remove it and its cleanup
|
||||
# entry, which is below the entry for $udrwImage.
|
||||
my(@tempCleanup) = splice(@gCleanup, -2);
|
||||
push(@gCleanup, $tempCleanup[1]);
|
||||
|
||||
if(commandInternal('unlink', $hybridImage) != 1) {
|
||||
cleanupDie('unlink hybridImage failed: '.$!);
|
||||
}
|
||||
}
|
||||
|
||||
my($rootDevice, $partitionDevice, $partitionMountPoint);
|
||||
if(!(($rootDevice, $partitionDevice, $partitionMountPoint) =
|
||||
hdidMountImage($tempMount, $uncompressedImage))) {
|
||||
cleanupDie('hdid mount failed');
|
||||
}
|
||||
|
||||
push(@gCleanup, sub {commandVerbosity(0,
|
||||
$gConfig{'cmd_diskutil'}, 'eject', $rootDevice);});
|
||||
|
||||
if(command($gConfig{'cmd_find'}, $partitionMountPoint,
|
||||
'-mindepth', '1', '-not', '-type', 'l',
|
||||
'-xattrname', 'com.apple.FinderInfo',
|
||||
'-exec', $gConfig{'cmd_xattr'}, '-d', 'com.apple.FinderInfo',
|
||||
'{}', '+') != 0) {
|
||||
cleanupDie('stripping com.apple.FinderInfo failed');
|
||||
}
|
||||
|
||||
# Pop diskutil eject
|
||||
pop(@gCleanup);
|
||||
|
||||
if(command($gConfig{'cmd_diskutil'}, 'eject', $rootDevice) != 0) {
|
||||
cleanupDie('diskutil eject failed');
|
||||
}
|
||||
}
|
||||
else {
|
||||
# makehybrid is not available, fall back to making a UDRW and
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:361730d7cea854b38c6968143555c5f5fe251e1c95f265a6133514ee72ea2c53
|
||||
size 98904
|
||||
oid sha256:53642619a815ae1c848dfb6f33e1e528db74edc39b3a692b6f312cace640c437
|
||||
size 92884
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
Subproject commit 2e78b1010ad49b4dde70ec9f0094b34a7b6f8965
|
||||
Subproject commit 55a2de7e79f2d92c26c7a7e1ac4c63178b95121f
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 300886d1984de7a0006d7a48310baa0c8c99bea0
|
||||
Subproject commit de8ab22db748bef42f3730c20b9da1c0563de6c2
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit c0aa6e4bef039d94e17e81cb28b1fe9170c45b96
|
||||
Subproject commit 13a6cc157397ed76728806e1c9a23e29f45892bb
|
||||
|
|
@ -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
|
||||
30
app/scripts/7.0_beta_build_and_deploy
Executable file
30
app/scripts/7.0_beta_build_and_deploy
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/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"
|
||||
export SAFARI_APPEX="$ROOT_DIR/../safari-app-extension-builds/beta/ZoteroSafariExtension.appex"
|
||||
|
||||
# Set Safari extension LSMinimumSystemVersion to Mojave for betas
|
||||
perl -pi -e 's/<string>11\.0<\/string>/<string>10.14<\/string>/' "$SAFARI_APPEX"/Contents/Info.plist
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
./check_requirements
|
||||
|
||||
hash=`./get_repo_branch_hash $BRANCH`
|
||||
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
|
||||
./build_and_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL
|
||||
27
app/scripts/7.0_dev_build_and_deploy
Executable file
27
app/scripts/7.0_dev_build_and_deploy
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/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"
|
||||
export SAFARI_APPEX="$ROOT_DIR/../safari-app-extension-builds/dev/ZoteroSafariExtension.appex"
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
./check_requirements
|
||||
|
||||
hash=`./get_repo_branch_hash $BRANCH`
|
||||
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
|
||||
./build_and_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL -i 1
|
||||
29
app/scripts/7.0_release_build_and_deploy
Executable file
29
app/scripts/7.0_release_build_and_deploy
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/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="main"
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
export SAFARI_APPEX="$ROOT_DIR/../safari-app-extension-builds/release/ZoteroSafariExtension.appex"
|
||||
fi
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
./check_requirements
|
||||
|
||||
hash=`./get_repo_branch_hash $BRANCH`
|
||||
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
|
||||
./build_and_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL
|
||||
27
app/scripts/7.0_test_build_and_deploy
Executable file
27
app/scripts/7.0_test_build_and_deploy
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/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"
|
||||
export SAFARI_APPEX="$ROOT_DIR/../safari-app-extension-builds/test/ZoteroSafariExtension.appex"
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
./check_requirements
|
||||
|
||||
hash=`./get_repo_branch_hash $BRANCH`
|
||||
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
|
||||
./build_and_deploy -d "$build_dir" -p $BUILD_PLATFORMS -c $CHANNEL -i 1
|
||||
|
|
@ -37,14 +37,11 @@ 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
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ Update a builds manifest with info on a given build
|
|||
"""
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import shutil
|
||||
import json
|
||||
|
|
@ -26,7 +25,7 @@ def main():
|
|||
try:
|
||||
file = args.file
|
||||
version = args.version
|
||||
short_version = re.match(r'\d+\.\d+', version).group(0)
|
||||
short_version = version[0:3]
|
||||
|
||||
# Back up JSON file
|
||||
shutil.copy2(file, file + '.bak')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
14
app/scripts/bootstrap.sh
Normal file
14
app/scripts/bootstrap.sh
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#!/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).
|
||||
# Builds and deploys Zotero with full and incremental updates
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
|
|
@ -61,8 +60,8 @@ if [ -z "$VERSION" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Enforce signing unless a test build or macOS, which doesn't start otherwise
|
||||
if [[ "$CHANNEL" != "test" || $PLATFORMS = "m" ]]; then
|
||||
# Enforce signing unless a test build
|
||||
if [ "$CHANNEL" != "test" ]; then
|
||||
SIGN="-e"
|
||||
else
|
||||
SIGN=""
|
||||
|
|
@ -108,23 +107,13 @@ do
|
|||
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"
|
||||
|
|
@ -137,31 +126,46 @@ 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"
|
||||
# Upload file lists for each platform
|
||||
channel_deploy_path="$DEPLOY_PATH/$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"
|
||||
rsync -rv "$TEMP_DIR/version_info/" $DEPLOY_HOST:"$channel_deploy_path/$VERSION/"
|
||||
|
||||
# Download updates JSON for each platform, update it, and reupload it
|
||||
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" ;;
|
||||
m)
|
||||
architectures="mac"
|
||||
;;
|
||||
w)
|
||||
architectures="win32 win-x64 win-arm64"
|
||||
;;
|
||||
l)
|
||||
architectures="linux-i686 linux-x86_64"
|
||||
;;
|
||||
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"
|
||||
|
||||
for arch in $architectures;
|
||||
do
|
||||
jsonfile="updates-$arch.json"
|
||||
|
||||
scp $DEPLOY_HOST:"$channel_deploy_path/$jsonfile" "$TEMP_DIR/$jsonfile"
|
||||
"$SCRIPT_DIR/add_version_info" -f "$TEMP_DIR/$jsonfile" -v $VERSION -b $BUILD_ID
|
||||
scp "$TEMP_DIR/$jsonfile" $DEPLOY_HOST:"$channel_deploy_path/$jsonfile"
|
||||
done
|
||||
done
|
||||
rsync -rv "$TEMP_DIR/version_info/" $DEPLOY_HOST:"$channel_deploy_path/$VERSION/"
|
||||
|
||||
# Add version to incremental lists
|
||||
echo
|
||||
for i in `seq 0 1 $((${#PLATFORMS}-1))`
|
||||
do
|
||||
"$SCRIPT_DIR/manage_incrementals" -c $CHANNEL -p ${PLATFORMS:i:1} -a $VERSION
|
||||
done
|
||||
|
||||
$DEPLOY_CMD
|
||||
|
||||
rm -rf "$STAGE_DIR"/*
|
||||
|
||||
echo
|
||||
echo "Build $VERSION uploaded successfully."
|
||||
echo "To deploy, run on the deploy server:"
|
||||
echo " ./deploy $CHANNEL $VERSION $PLATFORMS"
|
||||
|
|
@ -5,28 +5,17 @@ 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")
|
||||
profile="-p $ZOTERO_PROFILE"
|
||||
fi
|
||||
|
||||
FORCE_FULL=0
|
||||
NO_REBUILD=0
|
||||
REBUILD=0
|
||||
SKIP_BUNDLED_FILES=0
|
||||
DEBUGGER=0
|
||||
while getopts "rfnbd" opt; do
|
||||
while getopts "rbd" 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
|
||||
REBUILD=1
|
||||
;;
|
||||
|
||||
b)
|
||||
|
|
@ -38,6 +27,7 @@ while getopts "rfnbd" opt; do
|
|||
;;
|
||||
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
@ -46,15 +36,12 @@ done
|
|||
# Remove options from $@
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [ $NO_REBUILD -eq 0 ]; then
|
||||
if [ $REBUILD -eq 1 ]; 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
|
||||
|
|
@ -62,11 +49,16 @@ if [ $NO_REBUILD -eq 0 ]; then
|
|||
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
|
||||
NODE_OPTIONS=--openssl-legacy-provider npm run build
|
||||
echo
|
||||
fi
|
||||
|
||||
"$SCRIPT_DIR/dir_build" $PARAMS
|
||||
|
||||
"$SCRIPT_DIR/dir_build" -q $PARAMS
|
||||
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
# Sign the Word dylib so it works on Apple Silicon
|
||||
"$SCRIPT_DIR/codesign_local" "$APP_ROOT_DIR/staging/Zotero.app"
|
||||
fi
|
||||
fi
|
||||
|
||||
PARAMS=""
|
||||
|
|
@ -82,11 +74,7 @@ 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
|
||||
command="Zotero_linux-x86_64/zotero"
|
||||
elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
|
||||
command="Zotero_win-x64/zotero.exe"
|
||||
else
|
||||
|
|
@ -94,5 +82,4 @@ else
|
|||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
"$APP_ROOT_DIR/staging/$command" "${profile_args[@]}" -ZoteroDebugText $PARAMS "$@"
|
||||
"$APP_ROOT_DIR/staging/$command" $profile -ZoteroDebugText -purgecaches $PARAMS "$@"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -5,7 +5,7 @@ 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"
|
||||
. "$SCRIPT_DIR/bootstrap.sh"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ for lfs_file in $lfs_files; do
|
|||
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'"; }
|
||||
{ $FAIL_CMD_INLINE; FAILED=1; echo " -- run 'git lfs pull'"; }
|
||||
fi
|
||||
echo
|
||||
done
|
||||
|
|
@ -56,6 +56,9 @@ which python3 || { $FAIL_CMD; FAILED=1; }
|
|||
echo -n "Checking for curl: "
|
||||
which curl || { $FAIL_CMD; FAILED=1; }
|
||||
|
||||
echo -n "Checking for wget: "
|
||||
which wget || { $FAIL_CMD; FAILED=1; }
|
||||
|
||||
echo -n "Checking for zip: "
|
||||
which zip || { $FAIL_CMD; FAILED=1; }
|
||||
|
||||
|
|
@ -107,7 +110,7 @@ if [ $platform = "w" ]; then
|
|||
fi
|
||||
|
||||
plugin_path=$(cd "$NSIS_DIR\\Plugins\\x86-unicode" && pwd)
|
||||
plugins="AppAssocReg ApplicationID InvokeShellVerb nsProcess ShellLink UAC"
|
||||
plugins="AppAssocReg ApplicationID InvokeShellVerb ShellLink UAC"
|
||||
echo "Checking for NSIS plugins in $plugin_path"
|
||||
for i in $plugins; do
|
||||
echo -n " $i.dll: "
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -5,171 +5,84 @@ 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.
|
||||
|
||||
Usage: $0 -p platforms
|
||||
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
|
||||
-p PLATFORMS Platforms to build (m=Mac, w=Windows, l=Linux)
|
||||
-t add devtools
|
||||
-q quick build (skip compression and other optional steps for faster restarts during development)
|
||||
DONE
|
||||
exit 1
|
||||
}
|
||||
|
||||
platform=""
|
||||
arch=""
|
||||
devtools=0
|
||||
force_full=0
|
||||
while getopts "tp:a:f" opt; do
|
||||
DEVTOOLS=0
|
||||
PLATFORM=""
|
||||
quick_build=0
|
||||
while getopts "tp:q" opt; do
|
||||
case $opt in
|
||||
t)
|
||||
devtools=1
|
||||
DEVTOOLS=1
|
||||
;;
|
||||
p)
|
||||
for (( i=0; i<${#OPTARG}; i++ )); do
|
||||
for i in `seq 0 1 $((${#OPTARG}-1))`
|
||||
do
|
||||
case ${OPTARG:i:1} in
|
||||
m) platform="m";;
|
||||
w) platform="w";;
|
||||
l) platform="l";;
|
||||
m) PLATFORM="m";;
|
||||
w) PLATFORM="w";;
|
||||
l) PLATFORM="l";;
|
||||
*)
|
||||
echo "$0: Invalid platform option ${OPTARG:i:1}" >&2
|
||||
echo "$0: Invalid platform option ${OPTARG:i:1}"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
a)
|
||||
arch="$OPTARG"
|
||||
;;
|
||||
f)
|
||||
force_full=1
|
||||
q)
|
||||
quick_build=1
|
||||
;;
|
||||
\?)
|
||||
usage
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
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
|
||||
if [[ -z $PLATFORM ]]; then
|
||||
if [ "`uname`" = "Darwin" ]; then
|
||||
PLATFORM="m"
|
||||
elif [ "`uname`" = "Linux" ]; then
|
||||
PLATFORM="l"
|
||||
|
||||
# If platform not given explicitly, skip 32-bit build if 64-bit system
|
||||
if [ "$(arch)" = "x86_64" ]; then
|
||||
export SKIP_32=1
|
||||
fi
|
||||
elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
|
||||
PLATFORM="w"
|
||||
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
|
||||
PARAMS=""
|
||||
if [ $DEVTOOLS -eq 1 ]; then
|
||||
PARAMS+=" -t"
|
||||
fi
|
||||
if [ $quick_build -eq 1 ]; then
|
||||
PARAMS+=" -q"
|
||||
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`
|
||||
|
||||
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
|
||||
build_dir=`mktemp -d`
|
||||
function cleanup {
|
||||
rm -rf $build_dir
|
||||
}
|
||||
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
|
||||
"$SCRIPT_DIR/prepare_build" -s "$ROOT_DIR/build" -o "$build_dir" -c $CHANNEL -m $hash
|
||||
"$APP_ROOT_DIR/build.sh" -d "$build_dir" -p $PLATFORM -c $CHANNEL -s $PARAMS
|
||||
|
||||
echo Done
|
||||
|
|
|
|||
|
|
@ -22,21 +22,13 @@ set -euo pipefail
|
|||
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.
|
||||
function usage {
|
||||
cat >&2 <<DONE
|
||||
Usage: $0 -p platforms [-s]
|
||||
Options
|
||||
-p PLATFORMS Platforms to build (m=Mac, w=Windows, l=Linux)
|
||||
DONE
|
||||
exit 1
|
||||
}
|
||||
|
|
@ -44,20 +36,22 @@ DONE
|
|||
BUILD_MAC=0
|
||||
BUILD_WIN=0
|
||||
BUILD_LINUX=0
|
||||
arch=""
|
||||
while getopts 'p:a:' opt; do
|
||||
while getopts "p:s" opt; do
|
||||
case $opt in
|
||||
p)
|
||||
for (( i=0; i<${#OPTARG}; i++ )); do
|
||||
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 "Invalid platform: ${OPTARG:i:1}"; echo; usage;;
|
||||
*)
|
||||
echo "$0: Invalid platform option ${OPTARG:i:1}"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done;;
|
||||
a) arch="$OPTARG";;
|
||||
*) usage;;
|
||||
done
|
||||
;;
|
||||
esac
|
||||
shift $((OPTIND-1)); OPTIND=1
|
||||
done
|
||||
|
|
@ -67,30 +61,31 @@ if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; th
|
|||
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
|
||||
function replace_line {
|
||||
pattern=$1
|
||||
replacement=$2
|
||||
file=$3
|
||||
|
||||
if egrep -q "$pattern" "$file"; then
|
||||
perl -pi -e "s/$pattern/$replacement/" "$file"
|
||||
else
|
||||
echo "$pattern" not found in "$file" -- aborting 2>&1
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function remove_line {
|
||||
pattern=$1
|
||||
file=$2
|
||||
|
||||
if egrep -q "$pattern" "$file"; then
|
||||
egrep -v "$pattern" "$file" > "$file.tmp"
|
||||
mv "$file.tmp" "$file"
|
||||
else
|
||||
echo "$pattern" not found in "$file" -- aborting 2>&1
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function get_utf16_chars {
|
||||
str=$(echo -n "$1" | xxd -p | fold -w 2 | sed -r 's/(.+)/\\\\x{\1}\\\\x{00}/')
|
||||
|
|
@ -120,32 +115,16 @@ function modify_omni {
|
|||
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_SERVICES_HEALTHREPORT:' 'MOZ_SERVICES_HEALTHREPORT: false \&\&' modules/AppConstants.sys.mjs
|
||||
replace_line 'MOZ_TELEMETRY_REPORTING:' 'MOZ_TELEMETRY_REPORTING: false \&\&' modules/AppConstants.sys.mjs
|
||||
replace_line 'MOZ_TELEMETRY_ON_BY_DEFAULT:' 'MOZ_TELEMETRY_ON_BY_DEFAULT: false \&\&' modules/AppConstants.sys.mjs
|
||||
replace_line 'MOZ_CRASHREPORTER:' 'MOZ_CRASHREPORTER: false \&\&' modules/AppConstants.sys.mjs
|
||||
replace_line 'MOZ_UPDATE_CHANNEL:.+' 'MOZ_UPDATE_CHANNEL: "none",' modules/AppConstants.sys.mjs
|
||||
replace_line '"https:\/\/[^\/]+mozilla.com.+"' '""' modules/AppConstants.sys.mjs
|
||||
|
|
@ -169,20 +148,6 @@ function modify_omni {
|
|||
|
||||
# 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
|
||||
|
|
@ -199,21 +164,8 @@ function modify_omni {
|
|||
# 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
|
||||
remove_line 'DOMInputPasswordAdded: \{\},' modules/ActorManagerParent.sys.mjs
|
||||
|
||||
# On Mac/Linux, ignore relative paths in PATH in Subprocess.pathSearch(), used when a bare command is passed to Utilities.Internal.subprocess()
|
||||
if [[ $platform != win* ]]; then
|
||||
|
|
@ -224,7 +176,6 @@ function modify_omni {
|
|||
pushd modules
|
||||
rm Bookmarks*
|
||||
rm FxAccounts*
|
||||
echo '' > FingerprintingWebCompatService.sys.mjs
|
||||
echo '' > PartitioningExceptionListService.sys.mjs
|
||||
rm Places*
|
||||
echo '' > PlacesExpiration.sys.mjs
|
||||
|
|
@ -239,29 +190,6 @@ function modify_omni {
|
|||
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") {
|
||||
|
|
@ -281,35 +209,21 @@ function modify_omni {
|
|||
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 'let bss = manifest.applications\?.gecko' 'let bss = manifest.applications?.zotero' modules/addons/XPIInstall.jsm
|
||||
replace_line 'manifest.applications\?.gecko' 'manifest.applications?.zotero' modules/Extension.sys.mjs
|
||||
|
||||
# When installing addon, use app version instead of toolkit version for targetApplication
|
||||
replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/XPIInstall.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
|
||||
replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/XPIInstall.jsm
|
||||
|
||||
# Accept zotero@chnm.gmu.edu for target application to allow Zotero 6 plugins to remain
|
||||
# installed in Zotero 7
|
||||
replace_line "if \(targetApp.id == Services.appinfo.ID\) \{" "if (targetApp.id == 'zotero\@chnm.gmu.edu') targetApp.id = '$APP_ID'; if (targetApp.id == Services.appinfo.ID) {" modules/addons/XPIDatabase.sys.mjs
|
||||
replace_line "if \(targetApp.id == Services.appinfo.ID\) \{" "if (targetApp.id == 'zotero\@chnm.gmu.edu') targetApp.id = '$APP_ID'; if (targetApp.id == Services.appinfo.ID) {" modules/addons/XPIDatabase.jsm
|
||||
|
||||
# TEMP: Skip addons without ids until we figure out what's going on with the default theme.
|
||||
# This fixes plugin updating and other things.
|
||||
replace_line 'return addons;' 'return addons.filter(addon => addon.id);' modules/addons/XPIDatabase.sys.mjs
|
||||
replace_line 'return addons;' 'return addons.filter(addon => addon.id);' modules/addons/XPIDatabase.jsm
|
||||
|
||||
# For updates, look for applications.zotero instead of applications.gecko in manifest.json and
|
||||
# use the app id and version for strict_min_version/strict_max_version comparisons
|
||||
|
|
@ -320,22 +234,13 @@ function modify_omni {
|
|||
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
|
||||
replace_line 'this.maybeInstallBuiltinAddon\(' 'if (false) this.maybeInstallBuiltinAddon(' modules/addons/XPIProvider.jsm
|
||||
|
||||
# Don't log a warning when checking 'distribution/extensions' for addons
|
||||
replace_line 'logger.warn\(`Checking' 'logger.debug(`Checking' modules/addons/XPIProvider.sys.mjs
|
||||
replace_line 'logger.warn\(`Checking' 'logger.debug(`Checking' modules/addons/XPIProvider.jsm
|
||||
|
||||
# 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
|
||||
|
|
@ -344,7 +249,7 @@ function modify_omni {
|
|||
# [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 'if \(info.addon.userPermissions\) \{' 'if (false) {' modules/AddonManager.sys.mjs
|
||||
replace_line '\} else if \(info.addon.sitePermissions\) \{' '} else if (false) {' modules/AddonManager.sys.mjs
|
||||
replace_line '\} else if \(requireConfirm\) \{' '} else if (false) {' modules/AddonManager.sys.mjs
|
||||
|
||||
|
|
@ -358,35 +263,9 @@ function modify_omni {
|
|||
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
|
||||
replace_line 'AddonManagerPrivate.callAddonListeners' 'await AddonManagerPrivate.callAddonListeners' modules/addons/XPIInstall.jsm
|
||||
replace_line 'let uninstall = \(\) => \{' 'let uninstall = async () => {' modules/addons/XPIInstall.jsm
|
||||
replace_line 'cancelUninstallAddon\(aAddon\)' 'async cancelUninstallAddon(aAddon)' modules/addons/XPIInstall.jsm
|
||||
|
||||
# No idea why this is necessary, but without it initialization fails with "TypeError: "constructor" is read-only"
|
||||
replace_line 'LoginStore.prototype.constructor = LoginStore;' '\/\/LoginStore.prototype.constructor = LoginStore;' modules/LoginStore.sys.mjs
|
||||
|
|
@ -415,67 +294,48 @@ function modify_omni {
|
|||
|
||||
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
|
||||
# Hide search bar, Themes and Plugins tabs, and sidebar footer
|
||||
echo '.main-search, button[name="theme"], button[name="plugin"], sidebar-footer { display: none; }' >> $file
|
||||
echo '.main-heading { margin-top: 2em; }' >> $file
|
||||
# Hide Details/Permissions tabs in addon details so we only show details
|
||||
echo 'addon-details > button-group { display: none !important; }' >> $file
|
||||
# Hide "Run on sites with restrictions" row and helper message
|
||||
echo '.addon-detail-row-quarantined-domains { display: none; }' >> $file
|
||||
echo '.addon-detail-help-row:has([data-l10n-id="addon-detail-quarantined-domains-help"]) { display: none; }' >> $file
|
||||
# Hide "Report" button from plugin menu
|
||||
echo 'addon-card addon-options hr:first-of-type { display: none }' >> $file
|
||||
echo '[data-l10n-id="report-addon-button"] { display: none }' >> $file
|
||||
# Hide "Debug Addons" and "Manage Extension Shortcuts"
|
||||
echo 'panel-item[action="debug-addons"], panel-item[action="reset-update-states"] + hr, panel-item[action="manage-shortcuts"] { display: none }' >> $file
|
||||
# Show cursor feedback on plugin homepage links
|
||||
echo '.addon-detail-row-homepage .text-link { cursor: pointer; color: LinkText; }' >> $file
|
||||
echo '.addon-detail-row-homepage .text-link { cursor: pointer; color: -moz-nativehyperlinktext; }' >> $file
|
||||
echo '.addon-detail-row-homepage .text-link:hover { text-decoration: underline; }' >> $file
|
||||
|
||||
file="chrome/toolkit/content/mozapps/extensions/aboutaddons-utils.mjs"
|
||||
file="chrome/toolkit/content/mozapps/extensions/aboutaddons.js"
|
||||
# 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"
|
||||
replace_line 'if \(!isCorrectlySigned\(addon\)\) \{' 'if (!isCorrectlySigned(addon)) {return {};' $file
|
||||
# Hide Private Browsing setting in addon details
|
||||
replace_line 'pbRow\.' '\/\/pbRow.' $file
|
||||
replace_line 'let isAllowed = await isAllowedInPrivateBrowsing' '\/\/let isAllowed = await isAllowedInPrivateBrowsing' $file
|
||||
# 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 'let \{ BrowserAddonUI \} = windowRoot.ownerGlobal;' '' $file
|
||||
replace_line 'await BrowserAddonUI.promptRemoveExtension' 'promptRemoveExtension' $file
|
||||
|
||||
|
||||
# Customize empty-list message
|
||||
replace_line 'createEmptyListMessage\(\) {' 'createEmptyListMessage() {
|
||||
var p = document.createElement("p");
|
||||
p.id = "empty-list-message";
|
||||
return p;' 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"' \
|
||||
return p;' $file
|
||||
# Swap in include.js, which we need for Zotero.getString(), for abuse-reports.js, which we don't need
|
||||
|
||||
# Open plugin links in external browser
|
||||
replace_line 'let homepageURL = homepageRow.querySelector\(\"a\"\);' 'let homepageURL = homepageRow.querySelector(\"\.text-link\");' $file
|
||||
replace_line 'homepageURL.href = addon.homepageURL;' 'homepageURL.setAttribute("href", addon.homepageURL);' $file
|
||||
replace_line '<a target="_blank" dir="ltr"><\/a>' \
|
||||
'<label target="_blank" class="text-link" dir="ltr"><\/label>' \
|
||||
chrome/toolkit/content/mozapps/extensions/aboutaddons.html
|
||||
|
||||
|
||||
# TODO: Use our own strings, but for now fix the bundled English ones
|
||||
replace_line 'Manage Your Extensions' 'Manage Your Plugins' localization/en-US/toolkit/about/aboutAddons.ftl
|
||||
replace_line '= Extensions' '= Plugins' localization/en-US/toolkit/about/aboutAddons.ftl
|
||||
replace_line 'Add-on' 'Plugin' localization/en-US/toolkit/about/aboutAddons.ftl
|
||||
replace_line 'add-ons' 'plugins' localization/en-US/toolkit/about/aboutAddons.ftl
|
||||
replace_line 'Select add-on to install' 'Select plugin to install' localization/en-US/toolkit/about/aboutAddons.ftl
|
||||
|
||||
# Hide Recommendations tab in sidebar and recommendations in main pane
|
||||
replace_line 'function isDiscoverEnabled\(\) \{' 'function isDiscoverEnabled() {return false;' chrome/toolkit/content/mozapps/extensions/aboutaddonsCommon.js
|
||||
replace_line 'pref\("extensions.htmlaboutaddons.recommendations.enabled".+' 'pref("extensions.htmlaboutaddons.recommendations.enabled", false);' greprefs.js
|
||||
|
||||
# Hide Report option
|
||||
|
|
@ -490,23 +350,20 @@ function modify_omni {
|
|||
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"' \
|
||||
replace_line '<\?xml-stylesheet href=\"chrome:\/\/global\/skin\/commonDialog.css\" type=\"text\/css\"\?>' \
|
||||
'<\?xml-stylesheet href=\"chrome:\/\/global\/skin\/commonDialog.css\" type=\"text\/css\"\?>
|
||||
<\?xml-stylesheet href=\"chrome:\/\/zotero-platform\/content\/zotero.css\" type=\"text\/css\"\?>' \
|
||||
chrome/toolkit/content/global/commonDialog.xhtml
|
||||
# commonDialog.xhtml can be opened via Services.prompt in the Profile Manager, so check for a profile first
|
||||
replace_line 'function commonDialogOnLoad\(\) \{' \
|
||||
'function commonDialogOnLoad() {
|
||||
replace_line '<script>' \
|
||||
'<script>
|
||||
try {
|
||||
Services.dirsvc.get(\"ProfD\", Ci.nsIFile);
|
||||
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/include.js\", this);
|
||||
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/titlebar.js\", this);
|
||||
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/customElements.js\", this);
|
||||
} catch (e) {}' \
|
||||
chrome/toolkit/content/global/commonDialog.js
|
||||
chrome/toolkit/content/global/commonDialog.xhtml
|
||||
|
||||
# Use native checkbox instead of Firefox-themed version in prompt dialogs
|
||||
replace_line '<xul:checkbox' '<xul:checkbox native=\"true\"' chrome/toolkit/content/global/commonDialog.xhtml
|
||||
|
|
@ -520,42 +377,12 @@ function modify_omni {
|
|||
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
|
||||
popupset.appendChild(this._autoScrollPopup);' chrome/toolkit/content/global/elements/browser-custom-element.js
|
||||
|
||||
# Show Services menu on input fields
|
||||
# (JS part of https://hg.mozilla.org/mozilla-central/rev/defc298879844981e7c1817f13e3fc2caf84f148 for input boxes)
|
||||
replace_line '<menupopup class="textbox-contextmenu">' '<menupopup class="textbox-contextmenu" showservicesmenu="true">' chrome/toolkit/content/global/elements/moz-input-box.js
|
||||
|
||||
zip -qr9XD omni.ja *
|
||||
mv omni.ja ..
|
||||
cd ..
|
||||
|
|
@ -601,14 +428,12 @@ if [ $BUILD_MAC == 1 ]; 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"
|
||||
curl -o Firefox.dmg "$DOWNLOAD_URL/mac/en-US/Firefox%20$GECKO_VERSION.dmg"
|
||||
set +e
|
||||
hdiutil detach -quiet /Volumes/Firefox 2>/dev/null
|
||||
set -e
|
||||
hdiutil attach -quiet Firefox.dmg
|
||||
cp -a /Volumes/Firefox/Firefox.app .
|
||||
# Store local copy of unmodified app
|
||||
zip -r "Firefox $GECKO_VERSION.app.zip" Firefox.app
|
||||
hdiutil detach -quiet /Volumes/Firefox
|
||||
fi
|
||||
|
||||
|
|
@ -617,42 +442,27 @@ if [ $BUILD_MAC == 1 ]; then
|
|||
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"
|
||||
if [ -e "Firefox $GECKO_VERSION MacOS.zip" ]; then
|
||||
echo "Using Firefox $GECKO_VERSION MacOS.zip"
|
||||
unzip "Firefox $GECKO_VERSION MacOS.zip"
|
||||
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
|
||||
curl -o MacOS.tar.xz "${custom_components_url}mac/$custom_components_hash_mac.tar.xz"
|
||||
|
||||
# Verify hash
|
||||
if [[ "`uname`" = "Darwin" ]]; then
|
||||
shasum=shasum
|
||||
else
|
||||
shasum=sha256sum
|
||||
fi
|
||||
echo
|
||||
echo "$custom_components_hash_mac MacOS.tar.xz" | $shasum -c -
|
||||
echo
|
||||
|
||||
tar xvf MacOS.tar.xz
|
||||
fi
|
||||
echo
|
||||
tar xvf "$tarfile"
|
||||
echo
|
||||
fi
|
||||
|
||||
pushd Firefox.app/Contents/Resources
|
||||
|
|
@ -670,8 +480,13 @@ if [ $BUILD_MAC == 1 ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
rm -f Firefox.dmg
|
||||
if [ ! -e "Firefox $GECKO_VERSION.app.zip" ]; then
|
||||
rm "Firefox.dmg"
|
||||
fi
|
||||
|
||||
#if [ ! -e "Firefox $GECKO_VERSION MacOS.zip" ]; then
|
||||
# rm "MacOS.zip"
|
||||
#fi
|
||||
echo $("$SCRIPT_DIR/xulrunner_hash" -p m) > hash-mac
|
||||
fi
|
||||
|
||||
|
|
@ -679,32 +494,23 @@ 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
|
||||
for arch in win32 win-x64 win-arm64; 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"
|
||||
if [ -e "Firefox Setup $GECKO_VERSION-$arch.exe" ]; then
|
||||
echo "Using Firefox Setup $GECKO_VERSION-$arch.exe"
|
||||
cp "Firefox Setup $GECKO_VERSION-$arch.exe" "Firefox%20Setup%20$GECKO_VERSION.exe"
|
||||
else
|
||||
if [ $arch = "win-x64" ]; then
|
||||
moz_arch=win64
|
||||
curl -O "$DOWNLOAD_URL/win64/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
|
||||
elif [ $arch = "win-arm64" ]; then
|
||||
moz_arch=win64-aarch64
|
||||
curl -O "$DOWNLOAD_URL/win64-aarch64/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
|
||||
else
|
||||
moz_arch=$arch
|
||||
curl -O "$DOWNLOAD_URL/$arch/en-US/Firefox%20Setup%20$GECKO_VERSION.exe"
|
||||
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/*'
|
||||
|
|
@ -712,88 +518,29 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
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
|
||||
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
|
||||
# (The macOS strings command doesn't have an encoding option, so skip on macOS. We could
|
||||
# require binutils, which has GNU strings, but you need to build on Windows to make an
|
||||
# installer, so a Windows build from macOS likely isn't being deployed, and there's no
|
||||
# reason the Perl command above should fail anyway.)
|
||||
if [[ "`uname`" != "Darwin" ]] && [[ -z "$(strings -e l xul.dll | grep -m 1 Zotero)" ]]; then
|
||||
echo '"Zotero" not found in xul.dll after replacement'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
modify_omni $arch
|
||||
# Disable skeleton UI window
|
||||
|
|
@ -806,49 +553,41 @@ if [ $BUILD_WIN == 1 ]; then
|
|||
rm "Firefox%20Setup%20$GECKO_VERSION.exe"
|
||||
echo
|
||||
echo
|
||||
|
||||
echo "$( "$SCRIPT_DIR/xulrunner_hash" -p w -a "$arch" )" > "hash-$arch"
|
||||
done
|
||||
echo $("$SCRIPT_DIR/xulrunner_hash" -p w) > hash-win
|
||||
fi
|
||||
|
||||
if [ $BUILD_LINUX == 1 ]; then
|
||||
GECKO_VERSION="$GECKO_VERSION_LINUX"
|
||||
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
|
||||
|
||||
if [[ -n $arch ]]; then
|
||||
arch_list=("$arch")
|
||||
|
||||
|
||||
# Include 32-bit build if not in CI
|
||||
if [[ "${CI:-}" = "1" ]] || [[ "${SKIP_32:-}" = "1" ]]; then
|
||||
arches="x86_64"
|
||||
else
|
||||
arch_list=(x86_64 arm64)
|
||||
arches="i686 x86_64"
|
||||
fi
|
||||
for arch in "${arch_list[@]}"; do
|
||||
xdir="firefox-linux-$arch"
|
||||
for arch in $arches; do
|
||||
xdir="firefox-$arch"
|
||||
rm -rf $xdir
|
||||
|
||||
archived_file="firefox-$GECKO_VERSION-$arch.tar.xz"
|
||||
archived_file="firefox-$GECKO_VERSION-$arch.tar.bz2"
|
||||
if [ -e "$archived_file" ]; then
|
||||
echo "Using $archived_file"
|
||||
cp "$archived_file" "firefox-$GECKO_VERSION.tar.xz"
|
||||
cp "$archived_file" "firefox-$GECKO_VERSION.tar.bz2"
|
||||
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"
|
||||
curl -O "$DOWNLOAD_URL/linux-$arch/en-US/firefox-$GECKO_VERSION.tar.bz2"
|
||||
fi
|
||||
|
||||
tar xvf firefox-$GECKO_VERSION.tar.xz
|
||||
mv firefox $xdir
|
||||
tar xvf firefox-$GECKO_VERSION.tar.bz2
|
||||
mv firefox firefox-$arch
|
||||
|
||||
pushd $xdir
|
||||
pushd firefox-$arch
|
||||
modify_omni $arch
|
||||
popd
|
||||
|
||||
rm "firefox-$GECKO_VERSION.tar.xz"
|
||||
|
||||
echo "$( $SCRIPT_DIR/xulrunner_hash -p l -a "$arch" )" > "hash-linux-${arch}"
|
||||
echo $($SCRIPT_DIR/xulrunner_hash -p l) > hash-linux
|
||||
rm "firefox-$GECKO_VERSION.tar.bz2"
|
||||
done
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ 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 \
|
||||
wget -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -64,19 +64,22 @@ if [[ "$VERSION" ]] && [[ "$NUM_VERSIONS" ]]; then
|
|||
usage
|
||||
fi
|
||||
|
||||
INCR_FILENAME="incrementals-$PLATFORM"
|
||||
REMOTE_PATH="$DEPLOY_PATH/manifests/$CHANNEL/$INCR_FILENAME"
|
||||
INCR_FILENAME="incrementals-$CHANNEL-$PLATFORM"
|
||||
S3_URL="s3://$S3_BUCKET/$S3_DIST_PATH/$CHANNEL/incrementals-$PLATFORM"
|
||||
INCR_PATH="$DIST_DIR/$INCR_FILENAME"
|
||||
|
||||
if [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
|
||||
INCR_PATH=$(cygpath -w "$INCR_PATH")
|
||||
fi
|
||||
|
||||
mkdir -p "$DIST_DIR"
|
||||
scp $DEPLOY_HOST:"$REMOTE_PATH" "$INCR_PATH" >&2
|
||||
aws s3 cp $S3_URL "$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"
|
||||
aws s3 cp "$INCR_PATH" $S3_URL
|
||||
# Show last n versions
|
||||
elif [ "$NUM_VERSIONS" -gt 0 ]; then
|
||||
# TEMP: Don't include 6.0 versions
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ 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"
|
||||
|
|
@ -16,7 +15,5 @@ if [[ -z "$id" ]]; then
|
|||
usage
|
||||
fi
|
||||
|
||||
prepare_notary_auth
|
||||
|
||||
xcrun notarytool log "$id" "${notary_auth[@]}" notary_log.json
|
||||
xcrun notarytool log "$id" --apple-id "$NOTARIZATION_USER" --team-id "$NOTARIZATION_TEAM_ID" --password "$NOTARIZATION_PASSWORD" notary_log.json
|
||||
cat notary_log.json
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ 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"
|
||||
|
|
@ -16,7 +15,5 @@ if [[ -z "$file" ]]; then
|
|||
usage
|
||||
fi
|
||||
|
||||
prepare_notary_auth
|
||||
|
||||
echo "Uploading ${file##*/} to Apple for notarization" >&2
|
||||
xcrun notarytool submit $file "${notary_auth[@]}" --wait
|
||||
xcrun notarytool submit $file --apple-id "$NOTARIZATION_USER" --team-id "$NOTARIZATION_TEAM_ID" --password="$NOTARIZATION_PASSWORD" --wait
|
||||
|
|
|
|||
|
|
@ -71,42 +71,34 @@ def main():
|
|||
|
||||
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
|
||||
])
|
||||
# Export a clean copy of the source tree
|
||||
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
|
||||
# Make sure rsync 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)
|
||||
subprocess.check_call([
|
||||
'find',
|
||||
os.path.normpath(tmp_src_dir + '/chrome/content/zotero/locale/csl/'),
|
||||
'-mindepth', '1',
|
||||
'!', '-name', '*.xml',
|
||||
'!', '-name', 'locales.json',
|
||||
#'-print',
|
||||
'-delete'
|
||||
])
|
||||
|
||||
# Delete styles build script
|
||||
os.remove(os.path.join(tmp_src_dir, 'styles', 'update'))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,15 +1,4 @@
|
|||
#!/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 )"
|
||||
|
|
@ -17,79 +6,39 @@ 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
|
||||
platform=""
|
||||
while getopts ":p:" 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;;
|
||||
p)
|
||||
platform="$OPTARG"
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
:)
|
||||
echo "Option -$OPTARG requires an argument." >&2
|
||||
exit 1
|
||||
;;
|
||||
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"
|
||||
if [ $platform == "m" ]; then
|
||||
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
|
||||
elif [ $platform == "w" ]; then
|
||||
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
|
||||
elif [ $platform == "l" ]; then
|
||||
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:-}
|
||||
echo "Platform parameter incorrect. Usage: -p m(mac)/w(windows)/l(linux)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
input_string="${platform}-${arch}-${components_hash}|${GECKO_VERSION}|${xulrunner_content}"
|
||||
xulrunner_content=$(< "$SCRIPT_DIR/fetch_xulrunner")
|
||||
xulrunner_content+=$(< "$APP_ROOT_DIR/assets/multilocale.txt")
|
||||
xulrunner_gecko_hash=$(echo -n "$GECKO_VERSION - '$xulrunner_content'" | openssl dgst -sha256)
|
||||
|
||||
echo "$xulrunner_gecko_hash"
|
||||
|
||||
|
||||
printf "%s" "$input_string" | openssl dgst -sha256 | awk '{print $NF}'
|
||||
|
|
|
|||
|
|
@ -97,8 +97,6 @@ if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; th
|
|||
usage
|
||||
fi
|
||||
|
||||
TO_MAJOR_VERSION="${TO%%.*}"
|
||||
|
||||
rm -rf "$UPDATE_STAGE_DIR"
|
||||
mkdir "$UPDATE_STAGE_DIR"
|
||||
|
||||
|
|
@ -138,43 +136,19 @@ for version in "$FROM" "$TO"; do
|
|||
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}"
|
||||
LINUX_X86_ARCHIVE="Zotero-${version}_linux-i686.tar.bz2"
|
||||
LINUX_X86_64_ARCHIVE="Zotero-${version}_linux-x86_64.tar.bz2"
|
||||
|
||||
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
|
||||
for archive in "$MAC_ARCHIVE" "$WIN32_ARCHIVE" "$WIN_X64_ARCHIVE" "$WIN_ARM64_ARCHIVE" "$LINUX_X86_ARCHIVE" "$LINUX_X86_64_ARCHIVE"; do
|
||||
if [[ $archive = "$MAC_ARCHIVE" ]] && [[ $BUILD_MAC != 1 ]]; then
|
||||
continue
|
||||
fi
|
||||
|
|
@ -193,9 +167,6 @@ for version in "$FROM" "$TO"; do
|
|||
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"
|
||||
|
||||
|
|
@ -215,12 +186,10 @@ for version in "$FROM" "$TO"; do
|
|||
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
|
||||
NEW_ETAG=$(wget -nv -S --header "If-None-Match: $ETAG" $URL 2>&1 | awk '/ *ETag: */ {print $2}')
|
||||
|
||||
# If ETag didn't match, cache newly downloaded version
|
||||
if [ "$HTTP_CODE" = 200 ]; then
|
||||
if [ -f $archive ]; then
|
||||
echo "ETag for $archive didn't match! -- using new version"
|
||||
rm -f "$CACHE_DIR/$archive.etag"
|
||||
cp $archive "$CACHE_DIR/"
|
||||
|
|
@ -231,9 +200,7 @@ for version in "$FROM" "$TO"; do
|
|||
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
|
||||
NEW_ETAG=$(wget -nv -S $URL 2>&1 | awk '/ *ETag: */ {print $2}')
|
||||
|
||||
# Save archive to cache
|
||||
rm -f "$CACHE_DIR/$archive.etag"
|
||||
|
|
@ -243,13 +210,8 @@ for version in "$FROM" "$TO"; do
|
|||
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
|
||||
# Delete cached files older than 14 days
|
||||
find "$CACHE_DIR" -ctime +14 -delete
|
||||
|
||||
# Unpack Zotero.app
|
||||
if [ $BUILD_MAC == 1 ]; then
|
||||
|
|
@ -270,14 +232,9 @@ for version in "$FROM" "$TO"; do
|
|||
# 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
|
||||
unzip -q "$build"
|
||||
rm "$build"
|
||||
done
|
||||
INCREMENTALS_FOUND=1
|
||||
else
|
||||
|
|
@ -287,36 +244,14 @@ for version in "$FROM" "$TO"; do
|
|||
|
||||
# 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
|
||||
if [[ -f "$LINUX_X86_ARCHIVE" ]] && [[ -f "$LINUX_X86_64_ARCHIVE" ]]; then
|
||||
for build in "$LINUX_X86_ARCHIVE" "$LINUX_X86_64_ARCHIVE"; do
|
||||
tar -xjf "$build"
|
||||
rm "$build"
|
||||
done
|
||||
INCREMENTALS_FOUND=1
|
||||
else
|
||||
echo "Linux tarballs not found: ${LINUX_BUILDS_TO_UNPACK[*]}"
|
||||
echo "$LINUX_X86_ARCHIVE/$LINUX_X86_64_ARCHIVE not found"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -327,21 +262,8 @@ done
|
|||
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
|
||||
for build in "mac" "win32" "win-x64" "win-arm64" "linux-i686" "linux-x86_64"; do
|
||||
if [[ $build == "mac" ]]; then
|
||||
if [[ $BUILD_MAC == 0 ]]; then
|
||||
continue
|
||||
|
|
@ -351,7 +273,7 @@ for build in "mac" "win32" "win-x64" "win-arm64" "linux-i686" "linux-x86_64" "li
|
|||
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
|
||||
if ([[ $build == "linux-i686" ]] || [[ $build == "linux-x86_64" ]]) && [[ $BUILD_LINUX == 0 ]]; then
|
||||
continue
|
||||
fi
|
||||
dir="Zotero_$build"
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@
|
|||
#
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Preserve a value set by the parent so it survives in the child processes
|
||||
# spawned by run_parallel_tasks()
|
||||
QUIET=${QUIET:-0}
|
||||
QUIET=0
|
||||
|
||||
# By default just assume that these tools exist on our path
|
||||
MAR=${MAR:-mar}
|
||||
|
|
@ -35,11 +33,7 @@ if [ $? -ne 0 ]; then
|
|||
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.
|
||||
# Ensure that we're always using the right compression settings
|
||||
export XZ_OPT="-T1 -7e"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
|
@ -99,14 +93,16 @@ make_add_instruction() {
|
|||
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"
|
||||
# XXX unconditional add-if for channel-prefs.js to fix mac signature on old installs (bug 1804303)
|
||||
if [ "$add_if_not_file_chk" = "Contents/Resources/defaults/pref/channel-prefs.js" ]; then
|
||||
return 1
|
||||
fi
|
||||
if [ `basename $add_if_not_file_chk` = "channel-prefs.js" -o \
|
||||
`basename $add_if_not_file_chk` = "update-settings.ini" ]; then
|
||||
## "true" *giggle*
|
||||
return 0;
|
||||
fi
|
||||
## "false"
|
||||
## 'false'... because this is bash. Oh yay!
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -162,10 +158,10 @@ append_remove_instructions() {
|
|||
if [ -n "$f" ]; then
|
||||
# Exclude comments
|
||||
if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then
|
||||
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
|
||||
elif [ $(echo "$f" | grep -c '\/\*$') = 1 ]; then
|
||||
# Remove the *
|
||||
f=$(echo "$f" | sed -e 's:\*$::')
|
||||
verbose_notice " rmrfdir \"$f\""
|
||||
|
|
@ -181,236 +177,6 @@ append_remove_instructions() {
|
|||
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() {
|
||||
|
|
|
|||
|
|
@ -13,12 +13,6 @@ 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() {
|
||||
|
|
@ -56,7 +50,7 @@ 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
|
||||
if [ $(echo "$targetdir" | grep -c '\/$') = 1 ]; then
|
||||
# Remove the /
|
||||
targetdir=$(echo "$targetdir" | sed -e 's:\/$::')
|
||||
fi
|
||||
|
|
@ -94,23 +88,6 @@ 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]}"
|
||||
|
||||
|
|
@ -120,6 +97,11 @@ for ((i=0; $i<$num_files; i=$i+1)); do
|
|||
make_add_instruction "$f" "$updatemanifestv3"
|
||||
fi
|
||||
|
||||
dir=$(dirname "$f")
|
||||
mkdir -p "$workdir/$dir"
|
||||
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$targetdir/$f" > "$workdir/$f"
|
||||
copy_perm "$targetdir/$f" "$workdir/$f"
|
||||
|
||||
targetfiles="$targetfiles \"$f\""
|
||||
done
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,6 @@ 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() {
|
||||
|
|
@ -87,6 +81,7 @@ if [ $# = 0 ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# channel-prefs.js removed for Zotero
|
||||
requested_forced_updates='Contents/MacOS/firefox'
|
||||
|
||||
while getopts "hqf:" flag
|
||||
|
|
@ -118,7 +113,7 @@ 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
|
||||
if [ $(echo "$newdir" | grep -c '\/$') = 1 ]; then
|
||||
# Remove the /
|
||||
newdir=$(echo "$newdir" | sed -e 's:\/$::')
|
||||
fi
|
||||
|
|
@ -170,67 +165,93 @@ 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
|
||||
# The full workdir may not exist yet, so create it if necessary.
|
||||
mkdir -p `dirname "$workdir/$f"`
|
||||
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$newdir/$f" > "$workdir/$f"
|
||||
copy_perm "$newdir/$f" "$workdir/$f"
|
||||
make_add_if_not_instruction "$f" "$updatemanifestv3"
|
||||
archivefiles="$archivefiles \"$f\""
|
||||
elif check_for_forced_update "$requested_forced_updates" "$f"; then
|
||||
continue 1
|
||||
fi
|
||||
|
||||
if check_for_forced_update "$requested_forced_updates" "$f"; then
|
||||
# The full workdir may not exist yet, so create it if necessary.
|
||||
mkdir -p `dirname "$workdir/$f"`
|
||||
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$newdir/$f" > "$workdir/$f"
|
||||
copy_perm "$newdir/$f" "$workdir/$f"
|
||||
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\""
|
||||
continue 1
|
||||
fi
|
||||
|
||||
if ! diff "$olddir/$f" "$newdir/$f" > /dev/null; then
|
||||
# Compute both the compressed binary diff and the compressed file, and
|
||||
# compare the sizes. Then choose the smaller of the two to package.
|
||||
dir=$(dirname "$workdir/$f")
|
||||
mkdir -p "$dir"
|
||||
verbose_notice "diffing \"$f\""
|
||||
# MBSDIFF_HOOK represents the communication interface with funsize and,
|
||||
# if enabled, caches the intermediate patches for future use and
|
||||
# compute avoidance
|
||||
#
|
||||
# An example of MBSDIFF_HOOK env variable could look like this:
|
||||
# export MBSDIFF_HOOK="myscript.sh -A https://funsize/api -c /home/user"
|
||||
# where myscript.sh has the following usage:
|
||||
# myscript.sh -A SERVER-URL [-c LOCAL-CACHE-DIR-PATH] [-g] [-u] \
|
||||
# PATH-FROM-URL PATH-TO-URL PATH-PATCH SERVER-URL
|
||||
#
|
||||
# Note: patches are bzipped or xz stashed in funsize to gain more speed
|
||||
|
||||
# if service is not enabled then default to old behavior
|
||||
# Disabled for Zotero
|
||||
#if [ -z "$MBSDIFF_HOOK" ]; then
|
||||
if true; then
|
||||
# mbsdiff doesn't like POSIX paths on Windows
|
||||
if [ $WIN_NATIVE -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"
|
||||
else
|
||||
# if service enabled then check patch existence for retrieval
|
||||
if $MBSDIFF_HOOK -g "$olddir/$f" "$newdir/$f" "$workdir/$f.patch.xz"; then
|
||||
verbose_notice "file \"$f\" found in funsize, diffing skipped"
|
||||
else
|
||||
# if not found already - compute it and cache it for future use
|
||||
$MBSDIFF "$olddir/$f" "$newdir/$f" "$workdir/$f.patch"
|
||||
$XZ $XZ_OPT --compress --lzma2 --format=xz --check=crc64 --force "$workdir/$f.patch"
|
||||
$MBSDIFF_HOOK -u "$olddir/$f" "$newdir/$f" "$workdir/$f.patch.xz"
|
||||
fi
|
||||
fi
|
||||
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$newdir/$f" > "$workdir/$f"
|
||||
copy_perm "$newdir/$f" "$workdir/$f"
|
||||
patchfile="$workdir/$f.patch.xz"
|
||||
patchsize=$(get_file_size "$patchfile")
|
||||
fullsize=$(get_file_size "$workdir/$f")
|
||||
|
||||
if [ $patchsize -lt $fullsize ]; then
|
||||
make_patch_instruction "$f" "$updatemanifestv3"
|
||||
mv -f "$patchfile" "$workdir/$f.patch"
|
||||
rm -f "$workdir/$f"
|
||||
archivefiles="$archivefiles \"$f.patch\""
|
||||
else
|
||||
make_add_instruction "$f" "$updatemanifestv3"
|
||||
rm -f "$patchfile"
|
||||
archivefiles="$archivefiles \"$f\""
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# remove instructions are added after add / patch instructions for
|
||||
|
|
@ -245,18 +266,33 @@ done
|
|||
# Newly added files
|
||||
notice ""
|
||||
notice "Adding file add instructions to update manifests"
|
||||
num_newfiles=${#newfiles[*]}
|
||||
|
||||
for ((i=0; $i<$num_newfiles; i=$i+1)); do
|
||||
f="${newfiles[$i]}"
|
||||
|
||||
# If we've already tested this file, then skip it
|
||||
for ((j=0; $j<$num_oldfiles; j=$j+1)); do
|
||||
if [ "$f" = "${oldfiles[j]}" ]; then
|
||||
continue 2
|
||||
fi
|
||||
done
|
||||
|
||||
dir=$(dirname "$workdir/$f")
|
||||
mkdir -p "$dir"
|
||||
|
||||
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$newdir/$f" > "$workdir/$f"
|
||||
copy_perm "$newdir/$f" "$workdir/$f"
|
||||
|
||||
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"
|
||||
archivefiles="$archivefiles \"$f\""
|
||||
done
|
||||
|
||||
notice ""
|
||||
notice "Adding file remove instructions to update manifests"
|
||||
|
|
|
|||
24
app/update-packaging/removed-files_linux-i686
Normal file
24
app/update-packaging/removed-files_linux-i686
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
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
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Contents/Frameworks/XUL.framework/
|
||||
Contents/Frameworks/*
|
||||
Contents/MacOS/Info.plist
|
||||
Contents/MacOS/LICENSE
|
||||
Contents/MacOS/README.xulrunner
|
||||
|
|
@ -38,6 +38,12 @@ Contents/MacOS/updater.app/Contents/MacOS/updater-bin
|
|||
Contents/MacOS/updates.xml
|
||||
Contents/MacOS/updates/*
|
||||
Contents/MacOS/zotero-bin
|
||||
Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/inject/translate_inject.js
|
||||
Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/node_modules.js
|
||||
Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/test/*
|
||||
Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/translate/cachedTypes.js
|
||||
Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/translate/resource/README.md
|
||||
Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/translate/resource/zoteroTypeSchemaData.js
|
||||
Contents/Resources/CONTRIBUTING.md
|
||||
Contents/Resources/COPYING
|
||||
Contents/Resources/README.md
|
||||
|
|
|
|||
|
|
@ -61,6 +61,6 @@ 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"
|
||||
$mar -V $version -H $channel -c "$outfile" `find . -type f | sed 's/^\.\///'`
|
||||
echo
|
||||
$mar -T "$outfile"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
set -e
|
||||
|
||||
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
|
||||
echo "Usage: $0 /path/to/mozilla-unified x86_64|i686" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
@ -20,40 +20,20 @@ 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
|
||||
if [[ $arch = "x86_64" ]] || [[ $arch == "i686" ]]; then
|
||||
rustup target add $arch-pc-windows-msvc
|
||||
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
|
||||
if [ $arch = "i686" ]; then
|
||||
export Z_ARCH=win32
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
|||
#mkdir -p nsis
|
||||
#cd nsis
|
||||
#for plugin in $plugins; do
|
||||
# curl -fsSLO $plugin
|
||||
# wget $plugin
|
||||
#done
|
||||
#
|
||||
#unzip_cmd="unzip -d Plugins -j"
|
||||
|
|
@ -46,12 +46,6 @@ 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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Binary file not shown.
|
|
@ -59,7 +59,6 @@ Var InstallType
|
|||
!insertmacro WordFind
|
||||
!insertmacro WordReplace
|
||||
|
||||
!include nsProcess.nsh
|
||||
; The following includes are custom.
|
||||
!include branding.nsi
|
||||
!include defines.nsi
|
||||
|
|
@ -174,12 +173,10 @@ Function UninstallOld
|
|||
Push $R1
|
||||
Push $R2
|
||||
StrCpy $0 0
|
||||
StrCpy $5 "HKLM"
|
||||
SetShellVarContext all
|
||||
|
||||
enum_uninst_keys:
|
||||
EnumRegKey $1 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall" $0
|
||||
StrCmp $1 "" switch_root_key_or_continue
|
||||
EnumRegKey $1 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall" $0
|
||||
StrCmp $1 "" continue_installation
|
||||
; $R1 is "Zotero", registry key name contains version and locale e.g. "Zotero 6.0.0 (x86 en-US)" so:
|
||||
StrLen $3 $R1 ; $3 = length of $R1, e.g. 6 for "Zotero"
|
||||
StrCpy $2 $1 $3 ; $2 = first $3 characters of $1, i.e. name without version and locale, e.g. "Zotero"
|
||||
|
|
@ -187,18 +184,9 @@ Function UninstallOld
|
|||
IntOp $0 $0 + 1
|
||||
Goto enum_uninst_keys ; loop through all keys
|
||||
|
||||
switch_root_key_or_continue:
|
||||
# Previous installation was not found in HKLM, switch to HKCU (using SetShellVarContext) and search again
|
||||
# $5 is used to ensure this only happens once
|
||||
StrCmp $5 "HKLM" +1 continue_installation
|
||||
StrCpy $5 "HKCU"
|
||||
StrCpy $0 0
|
||||
SetShellVarContext current
|
||||
Goto enum_uninst_keys
|
||||
|
||||
get_uninst_exe:
|
||||
ReadRegStr $2 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\$1" "UninstallString"
|
||||
ReadRegStr $3 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\$1" "InstallLocation"
|
||||
ReadRegStr $2 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$1" "UninstallString"
|
||||
ReadRegStr $3 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$1" "InstallLocation"
|
||||
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
|
||||
$R2 \
|
||||
|
|
@ -218,30 +206,28 @@ Function UninstallOld
|
|||
${GetFileName} $3 $4
|
||||
StrCpy $5 $4 6
|
||||
StrCmp $5 "Zotero" +1 continue_installation
|
||||
RMDir /r "$3\chrome"
|
||||
RMDir /r "$3\components"
|
||||
RMDir /r "$3\defaults"
|
||||
RMDir /r "$3\dictionaries"
|
||||
RMDir /r "$3\extensions"
|
||||
RMDir /r "$3\fonts"
|
||||
RMDir /r "$3\gmp-clearkey"
|
||||
RMDir /r "$3\uninstall"
|
||||
RMDir /r "$3\xulrunner"
|
||||
Delete "$3\*.chk"
|
||||
Delete "$3\*.dll"
|
||||
Delete "$3\*.exe"
|
||||
Delete "$3\Accessible.tlb"
|
||||
Delete "$3\dependentlibs.list"
|
||||
Delete "$3\firefox.VisualElementsManifest.xml"
|
||||
Delete "$3\omni.ja"
|
||||
Delete "$3\platform.ini"
|
||||
Delete "$3\precomplete"
|
||||
Delete "$3\voucher.bin"
|
||||
RMDir $3
|
||||
|
||||
RMDir /r /REBOOTOK "$3\chrome"
|
||||
RMDir /r /REBOOTOK "$3\components"
|
||||
RMDir /r /REBOOTOK "$3\defaults"
|
||||
RMDir /r /REBOOTOK "$3\dictionaries"
|
||||
RMDir /r /REBOOTOK "$3\extensions"
|
||||
RMDir /r /REBOOTOK "$3\fonts"
|
||||
RMDir /r /REBOOTOK "$3\gmp-clearkey"
|
||||
RMDir /r /REBOOTOK "$3\uninstall"
|
||||
RMDir /r /REBOOTOK "$3\xulrunner"
|
||||
Delete /REBOOTOK "$3\*.chk"
|
||||
Delete /REBOOTOK "$3\*.dll"
|
||||
Delete /REBOOTOK "$3\*.exe"
|
||||
Delete /REBOOTOK "$3\Accessible.tlb"
|
||||
Delete /REBOOTOK "$3\dependentlibs.list"
|
||||
Delete /REBOOTOK "$3\firefox.VisualElementsManifest.xml"
|
||||
Delete /REBOOTOK "$3\omni.ja"
|
||||
Delete /REBOOTOK "$3\platform.ini"
|
||||
Delete /REBOOTOK "$3\precomplete"
|
||||
Delete /REBOOTOK "$3\voucher.bin"
|
||||
RMDir /REBOOTOK $3
|
||||
continue_installation:
|
||||
; End uninstallation
|
||||
SetShellVarContext current
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
FunctionEnd
|
||||
|
|
@ -618,33 +604,29 @@ Function AddQuickLaunchShortcut
|
|||
FunctionEnd
|
||||
|
||||
Function CheckExistingInstall
|
||||
UserInfo::GetAccountType
|
||||
pop $0 ; $0 = "Admin" or "User" or "Guest". This is the account type.
|
||||
|
||||
; If there is a pending file copy from a previous upgrade don't allow
|
||||
; installing until after the system has rebooted.
|
||||
${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
|
||||
StrCmp $0 "Admin" +1 +4
|
||||
MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2
|
||||
Reboot
|
||||
Quit
|
||||
; Non-admins cannot depend on files being removed on restart.
|
||||
; Our only option is to remove these files and continue with the installation.
|
||||
Delete "$INSTDIR\${FileMainEXE}.moz-upgrade"
|
||||
Delete "$INSTDIR\${FileMainEXE}"
|
||||
${EndIf}
|
||||
IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +5
|
||||
MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +3 IDCANCEL +2
|
||||
Reboot
|
||||
Quit
|
||||
RMDir /r $INSTDIR
|
||||
|
||||
IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +3
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to remove the previous installation. Please delete $INSTDIR and try again."
|
||||
Quit
|
||||
|
||||
; If there is a pending file deletion from a previous uninstall don't allow
|
||||
; installing until after the system has rebooted.
|
||||
${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-delete"
|
||||
StrCmp $0 "Admin" +1 +4
|
||||
MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UNINSTALL)" IDNO +2
|
||||
Reboot
|
||||
Quit
|
||||
; Non-admins cannot depend on files being removed on restart.
|
||||
Delete "$INSTDIR\${FileMainEXE}.moz-delete"
|
||||
Delete "$INSTDIR\${FileMainEXE}"
|
||||
${EndIf}
|
||||
IfFileExists "$INSTDIR\${FileMainEXE}.moz-delete" +1 +5
|
||||
MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UNINSTALL)" IDNO +3 IDCANCEL +2
|
||||
Reboot
|
||||
Quit
|
||||
RMDir /r $INSTDIR
|
||||
|
||||
IfFileExists "$INSTDIR\${FileMainEXE}.moz-delete" +1 +3
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to remove the previous installation. Please delete $INSTDIR and try again."
|
||||
Quit
|
||||
|
||||
${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
|
||||
; Disable the next, cancel, and back buttons
|
||||
|
|
@ -966,21 +948,6 @@ Function .onInit
|
|||
${EndIf}
|
||||
!endif
|
||||
|
||||
# Check if Zotero is running. If it is, prompt the user to close Zotero and exit.
|
||||
# For silent installs, kill Zotero and if successful, continue with the install.
|
||||
${nsProcess::FindProcess} ${FileMainEXE} $R0
|
||||
${If} $R0 = 0
|
||||
IfSilent +3
|
||||
MessageBox MB_OK|MB_ICONSTOP "Zotero is already running. Please close Zotero before running the installer."
|
||||
Quit
|
||||
${nsProcess::KillProcess} ${FileMainEXE} $R0
|
||||
Sleep 500
|
||||
${nsProcess::FindProcess} ${FileMainEXE} $R0
|
||||
${If} $R0 = 0
|
||||
Quit
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
StrCpy $R1 "Zotero Standalone"
|
||||
StrCpy $R2 "An older version of Zotero is installed. If you continue, the existing version will be removed.$\n$\nYour Zotero data will not be affected."
|
||||
Call UninstallOld
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
!define nsProcess::FindProcess `!insertmacro nsProcess::FindProcess`
|
||||
|
||||
!macro nsProcess::FindProcess _FILE _ERR
|
||||
nsProcess::_FindProcess /NOUNLOAD `${_FILE}`
|
||||
Pop ${_ERR}
|
||||
!macroend
|
||||
|
||||
|
||||
!define nsProcess::KillProcess `!insertmacro nsProcess::KillProcess`
|
||||
|
||||
!macro nsProcess::KillProcess _FILE _ERR
|
||||
nsProcess::_KillProcess /NOUNLOAD `${_FILE}`
|
||||
Pop ${_ERR}
|
||||
!macroend
|
||||
|
||||
!define nsProcess::CloseProcess `!insertmacro nsProcess::CloseProcess`
|
||||
|
||||
!macro nsProcess::CloseProcess _FILE _ERR
|
||||
nsProcess::_CloseProcess /NOUNLOAD `${_FILE}`
|
||||
Pop ${_ERR}
|
||||
!macroend
|
||||
|
||||
|
||||
!define nsProcess::Unload `!insertmacro nsProcess::Unload`
|
||||
|
||||
!macro nsProcess::Unload
|
||||
nsProcess::_Unload
|
||||
!macroend
|
||||
|
|
@ -564,7 +564,7 @@ Function .onInit
|
|||
; Prevent launching the application when a reboot is required and this
|
||||
; executable is the main application executable
|
||||
IfFileExists "$EXEDIR\${FileMainEXE}.moz-upgrade" +1 +4
|
||||
MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2
|
||||
MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE_HELPER)" IDNO +2
|
||||
Reboot
|
||||
Quit ; Nothing initialized so no need to call OnEndCommon
|
||||
|
||||
|
|
@ -590,7 +590,7 @@ Function .onInit
|
|||
; Prevent all operations (e.g. set as default, postupdate, etc.) when a
|
||||
; reboot is required and the executable launched is helper.exe
|
||||
IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4
|
||||
MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2
|
||||
MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE_HELPER)" IDNO +2
|
||||
Reboot
|
||||
Quit ; Nothing initialized so no need to call OnEndCommon
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# Set Z_ARCH=win32 or Z_ARCH=aarch64 on command line
|
||||
# Set Z_ARCH=win32 on command line to build and package win32
|
||||
if [ "$Z_ARCH" == "win32" ]; then
|
||||
ac_add_options --target=i686-pc-windows-msvc
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1804548
|
||||
ac_add_options --without-wasm-sandboxed-libraries
|
||||
elif [ "$Z_ARCH" == "arm64" ]; then
|
||||
elif [ "$Z_ARCH" == "aarch64" ]; then
|
||||
ac_add_options --target=aarch64-pc-windows-msvc
|
||||
fi
|
||||
|
||||
|
|
@ -23,17 +23,8 @@ ac_add_options --disable-webrtc
|
|||
ac_add_options --disable-eme
|
||||
ac_add_options --enable-official-branding
|
||||
|
||||
ac_add_options --disable-maintenance-service
|
||||
ac_add_options --disable-notification-server
|
||||
ac_add_options --disable-update-agent
|
||||
|
||||
export MOZILLA_OFFICIAL=1
|
||||
export RELEASE_OR_BETA=1
|
||||
|
||||
# We don't need source-repo info embedded, and generating it would require a
|
||||
# source-repo.h that Firefox only auto-populates for Mercurial, not our git
|
||||
# checkout, which breaks packaging. Turn it off.
|
||||
ac_add_options MOZ_INCLUDE_SOURCE_INFO=
|
||||
MOZ_REQUIRE_SIGNING=
|
||||
|
||||
# Build updater without MAR signature verification
|
||||
|
|
|
|||
118
app/win/mozilla-115.patch
Normal file
118
app/win/mozilla-115.patch
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp
|
||||
--- a/browser/app/nsBrowserApp.cpp
|
||||
+++ b/browser/app/nsBrowserApp.cpp
|
||||
@@ -154,19 +154,30 @@ static bool IsArg(const char* arg, const
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Bootstrap::UniquePtr gBootstrap;
|
||||
|
||||
static int do_main(int argc, char* argv[], char* envp[]) {
|
||||
+ // Allow profile downgrade for Zotero
|
||||
+ _putenv_s("MOZ_ALLOW_DOWNGRADE", "1");
|
||||
+ // Don't create dedicated profile (default-esr)
|
||||
+ _putenv_s("MOZ_LEGACY_PROFILES", "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 && IsArg(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/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
--- a/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
+++ b/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
@@ -162,17 +162,31 @@ static mozilla::LauncherFlags ProcessCmd
|
||||
mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND ||
|
||||
mozilla::CheckArg(aArgc, aArgv, "remote-debugging-port", nullptr,
|
||||
mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND ||
|
||||
mozilla::EnvHasValue("MOZ_AUTOMATION") ||
|
||||
mozilla::EnvHasValue("MOZ_HEADLESS")) {
|
||||
result |= mozilla::LauncherFlags::eWaitForBrowser;
|
||||
}
|
||||
|
||||
- if (mozilla::CheckArg(aArgc, aArgv, "no-deelevate") == mozilla::ARG_FOUND) {
|
||||
+ // Disable deelevation for Zotero
|
||||
+ //
|
||||
+ // If people are running as Administrator, or in some cases running with
|
||||
+ // UAC disabled, Word runs at integrity level High and deelevation drops
|
||||
+ // Zotero down to Medium, which causes 'Could not find a running Word
|
||||
+ // instance' errors.
|
||||
+ //
|
||||
+ // Even when not running as administrator, the process switch involved in
|
||||
+ // deelevation also seems to be causing various security software, such as
|
||||
+ // Cisco Secure Endpoint, to block Zotero from running:
|
||||
+ // https://forums.zotero.org/discussion/116762
|
||||
+ //
|
||||
+ // Disabling deelevation returns us to the behavior of Zotero 6, Word, and
|
||||
+ // most other programs
|
||||
+ if (true || mozilla::CheckArg(aArgc, aArgv, "no-deelevate") == mozilla::ARG_FOUND) {
|
||||
result |= mozilla::LauncherFlags::eNoDeelevate;
|
||||
}
|
||||
|
||||
if (mozilla::CheckArg(aArgc, aArgv, ATTEMPTING_DEELEVATION_FLAG) ==
|
||||
mozilla::ARG_FOUND) {
|
||||
result |= mozilla::LauncherFlags::eDeelevating;
|
||||
}
|
||||
|
||||
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h
|
||||
--- a/xpcom/build/BinaryPath.h
|
||||
+++ b/xpcom/build/BinaryPath.h
|
||||
@@ -283,16 +283,43 @@ class BinaryPath {
|
||||
if (NS_FAILED(Get(path))) {
|
||||
return nullptr;
|
||||
}
|
||||
UniqueFreePtr<char> result;
|
||||
result.reset(strdup(path));
|
||||
return result;
|
||||
}
|
||||
|
||||
+ static UniqueFreePtr<char> GetApplicationIni() {
|
||||
+ char path[MAXPATHLEN];
|
||||
+ if (NS_FAILED(Get(path))) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ char *c = path + strlen(path);
|
||||
+ while (c >= path && *c != '\\' && *c != '/') {
|
||||
+ *c = NULL;
|
||||
+ c--;
|
||||
+ }
|
||||
+
|
||||
+ if (c < path) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ char iniPath[MAXPATHLEN];
|
||||
+ int n = snprintf(iniPath, MAXPATHLEN, "%s\\app\\application.ini", path);
|
||||
+ if (n < 0 || n >= MAXPATHLEN) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ UniqueFreePtr<char> result;
|
||||
+ result.reset(strdup(iniPath));
|
||||
+ return result;
|
||||
+ }
|
||||
+
|
||||
#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];
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp
|
||||
index 4c5982c056fd..1ad4c9dfd597 100644
|
||||
--- a/browser/app/nsBrowserApp.cpp
|
||||
+++ b/browser/app/nsBrowserApp.cpp
|
||||
@@ -198,9 +198,20 @@ static bool HasFlag(int argc, char* argv[], const char* s) {
|
||||
constinit Bootstrap::UniquePtr gBootstrap;
|
||||
|
||||
static int do_main(int argc, char* argv[], char* envp[]) {
|
||||
+ // Allow profile downgrade for Zotero
|
||||
+ _putenv_s("MOZ_ALLOW_DOWNGRADE", "1");
|
||||
+ // Don't create dedicated profile (default-esr)
|
||||
+ _putenv_s("MOZ_LEGACY_PROFILES", "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");
|
||||
diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
index 8337336dc894..c55677631c88 100644
|
||||
--- a/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
+++ b/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
@@ -250,7 +250,21 @@ static mozilla::LauncherFlags ProcessCmdLine(int& aArgc, wchar_t* aArgv[]) {
|
||||
result |= mozilla::LauncherFlags::eWaitForBrowser;
|
||||
}
|
||||
|
||||
- if (mozilla::CheckArg(aArgc, aArgv, "no-deelevate") == mozilla::ARG_FOUND) {
|
||||
+ // Disable deelevation for Zotero
|
||||
+ //
|
||||
+ // If people are running as Administrator, or in some cases running with
|
||||
+ // UAC disabled, Word runs at integrity level High and deelevation drops
|
||||
+ // Zotero down to Medium, which causes 'Could not find a running Word
|
||||
+ // instance' errors.
|
||||
+ //
|
||||
+ // Even when not running as administrator, the process switch involved in
|
||||
+ // deelevation also seems to be causing various security software, such as
|
||||
+ // Cisco Secure Endpoint, to block Zotero from running:
|
||||
+ // https://forums.zotero.org/discussion/116762
|
||||
+ //
|
||||
+ // Disabling deelevation returns us to the behavior of Zotero 6, Word, and
|
||||
+ // most other programs
|
||||
+ if (true || mozilla::CheckArg(aArgc, aArgv, "no-deelevate") == mozilla::ARG_FOUND) {
|
||||
result |= mozilla::LauncherFlags::eNoDeelevate;
|
||||
}
|
||||
|
||||
diff --git a/toolkit/components/remote/WinRemoteMessage.cpp b/toolkit/components/remote/WinRemoteMessage.cpp
|
||||
index 16a59fa7beda..4aa85c4a7632 100644
|
||||
--- a/toolkit/components/remote/WinRemoteMessage.cpp
|
||||
+++ b/toolkit/components/remote/WinRemoteMessage.cpp
|
||||
@@ -32,6 +32,31 @@ WinRemoteMessageSender::WinRemoteMessageSender(int32_t aArgc,
|
||||
|
||||
COPYDATASTRUCT* WinRemoteMessageSender::CopyData() { return &mData; }
|
||||
|
||||
+nsresult WinRemoteMessageReceiver::ParseV0(const nsACString& aBuffer) {
|
||||
+ CommandLineParserWin<char> parser;
|
||||
+ parser.HandleCommandLine(aBuffer);
|
||||
+
|
||||
+ mCommandLine = new nsCommandLine();
|
||||
+ return mCommandLine->Init(parser.Argc(), parser.Argv(), nullptr,
|
||||
+ nsICommandLine::STATE_REMOTE_AUTO);
|
||||
+}
|
||||
+
|
||||
+nsresult WinRemoteMessageReceiver::ParseV1(const nsACString& aBuffer) {
|
||||
+ CommandLineParserWin<char> parser;
|
||||
+ size_t cch = parser.HandleCommandLine(aBuffer);
|
||||
+ ++cch; // skip a null char
|
||||
+
|
||||
+ nsCOMPtr<nsIFile> workingDir;
|
||||
+ if (cch < aBuffer.Length()) {
|
||||
+ (void)NS_NewLocalFile(NS_ConvertUTF8toUTF16(Substring(aBuffer, cch)),
|
||||
+ getter_AddRefs(workingDir));
|
||||
+ }
|
||||
+
|
||||
+ mCommandLine = new nsCommandLine();
|
||||
+ return mCommandLine->Init(parser.Argc(), parser.Argv(), workingDir,
|
||||
+ nsICommandLine::STATE_REMOTE_AUTO);
|
||||
+}
|
||||
+
|
||||
nsresult WinRemoteMessageReceiver::ParseV2(const nsAString& aBuffer) {
|
||||
CommandLineParserWin<char16_t> parser;
|
||||
size_t cch = parser.HandleCommandLine(aBuffer);
|
||||
@@ -110,6 +135,12 @@ nsresult WinRemoteMessageReceiver::ParseV3(const nsACString& aBuffer) {
|
||||
|
||||
nsresult WinRemoteMessageReceiver::Parse(const COPYDATASTRUCT* aMessageData) {
|
||||
switch (static_cast<WinRemoteMessageVersion>(aMessageData->dwData)) {
|
||||
+ case WinRemoteMessageVersion::CommandLineOnly:
|
||||
+ return ParseV0(nsDependentCSubstring(
|
||||
+ reinterpret_cast<char*>(aMessageData->lpData), aMessageData->cbData));
|
||||
+ case WinRemoteMessageVersion::CommandLineAndWorkingDir:
|
||||
+ return ParseV1(nsDependentCSubstring(
|
||||
+ reinterpret_cast<char*>(aMessageData->lpData), aMessageData->cbData));
|
||||
case WinRemoteMessageVersion::CommandLineAndWorkingDirInUtf16:
|
||||
return ParseV2(
|
||||
nsDependentSubstring(reinterpret_cast<wchar_t*>(aMessageData->lpData),
|
||||
diff --git a/toolkit/components/remote/WinRemoteMessage.h b/toolkit/components/remote/WinRemoteMessage.h
|
||||
index b62fb73fa13d..ee6612f55f4b 100644
|
||||
--- a/toolkit/components/remote/WinRemoteMessage.h
|
||||
+++ b/toolkit/components/remote/WinRemoteMessage.h
|
||||
@@ -28,10 +28,10 @@
|
||||
// some sort, as v3 does, to reduce the chances of variants of bug 1847458.
|
||||
enum class WinRemoteMessageVersion : uint32_t {
|
||||
// "<CommandLine>\0" in utf8
|
||||
- /* CommandLineOnly = 0, */
|
||||
+ CommandLineOnly = 0,
|
||||
|
||||
// "<CommandLine>\0<WorkingDir>\0" in utf8
|
||||
- /* CommandLineAndWorkingDir = 1, */
|
||||
+ CommandLineAndWorkingDir = 1,
|
||||
|
||||
// L"<CommandLine>\0<WorkingDir>\0" in utf16, used by ESR 128
|
||||
CommandLineAndWorkingDirInUtf16 = 2,
|
||||
@@ -61,6 +61,8 @@ class WinRemoteMessageSender final {
|
||||
class WinRemoteMessageReceiver final {
|
||||
nsCOMPtr<nsICommandLineRunner> mCommandLine;
|
||||
|
||||
+ nsresult ParseV0(const nsACString& aBuffer);
|
||||
+ nsresult ParseV1(const nsACString& aBuffer);
|
||||
nsresult ParseV2(const nsAString& aBuffer);
|
||||
nsresult ParseV3(const nsACString& aBuffer);
|
||||
|
||||
diff --git a/toolkit/mozapps/update/common/commonupdatedir.cpp b/toolkit/mozapps/update/common/commonupdatedir.cpp
|
||||
index 0ba9fcef9417..ed286476fec3 100644
|
||||
--- a/toolkit/mozapps/update/common/commonupdatedir.cpp
|
||||
+++ b/toolkit/mozapps/update/common/commonupdatedir.cpp
|
||||
@@ -43,7 +43,7 @@
|
||||
// this problem in the future, we are including a UUID in the root update
|
||||
// directory name to attempt to ensure that it will be created by this code and
|
||||
// won't already exist with the wrong permissions.
|
||||
-# define ROOT_UPDATE_DIR_NAME "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38"
|
||||
+# define ROOT_UPDATE_DIR_NAME "Zotero"
|
||||
// This describes the directory between the "Mozilla" directory and the install
|
||||
// path hash (i.e. C:\ProgramData\Mozilla\<UPDATE_PATH_MID_DIR_NAME>\<hash>)
|
||||
# define UPDATE_PATH_MID_DIR_NAME "updates"
|
||||
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h
|
||||
index 2c8d78f02440..f55d3db32469 100644
|
||||
--- a/xpcom/build/BinaryPath.h
|
||||
+++ b/xpcom/build/BinaryPath.h
|
||||
@@ -286,6 +286,33 @@ class BinaryPath {
|
||||
return result;
|
||||
}
|
||||
|
||||
+ static UniqueFreePtr<char> GetApplicationIni() {
|
||||
+ char path[MAXPATHLEN];
|
||||
+ if (NS_FAILED(Get(path))) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ char *c = path + strlen(path);
|
||||
+ while (c >= path && *c != '\\' && *c != '/') {
|
||||
+ *c = NULL;
|
||||
+ c--;
|
||||
+ }
|
||||
+
|
||||
+ if (c < path) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ char iniPath[MAXPATHLEN];
|
||||
+ int n = snprintf(iniPath, MAXPATHLEN, "%s\\app\\application.ini", path);
|
||||
+ if (n < 0 || n >= MAXPATHLEN) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ UniqueFreePtr<char> result;
|
||||
+ result.reset(strdup(iniPath));
|
||||
+ return result;
|
||||
+ }
|
||||
+
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
static nsresult GetFile(nsIFile** aResult) {
|
||||
nsCOMPtr<nsIFile> lf;
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f1cb0ea50ccb7a609358131ae2a06e040761f012cc196ff0c436f4939895b156
|
||||
size 876988
|
||||
oid sha256:aa342dc2f3cb260505154fc823710955026f882ee8153bda8f4e592912c957de
|
||||
size 363688
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:94ac6f2b12672e5dfe166287fce4d7bf357a77fac231b6c2818ad39d6a9d3f15
|
||||
size 614252
|
||||
oid sha256:b676ce06f149e296b294cfea72003797eb53f874bcf3786e496191f38e5688e9
|
||||
size 499708
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ content zotero-platform chrome/content/zotero-platform/unix/ os=FreeBSD
|
|||
content zotero-platform chrome/content/zotero-platform/unix/ os=OpenBSD
|
||||
|
||||
content zotero-platform-version chrome/content/zotero-platform/default-version/
|
||||
content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin osversion>=11
|
||||
|
||||
resource zotero resource/
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,17 +13,6 @@
|
|||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.monaco-editor .empty-editor-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0 64px;
|
||||
color: #ffffff8c; /* dark fill-secondary */
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -34,7 +23,6 @@
|
|||
<script>
|
||||
/**
|
||||
* @param {Object} [params = {}]
|
||||
* @param {string} [params.emptyOverlayMessage] Optional content for the empty overlay
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
function loadMonaco(params = {}) {
|
||||
|
|
@ -59,6 +47,7 @@
|
|||
globalEditor = monaco;
|
||||
|
||||
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||
target: monaco.languages.typescript.ScriptTarget.ES6,
|
||||
allowNonTsExtensions: true // needed for peeking definitions from in-memory models to work
|
||||
});
|
||||
|
||||
|
|
@ -76,30 +65,6 @@
|
|||
|
||||
editor.getModel().setEOL(monaco.editor.EndOfLineSequence.LF);
|
||||
|
||||
|
||||
// Display overlay message when editor is empty, if specified
|
||||
if (params.emptyOverlayMessage) {
|
||||
let overlayNode = document.createElement('div');
|
||||
overlayNode.className = 'empty-editor-overlay';
|
||||
|
||||
overlayNode.innerText = params.emptyOverlayMessage;
|
||||
let overlayWidget = {
|
||||
getId: () => `empty-editor-overlay`,
|
||||
getDomNode: () => overlayNode,
|
||||
getPosition: () => null
|
||||
};
|
||||
editor.addOverlayWidget(overlayWidget);
|
||||
|
||||
function update() {
|
||||
let overlayShown = editor.getModel().getValue() === "";
|
||||
overlayNode.style.display = overlayShown ? '' : 'none';
|
||||
}
|
||||
|
||||
overlayNode.addEventListener('click', () => editor.focus());
|
||||
editor.onDidChangeModelContent(update)
|
||||
update();
|
||||
}
|
||||
|
||||
window.onresize = function () {
|
||||
editor.layout();
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -43,7 +43,6 @@
|
|||
title="Scaffold"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
windowtype="zotero:scaffold"
|
||||
style="display: flex;"
|
||||
drawintitlebar-platforms="win, linux">
|
||||
|
||||
|
|
@ -52,15 +51,18 @@
|
|||
<html:link rel="localization" href="browser/browserSets.ftl"/>
|
||||
<html:link rel="localization" href="toolkit/global/textActions.ftl"/>
|
||||
<html:link rel="localization" href="zotero.ftl" />
|
||||
<html:link rel="localization" href="scaffold.ftl" />
|
||||
</linkset>
|
||||
|
||||
<script>
|
||||
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
|
||||
|
||||
Services.scriptloader.loadSubScript("resource://zotero/require.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/titlebar.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/platformKeys.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/editMenuOverlay.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/xpcom/translate/testTranslators/translatorTester.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://scaffold/content/translators.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://scaffold/content/scaffold.js", this);
|
||||
|
||||
|
|
@ -73,8 +75,6 @@
|
|||
|
||||
// Custom elements
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://scaffold/content/scaffoldItemPreview.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://scaffold/content/scaffoldItemPreviews.js", this);
|
||||
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/menuAccessKey.js", this);
|
||||
</script>
|
||||
|
|
@ -96,7 +96,7 @@
|
|||
|
||||
<command id="cmd_increase_font_size" oncommand="Scaffold.increaseFontSize()"/>
|
||||
<command id="cmd_decrease_font_size" oncommand="Scaffold.decreaseFontSize()"/>
|
||||
<command id="cmd_restore_font_size" oncommand="Scaffold.setFontSize(13)"/>
|
||||
<command id="cmd_restore_font_size" oncommand="Scaffold.setFontSize(11)"/>
|
||||
|
||||
<commandset id="editMenuCommands"/>
|
||||
</commandset>
|
||||
|
|
@ -138,19 +138,15 @@
|
|||
</keyset>
|
||||
|
||||
<popupset>
|
||||
<menupopup id="testing-context-menu" onpopupshowing="Scaffold.handleTestingContextMenuShowing()">
|
||||
<menupopup id="testing-context-menu">
|
||||
<menuitem label="&scaffold.testing.copyToClipboard;" tooltiptext="Copy the URL or data for the current test to the clipboard" oncommand="Scaffold.copyToClipboard()"/>
|
||||
<menuitem id="testing-editImport" label="&scaffold.testing.edit;" tooltiptext="Edit the input data for the current test" oncommand="Scaffold.editImportFromTest()"/>
|
||||
<menu id="testing-openURL" label="&scaffold.testing.openUrl;">
|
||||
<menuitem id="testing_editImport" label="&scaffold.testing.edit;" tooltiptext="Edit the input data for the current test" oncommand="Scaffold.editImportFromTest()"/>
|
||||
<menu id="testing_openURL" label="&scaffold.testing.openUrl;">
|
||||
<menupopup>
|
||||
<menuitem label="&scaffold.testing.openUrl.internally;" tooltiptext="Open the URL for the current test in the Scaffold browser" oncommand="Scaffold.openURL(false)"/>
|
||||
<menuitem label="&scaffold.testing.openUrl.externally;" tooltiptext="Open the URL for the current test in your default browser" oncommand="Scaffold.openURL(true)"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="testing-delete" label="Delete" oncommand="Scaffold.deleteSelectedTests()"/>
|
||||
<menuitem id="testing-run" label="Run" oncommand="Scaffold.runSelectedTests()"/>
|
||||
<menuitem id="testing-applyUpdates" label="&scaffold.testing.applyUpdates;" oncommand="Scaffold.updateSelectedTests()"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
|
||||
|
|
@ -293,16 +289,11 @@
|
|||
</keyset>
|
||||
|
||||
<!-- nsMenuBarX hides these and uses them to build the Application menu. -->
|
||||
<!-- macOS 13 changed "Preferences" to "Settings" so one of these gets picked -->
|
||||
<menupopup id="menu_FilePopup">
|
||||
<menuitem id="menu_preferences"
|
||||
label="&preferencesCmdMac.label;"
|
||||
key="key_preferencesCmdMac"
|
||||
oncommand="Zotero.Utilities.Internal.openPreferences()"/>
|
||||
<menuitem id="menu_settings"
|
||||
data-l10n-id="menu-application-settings"
|
||||
key="key_preferencesCmdMac"
|
||||
oncommand="Zotero.Utilities.Internal.openPreferences()"/>
|
||||
<menuitem id="menu_mac_services"
|
||||
label="&servicesMenuMac.label;"/>
|
||||
<menuitem id="menu_mac_hide_app"
|
||||
|
|
@ -362,25 +353,21 @@
|
|||
titlebar-btn="min"
|
||||
oncommand="window.minimize();"
|
||||
data-l10n-id="browser-window-minimize-button"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<toolbarbutton class="titlebar-button titlebar-max"
|
||||
titlebar-btn="max"
|
||||
oncommand="window.maximize();"
|
||||
data-l10n-id="browser-window-maximize-button"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<toolbarbutton class="titlebar-button titlebar-restore"
|
||||
titlebar-btn="max"
|
||||
oncommand="window.fullScreen ? BrowserFullScreen() : window.restore();"
|
||||
data-l10n-id="browser-window-restore-down-button"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<toolbarbutton class="titlebar-button titlebar-close"
|
||||
titlebar-btn="close"
|
||||
oncommand="window.close();"
|
||||
data-l10n-id="browser-window-close-button"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
|
@ -476,7 +463,7 @@
|
|||
</tabpanel>
|
||||
<tabpanel flex="1" id="tabpanel-code">
|
||||
<vbox flex="1">
|
||||
<iframe src="monaco/monaco.html" id="editor-code" focus-on-tab-select="true" flex="1" onmousedown="this.focus()"/>
|
||||
<iframe src="monaco/monaco.html" id="editor-code" flex="1" onmousedown="this.focus()"/>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel flex="1" id="tabpanel-tests">
|
||||
|
|
@ -487,28 +474,32 @@
|
|||
</keyset>
|
||||
|
||||
<listheader>
|
||||
<treecol label="&scaffold.testing.input.label;" class="col-input"/>
|
||||
<treecol label="&scaffold.testing.status.label;" class="col-status"/>
|
||||
<treecol label="&scaffold.testing.defer.label;" class="col-defer"/>
|
||||
<treecol label="&scaffold.testing.input.label;" flex="1"/>
|
||||
<treecol label="&scaffold.testing.status.label;" width="150"/>
|
||||
<treecol label="&scaffold.testing.defer.label;"/>
|
||||
</listheader>
|
||||
<richlistbox
|
||||
id="testing-listbox"
|
||||
observes="validate-tests"
|
||||
flex="1"
|
||||
seltype="multiple"
|
||||
onselect="Scaffold.handleTestSelect(event)"
|
||||
context="testing-context-menu"
|
||||
focus-on-tab-select="true"
|
||||
onselect="Scaffold.handleTestingListboxSelect()"
|
||||
ondblclick="Scaffold.handleTestingListboxDblClick()"
|
||||
/>
|
||||
</vbox>
|
||||
<hbox pack="end">
|
||||
<checkbox
|
||||
id="checkbox-remember-cookies"
|
||||
label="&scaffold.testing.rememberCookies;"
|
||||
native="true"
|
||||
checked="true"
|
||||
/>
|
||||
<hbox id="testing-buttons">
|
||||
<button observes="validate-tests" label="&scaffold.testing.delete;" tooltiptext="Delete the selected tests" oncommand="Scaffold.deleteSelectedTests()"/>
|
||||
<button observes="validate-tests" label="&scaffold.testing.run;" tooltiptext="Run the selected tests" oncommand="Scaffold.runSelectedTests()"/>
|
||||
<button observes="validate-tests" label="&scaffold.testing.update;" tooltiptext="Run the selected tests and update the test definitions with the latest data" oncommand="Scaffold.updateSelectedTests()"/>
|
||||
|
||||
<hbox flex="1" pack="end">
|
||||
<checkbox
|
||||
id="checkbox-remember-cookies"
|
||||
label="&scaffold.testing.rememberCookies;"
|
||||
native="true"
|
||||
checked="true"
|
||||
/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
<iframe src="monaco/monaco.html" id="editor-tests" onmousedown="this.focus()"/>
|
||||
</vbox>
|
||||
|
|
@ -518,9 +509,7 @@
|
|||
<hbox id="browser-url-container" align="center">
|
||||
<label control="browser-url" value="&scaffold.tabUrl.label;"/>
|
||||
<html:input id="browser-url" style="flex: 1;"/>
|
||||
<button id="load-url-button" label="→" tooltiptext="Load URL" oncommand="Scaffold.loadURLInBrowser()"/>
|
||||
<image id="browser-loading" class="zotero-spinner-16" />
|
||||
<button id="create-test-button" observes="validate-tests" label="&scaffold.testing.create.web;" tooltiptext="Create a new test from the current page" oncommand="Scaffold.saveTestFromCurrent('web')"/>
|
||||
<button observes="validate-tests" label="&scaffold.testing.create.web;" tooltiptext="Create a new test from the current page" oncommand="Scaffold.saveTestFromCurrent('web')"/>
|
||||
</hbox>
|
||||
|
||||
<browser
|
||||
|
|
@ -539,15 +528,14 @@
|
|||
<button observes="validate-tests" label="&scaffold.testing.create.import;" tooltiptext="Create a new test from the current import data" oncommand="Scaffold.saveTestFromCurrent('import')" />
|
||||
<button observes="validate-tests" label="&scaffold.testing.create.search;" tooltiptext="Create a new test from the current search data" oncommand="Scaffold.saveTestFromCurrent('search')" />
|
||||
</hbox>
|
||||
<iframe src="monaco/monaco.html" id="editor-import" flex="1" focus-on-tab-select="true" onmousedown="this.focus()"/>
|
||||
<iframe src="monaco/monaco.html" id="editor-import" flex="1" onmousedown="this.focus()"/>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
<splitter id="pane-splitter" resizeafter="farthest" />
|
||||
<vbox id="right-pane">
|
||||
<html:textarea id="output" readonly="true" data-l10n-id="scaffold-translator-output"/>
|
||||
<scaffold-item-previews id="item-previews" hidden="true"/>
|
||||
<html:textarea id="output" readonly="true"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,219 +0,0 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2025 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
https://www.zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
{
|
||||
let { diff } = ChromeUtils.importESModule('chrome://zotero/content/xpcom/translate/testTranslators/diff.mjs');
|
||||
|
||||
class ScaffoldItemPreview extends XULElementBase {
|
||||
content = MozXULElement.parseXULToFragment(`
|
||||
<html:div class="diff" readonly="true"/>
|
||||
<info-box/>
|
||||
<abstract-box/>
|
||||
<html:div class="attachments-preview"/>
|
||||
<html:div class="notes-preview"/>
|
||||
<tags-box/>
|
||||
`);
|
||||
|
||||
_preItem = null;
|
||||
|
||||
_postItem = null;
|
||||
|
||||
_cachedDiff = null;
|
||||
|
||||
get itemPair() {
|
||||
return [this._preItem, this._postItem];
|
||||
}
|
||||
|
||||
set itemPair([preItem, postItem]) {
|
||||
this._preItem = this._normalizeItem(preItem);
|
||||
if (!postItem) {
|
||||
// Default to cleaned version of preItem
|
||||
let zoteroPreItem = new Zotero.Item();
|
||||
zoteroPreItem.libraryID = Zotero.Libraries.userLibraryID;
|
||||
zoteroPreItem.fromJSON(this._preItem);
|
||||
|
||||
postItem = Zotero.Utilities.Internal.itemToExportFormat(zoteroPreItem, {
|
||||
legacy: true,
|
||||
skipBaseFields: true
|
||||
});
|
||||
delete postItem.relations;
|
||||
delete postItem.collections;
|
||||
delete postItem.uniqueFields;
|
||||
delete postItem.uri;
|
||||
for (let [key, value] of Object.entries(postItem)) {
|
||||
if (value === null) {
|
||||
delete postItem[key];
|
||||
}
|
||||
}
|
||||
// Because we're just diffing the result of cleaning, differences
|
||||
// in these arrays are mostly meaningless
|
||||
postItem.attachments = structuredClone(preItem.attachments);
|
||||
postItem.notes = structuredClone(preItem.notes);
|
||||
postItem.tags = structuredClone(preItem.tags);
|
||||
}
|
||||
this._postItem = this._normalizeItem(postItem);
|
||||
this._cachedDiff = null;
|
||||
this.render();
|
||||
}
|
||||
|
||||
get diff() {
|
||||
if (!this._cachedDiff) {
|
||||
if (!this._preItem || !this._postItem) {
|
||||
return null;
|
||||
}
|
||||
let diffString = diff(this._preItem, this._postItem)
|
||||
.split('\n')
|
||||
.filter(line => (
|
||||
// Remove lines without differences
|
||||
!/^\s*"\w+": (".*"|\d+|true|false)$/.test(line)
|
||||
// And accessDate diff lines - they just show a meaningless format change
|
||||
&& !/^\s*[-+]\s*"accessDate": "/.test(line)
|
||||
))
|
||||
.join('\n');
|
||||
// Remove now-empty object/array literals
|
||||
let emptyLiteralRe = /(\n|^)\s*("\w+": )?(\{\s*}|\[\s*])/g;
|
||||
while (emptyLiteralRe.test(diffString)) {
|
||||
diffString = diffString.replace(emptyLiteralRe, '');
|
||||
}
|
||||
this._cachedDiff = diffString;
|
||||
}
|
||||
return this._cachedDiff;
|
||||
}
|
||||
|
||||
get diffStats() {
|
||||
let added = 0;
|
||||
let removed = 0;
|
||||
for (let diffLine of this.diff.split('\n')) {
|
||||
if (diffLine.startsWith('+')) {
|
||||
added++;
|
||||
}
|
||||
else if (diffLine.startsWith('-')) {
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
return { added, removed };
|
||||
}
|
||||
|
||||
init() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.initialized) return;
|
||||
|
||||
let postItem = this._postItem;
|
||||
|
||||
let zoteroPostItem = new Zotero.Item();
|
||||
zoteroPostItem.libraryID = Zotero.Libraries.userLibraryID;
|
||||
if (Object.entries(postItem).length) {
|
||||
zoteroPostItem.fromJSON(postItem);
|
||||
}
|
||||
|
||||
let [diffBox, infoBox, abstractBox, attachmentsPreview, notesPreview, tagsBox] = this.children;
|
||||
for (let box of [infoBox, abstractBox, tagsBox]) {
|
||||
box.mode = 'view';
|
||||
box.editable = false;
|
||||
box.item = zoteroPostItem;
|
||||
box._forceRenderAll();
|
||||
}
|
||||
|
||||
attachmentsPreview.replaceChildren(
|
||||
...(postItem.attachments ?? []).map((jsonAttachment) => {
|
||||
let zoteroAttachment = new Zotero.Item('attachment');
|
||||
|
||||
let displayTitle = jsonAttachment.title;
|
||||
let urlOrPath = jsonAttachment.url || jsonAttachment.path;
|
||||
if (urlOrPath) {
|
||||
displayTitle += ` (${urlOrPath})`;
|
||||
}
|
||||
zoteroAttachment.setField('title', displayTitle);
|
||||
zoteroAttachment.attachmentContentType = jsonAttachment.mimeType;
|
||||
if (jsonAttachment.snapshot === false && jsonAttachment.mimeType === 'text/html') {
|
||||
zoteroAttachment.attachmentLinkMode = Zotero.Attachments.LINK_MODE_LINKED_URL;
|
||||
}
|
||||
else {
|
||||
zoteroAttachment.attachmentLinkMode = Zotero.Attachments.LINK_MODE_IMPORTED_URL;
|
||||
}
|
||||
let row = document.createXULElement('attachment-row');
|
||||
row.attachment = zoteroAttachment;
|
||||
row._handleAttachmentClick = () => {};
|
||||
return row;
|
||||
})
|
||||
);
|
||||
notesPreview.replaceChildren(
|
||||
...(postItem.notes ?? []).map((jsonNote) => {
|
||||
let row = document.createXULElement('note-row');
|
||||
row.note = {
|
||||
title: '',
|
||||
body: Zotero.Utilities.cleanTags(jsonNote.note),
|
||||
};
|
||||
return row;
|
||||
})
|
||||
);
|
||||
|
||||
for (let diffLine of this.diff.split('\n')) {
|
||||
let lineContainer = document.createElement('div');
|
||||
lineContainer.textContent = diffLine;
|
||||
if (diffLine.startsWith('+')) {
|
||||
lineContainer.classList.add('added');
|
||||
}
|
||||
else if (diffLine.startsWith('-')) {
|
||||
lineContainer.classList.add('removed');
|
||||
}
|
||||
diffBox.append(lineContainer);
|
||||
}
|
||||
}
|
||||
|
||||
_normalizeItem(jsonItem) {
|
||||
jsonItem = JSON.parse(JSON.stringify(jsonItem));
|
||||
|
||||
if (!Object.entries(jsonItem).length) {
|
||||
return jsonItem;
|
||||
}
|
||||
|
||||
// Delete ID (meaningless)
|
||||
delete jsonItem.id;
|
||||
|
||||
// _itemDone() sets 'title' in addition to the item type's mapped
|
||||
// title field for some reason. Delete it so it doesn't show up in the diff.
|
||||
let titleField = Zotero.ItemFields.getName(
|
||||
Zotero.ItemFields.getFieldIDFromTypeAndBase(jsonItem.itemType, 'title')
|
||||
);
|
||||
if (titleField !== 'title' && jsonItem[titleField]) {
|
||||
delete jsonItem.title;
|
||||
}
|
||||
|
||||
if (jsonItem.accessDate === 'CURRENT_TIMESTAMP') {
|
||||
jsonItem.accessDate = Zotero.Date.dateToISO(new Date());
|
||||
}
|
||||
|
||||
return jsonItem;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('scaffold-item-preview', ScaffoldItemPreview);
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2025 Corporation for Digital Scholarship
|
||||
Vienna, Virginia, USA
|
||||
https://www.zotero.org
|
||||
|
||||
This file is part of Zotero.
|
||||
|
||||
Zotero is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Zotero 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
{
|
||||
class ScaffoldItemPreviews extends XULElementBase {
|
||||
content = MozXULElement.parseXULToFragment(`
|
||||
<html:div class="switcher">
|
||||
<button label="<" class="previous"/>
|
||||
<html:div><html:span class="current"/>/<html:span class="max"/></html:div>
|
||||
<button label=">" class="next"/>
|
||||
</html:div>
|
||||
<deck/>
|
||||
`);
|
||||
|
||||
_previews = [];
|
||||
|
||||
_deck;
|
||||
|
||||
_switcher;
|
||||
|
||||
init() {
|
||||
this._deck = this.querySelector('deck');
|
||||
this._switcher = this.querySelector('.switcher');
|
||||
|
||||
this.querySelector('.previous').addEventListener('command', () => {
|
||||
this._deck.selectedIndex--;
|
||||
this._renderPreviews();
|
||||
});
|
||||
this.querySelector('.next').addEventListener('command', () => {
|
||||
this._deck.selectedIndex++;
|
||||
this._renderPreviews();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} preItem JSON item
|
||||
* @param {any} [postItem] JSON item (defaults to cleaned version of preItem)
|
||||
* @returns {ScaffoldItemPreview}
|
||||
*/
|
||||
createPreviewForItemPair(preItem, postItem) {
|
||||
let preview = document.createXULElement('scaffold-item-preview');
|
||||
preview.itemPair = [preItem, postItem];
|
||||
return preview;
|
||||
}
|
||||
|
||||
clearPreviews() {
|
||||
this._previews = [];
|
||||
this._deck.selectedIndex = 0;
|
||||
this._deck.replaceChildren();
|
||||
this._updateVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ScaffoldItemPreview[]} previews
|
||||
*/
|
||||
setPreviews(previews) {
|
||||
this._previews = previews;
|
||||
this._deck.selectedIndex = 0;
|
||||
this._renderPreviews();
|
||||
this._updateVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ScaffoldItemPreview} preview
|
||||
*/
|
||||
addPreview(preview) {
|
||||
this._previews.push(preview);
|
||||
this._renderPreviews();
|
||||
this._updateVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} preItem JSON item
|
||||
* @param {any} [postItem] JSON item (defaults to cleaned version of preItem)
|
||||
* @returns {ScaffoldItemPreview}
|
||||
*/
|
||||
addItemPair(preItem, postItem) {
|
||||
let preview = this.createPreviewForItemPair(preItem, postItem);
|
||||
this.addPreview(preview);
|
||||
return preview;
|
||||
}
|
||||
|
||||
_updateVisibility() {
|
||||
if (this._deck.childElementCount) {
|
||||
if (this.hidden) {
|
||||
let splitterPane = this.closest('splitter + *');
|
||||
if (splitterPane) {
|
||||
// If the pane hasn't been resized, it won't have a fixed width,
|
||||
// so it'll grow when wrapping text is added. Fix its width now.
|
||||
splitterPane.style.width = splitterPane.getBoundingClientRect().width + 'px';
|
||||
}
|
||||
this.hidden = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
_renderPreviews() {
|
||||
this._deck.replaceChildren(
|
||||
...this._previews.map((preview, i) => {
|
||||
if (i === this._deck.selectedIndex) {
|
||||
return preview;
|
||||
}
|
||||
return document.createXULElement('hbox');
|
||||
})
|
||||
);
|
||||
|
||||
if (this._deck.children.length > 1) {
|
||||
this._switcher.hidden = false;
|
||||
this._switcher.querySelector('.current').textContent = this._deck.selectedIndex + 1;
|
||||
this._switcher.querySelector('.max').textContent = this._deck.children.length;
|
||||
}
|
||||
else {
|
||||
this._switcher.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('scaffold-item-previews', ScaffoldItemPreviews);
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
import { AbstractWebTranslationEnvironment } from 'chrome://zotero/content/xpcom/translate/testTranslators/translatorTester.mjs';
|
||||
|
||||
export class ZoteroWebTranslationEnvironment extends AbstractWebTranslationEnvironment {
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {TranslatorTester} tester
|
||||
* @returns {Promise<HiddenBrowser>}
|
||||
*/
|
||||
async fetchPage(url, { tester }) {
|
||||
const { HiddenBrowser } = ChromeUtils.importESModule('chrome://zotero/content/HiddenBrowser.mjs');
|
||||
let browser = new HiddenBrowser({
|
||||
docShell: { allowMetaRedirects: true },
|
||||
userContextId: tester.cookieSandbox,
|
||||
});
|
||||
|
||||
await browser.load(url, { requireSuccessfulStatus: true });
|
||||
return browser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HiddenBrowser} browser
|
||||
* @param {TranslatorTester} tester
|
||||
* @returns {Promise<false>} False for now, because we aren't sure that
|
||||
* detection would work without the defer delay.
|
||||
*/
|
||||
async waitForLoad(browser, { tester }) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HiddenBrowser} browser
|
||||
* @param {TranslatorTester} tester
|
||||
* @param {Record<string, Function>} handlers
|
||||
* @param {AbortSignal} signal
|
||||
* @returns {Promise<{
|
||||
* detectedItemType?: string;
|
||||
* items?: Zotero.Item[];
|
||||
* reason?: string;
|
||||
* }>}
|
||||
*/
|
||||
async runTranslation(browser, { tester, handlers, signal }) {
|
||||
const { RemoteTranslate } = ChromeUtils.importESModule('chrome://zotero/content/RemoteTranslate.mjs');
|
||||
|
||||
let translate = new RemoteTranslate({ disableErrorReporting: true });
|
||||
try {
|
||||
await translate.setBrowser(browser);
|
||||
await translate.setTranslatorProvider(tester.translatorProvider);
|
||||
translate.setTranslator(tester.translator);
|
||||
for (let [type, fn] of Object.entries(handlers)) {
|
||||
translate.setHandler(type, fn);
|
||||
}
|
||||
|
||||
let detectedTranslators = await translate.detect();
|
||||
if (!detectedTranslators.length) {
|
||||
return { items: null, reason: 'Detection failed' };
|
||||
}
|
||||
|
||||
let detectedItemType = detectedTranslators[0].itemType;
|
||||
let items = await translate.translate({ libraryID: false });
|
||||
return { detectedItemType, items };
|
||||
}
|
||||
catch (e) {
|
||||
return { items: null, reason: 'Translation failed: ' + e };
|
||||
}
|
||||
finally {
|
||||
translate.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HiddenBrowser} browser
|
||||
*/
|
||||
destroy(browser) {
|
||||
browser.destroy();
|
||||
}
|
||||
}
|
||||
4
chrome/content/zotero-platform/mac-big-sur/style.css
Normal file
4
chrome/content/zotero-platform/mac-big-sur/style.css
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/* Quick Format dialog, which is based on window corners, which are different on Big Sur */
|
||||
#quick-format-iframe {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
|
@ -9,6 +9,11 @@ window.citation-dialog {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.citation-dialog.progress-bar .citation-dialog.deck {
|
||||
height: 37px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.citation-dialog.entry {
|
||||
background: -moz-linear-gradient(-90deg, rgb(243,123,119) 0, rgb(180,47,38) 50%, rgb(156,36,27) 50%);
|
||||
|
|
|
|||
26
chrome/content/zotero-platform/mac/preferences.css
Normal file
26
chrome/content/zotero-platform/mac/preferences.css
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* 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/. */
|
||||
|
||||
window[windowtype="zotero:pref"] {
|
||||
padding: 0;
|
||||
font: -moz-dialog !important;
|
||||
|
||||
-moz-appearance: dialog;
|
||||
color: -moz-DialogText;
|
||||
}
|
||||
|
||||
groupbox > label > h2, groupbox > * > label > h2 {
|
||||
border-bottom: #b5b5b5 1px solid;
|
||||
}
|
||||
|
||||
groupbox > label > h2, groupbox > * > label > h2, caption {
|
||||
padding-bottom: 0.2em;
|
||||
font: caption;
|
||||
}
|
||||
|
||||
.help-button {
|
||||
appearance: auto;
|
||||
-moz-default-appearance: -moz-mac-help-button;
|
||||
min-width: 0;
|
||||
}
|
||||
|
|
@ -10,11 +10,6 @@ tab {
|
|||
border-right: 1px solid hsla(0, 0%, 0%, 0.2);
|
||||
border-left: 1px solid hsla(0, 0%, 0%, 0.2);
|
||||
}
|
||||
|
||||
tab:where([visuallyselected="true"]) {
|
||||
background: Background;
|
||||
}
|
||||
|
||||
/* Fixes misc Ubuntu 16.10 rendering issue fixes. */
|
||||
#zotero-prefs menulist dropmarker{
|
||||
width: 30px;
|
||||
|
|
@ -22,3 +17,13 @@ tab:where([visuallyselected="true"]) {
|
|||
#zotero-prefs .numberbox-input-box{
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
#zotero-tags-splitter:not([state=collapsed]) {
|
||||
min-height: 5px;
|
||||
}
|
||||
|
||||
#zotero-context-splitter-stacked {
|
||||
-moz-appearance: none;
|
||||
background-color: #ececec;
|
||||
border-top: 1px solid hsla(0, 0%, 0%, 0.2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
window.citation-dialog {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
|
@ -7,8 +8,16 @@ window.citation-dialog {
|
|||
.citation-dialog.entry {
|
||||
background: -moz-linear-gradient(-90deg, rgb(243,123,119) 0, rgb(180,47,38) 50%, rgb(156,36,27) 50%);
|
||||
padding: 10px;
|
||||
/* mouse events on win don't fire right at the edge of the window. The dialog has a gap
|
||||
before the window edges to ensure the window can be dragged from any point of the dialog */
|
||||
margin:10px;
|
||||
}
|
||||
|
||||
.note-dialog .citation-dialog.entry {
|
||||
background: -moz-linear-gradient(-90deg, rgb(249, 231, 179) 0, rgb(228, 193, 94) 50%, rgb(221, 184, 81) 50%);
|
||||
}
|
||||
|
||||
.citation-dialog.entry:not([square="true"]) {
|
||||
-moz-border-radius: 15px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
|
@ -17,6 +17,14 @@ tree {
|
|||
padding-left: 0.05em;
|
||||
}
|
||||
|
||||
.zotero-editpane-item-box > scrollbox, .zotero-view-item > tabpanel > vbox,
|
||||
#zotero-editpane-tags > scrollbox, .zotero-editpane-related {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#zotero-editpane-tags > scrollbox {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
/*
|
||||
Default scrollbar displays up/down arrows even
|
||||
|
|
|
|||
|
|
@ -23,11 +23,15 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
ChromeUtils.defineESModuleGetters(globalThis, {
|
||||
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
Zotero: "chrome://zotero/content/zotero.mjs",
|
||||
});
|
||||
|
||||
export class BlockingObserver {
|
||||
var EXPORTED_SYMBOLS = ["BlockingObserver"];
|
||||
|
||||
class BlockingObserver {
|
||||
shouldBlock;
|
||||
|
||||
_observerAdded = false;
|
||||
|
|
@ -23,7 +23,9 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
ChromeUtils.defineESModuleGetters(globalThis, {
|
||||
var EXPORTED_SYMBOLS = ["EPUB"];
|
||||
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
Zotero: "chrome://zotero/content/zotero.mjs",
|
||||
});
|
||||
|
||||
|
|
@ -36,7 +38,7 @@ const ZipReader = Components.Constructor(
|
|||
const DC_NS = 'http://purl.org/dc/elements/1.1/';
|
||||
const OPF_NS = 'http://www.idpf.org/2007/opf';
|
||||
|
||||
export class EPUB {
|
||||
class EPUB {
|
||||
_zipReader;
|
||||
|
||||
_contentOPF = null;
|
||||
|
|
@ -24,27 +24,30 @@
|
|||
*/
|
||||
|
||||
|
||||
import { BlockingObserver } from "chrome://zotero/content/BlockingObserver.mjs";
|
||||
import "chrome://zotero/content/actors/ActorManager.mjs";
|
||||
var EXPORTED_SYMBOLS = ["HiddenBrowser"];
|
||||
|
||||
/* global HiddenFrame, E10SUtils */
|
||||
ChromeUtils.defineESModuleGetters(globalThis, {
|
||||
E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
|
||||
HiddenFrame: "resource://gre/modules/HiddenFrame.sys.mjs",
|
||||
Zotero: "chrome://zotero/content/zotero.mjs",
|
||||
setTimeout: "resource://gre/modules/Timer.sys.mjs"
|
||||
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const { BlockingObserver } = ChromeUtils.import("chrome://zotero/content/BlockingObserver.jsm");
|
||||
|
||||
ChromeUtils.import("chrome://zotero/content/actors/ActorManager.jsm");
|
||||
|
||||
/* global HiddenFrame, E10SUtils, this */
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
E10SUtils: "resource://gre/modules/E10SUtils.jsm",
|
||||
HiddenFrame: "resource://gre/modules/HiddenFrame.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
setTimeout: "resource://gre/modules/Timer.jsm",
|
||||
});
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
Zotero: "chrome://zotero/content/zotero.mjs"
|
||||
});
|
||||
|
||||
const progressListeners = new Set();
|
||||
|
||||
// From nsSandboxFlags.h
|
||||
const SANDBOXED_ORIGIN = 0x10;
|
||||
const SANDBOXED_SCRIPTS = 0x80;
|
||||
|
||||
/**
|
||||
* Functions for creating and destroying hidden browser objects
|
||||
**/
|
||||
export class HiddenBrowser {
|
||||
class HiddenBrowser {
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {Boolean} [options.allowJavaScript]
|
||||
|
|
@ -52,13 +55,10 @@ export class HiddenBrowser {
|
|||
* @param {Boolean} [options.blockRemoteResources] Block all remote (non-file:) resources
|
||||
* @param {Boolean} [options.useHiddenFrame=true] Use a hidden frame to create the browser.
|
||||
* Must be set to false if intending to call print().
|
||||
* @param {Number} [options.userContextId] - From Zotero.HTTP.newCookieContext() for cookie isolation
|
||||
* @param {String} [options.customUserAgent] - Override User-Agent for all requests
|
||||
* from this browser's browsing context
|
||||
* @param {Zotero.CookieSandbox} [options.cookieSandbox]
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
this._destroyed = false;
|
||||
this._allowJavaScript = options.allowJavaScript !== false;
|
||||
this._createdPromise = (async () => {
|
||||
let doc;
|
||||
if (options.useHiddenFrame !== false) {
|
||||
|
|
@ -66,6 +66,7 @@ export class HiddenBrowser {
|
|||
this._frame = frame;
|
||||
|
||||
var windowlessBrowser = await frame.get();
|
||||
windowlessBrowser.browsingContext.allowJavascript = options.allowJavaScript !== false;
|
||||
windowlessBrowser.docShell.allowImages = false;
|
||||
if (options.docShell) {
|
||||
Object.assign(windowlessBrowser.docShell, options.docShell);
|
||||
|
|
@ -86,12 +87,17 @@ export class HiddenBrowser {
|
|||
browser.setAttribute("remote", "true");
|
||||
browser.setAttribute('maychangeremoteness', 'true');
|
||||
browser.setAttribute("disableglobalhistory", "true");
|
||||
if (options.userContextId) {
|
||||
browser.setAttribute("usercontextid", String(options.userContextId));
|
||||
}
|
||||
browser.style.display = "none";
|
||||
doc.documentElement.appendChild(browser);
|
||||
|
||||
|
||||
if (options.cookieSandbox) {
|
||||
options.cookieSandbox.attachToBrowser(browser);
|
||||
}
|
||||
|
||||
if (Zotero.Debug.enabled) {
|
||||
startLeakWarningTimer(browser);
|
||||
}
|
||||
|
||||
if (options.blockRemoteResources) {
|
||||
this._blockingObserver = new BlockingObserver({
|
||||
shouldBlock(uri) {
|
||||
|
|
@ -101,17 +107,6 @@ export class HiddenBrowser {
|
|||
this._blockingObserver.register(browser);
|
||||
}
|
||||
|
||||
if (options.customUserAgent) {
|
||||
browser.browsingContext.customUserAgent = options.customUserAgent;
|
||||
}
|
||||
|
||||
if (!this._allowJavaScript) {
|
||||
// A system-principal document (e.g., a blob: URL created by chrome code) can run
|
||||
// scripts even when scripting is otherwise disabled, so sandbox it with a null
|
||||
// principal and no scripts.
|
||||
browser.browsingContext.sandboxFlags |= SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS;
|
||||
}
|
||||
|
||||
this._browser = browser;
|
||||
})();
|
||||
|
||||
|
|
@ -126,16 +121,15 @@ export class HiddenBrowser {
|
|||
set(target, prop, val) {
|
||||
if (prop in target) {
|
||||
target[prop] = val;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return Reflect.set(target._browser, prop, val);
|
||||
}
|
||||
Reflect.set(target._browser, prop, val)
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} source - HTTP URL, file: URL, or file path
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.requireSuccessfulStatus]
|
||||
|
|
@ -155,12 +149,18 @@ export class HiddenBrowser {
|
|||
Zotero.debug(`Loading ${uri} in hidden browser`);
|
||||
// Next bit adapted from Mozilla's HeadlessShell.jsm
|
||||
try {
|
||||
// Figure out whether the browser should be remote
|
||||
let remoteType = ChromeUtils.predictRemoteTypeForURI(uri, {
|
||||
preferredRemoteType: E10SUtils.DEFAULT_REMOTE_TYPE,
|
||||
useRemoteTabs: true,
|
||||
useRemoteSubframes: false
|
||||
});
|
||||
// Figure out whether the browser should be remote. We actually
|
||||
// perform the load in PageDataChild, but remoteness changes
|
||||
// need to happen here.
|
||||
let oa = E10SUtils.predictOriginAttributes({ browser: this });
|
||||
let remoteType = E10SUtils.getRemoteTypeForURI(
|
||||
uri,
|
||||
true,
|
||||
false,
|
||||
E10SUtils.DEFAULT_REMOTE_TYPE,
|
||||
null,
|
||||
oa
|
||||
);
|
||||
if (this.remoteType !== remoteType) {
|
||||
// The following functions need to be called on the <browser> directly,
|
||||
// not through our proxy (aka 'this')
|
||||
|
|
@ -215,18 +215,12 @@ export class HiddenBrowser {
|
|||
);
|
||||
});
|
||||
|
||||
let prepared = await this.browsingContext.currentWindowGlobal.getActor("PageData")
|
||||
.sendQuery("prepareLoad");
|
||||
if (!prepared) {
|
||||
let loadURISuccess = await this.browsingContext.currentWindowGlobal.getActor("PageData")
|
||||
.sendQuery("loadURI", { uri });
|
||||
if (!loadURISuccess) {
|
||||
Zotero.logError(new Error("Load failed"));
|
||||
return false;
|
||||
}
|
||||
// Start the load from the parent process. A load started by the content process
|
||||
// is restricted to URLs that process could load itself, which excludes blob: URLs
|
||||
// created by chrome code.
|
||||
this._browser.loadURI(Services.io.newURI(uri), {
|
||||
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
|
||||
});
|
||||
await loadCompletePromise;
|
||||
}
|
||||
catch (e) {
|
||||
|
|
@ -240,7 +234,7 @@ export class HiddenBrowser {
|
|||
let response = `${channelInfo.responseStatus} ${channelInfo.responseStatusText}`;
|
||||
Zotero.debug(`HiddenBrowser.load: ${uri} failed with ${response}`, 2);
|
||||
// HiddenBrowser will never get returned so we need to clean it up here
|
||||
this.destroy();
|
||||
this.destroy()
|
||||
throw new Zotero.HTTP.UnexpectedStatusException(
|
||||
{
|
||||
status: channelInfo.responseStatus
|
||||
|
|
@ -250,38 +244,16 @@ export class HiddenBrowser {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number | false} [allowInteractiveAfter = false] Delay (in milliseconds) before resolving on 'interactive'.
|
||||
* If false, documentIsReady() won't resolve until 'complete'.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
waitForDocument({ allowInteractiveAfter = false } = {}) {
|
||||
return this.browsingContext.currentWindowGlobal.getActor('DocumentIsReady')
|
||||
.sendQuery('waitForDocument', { allowInteractiveAfter });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String[]} props - 'characterSet', 'title', 'bodyText', 'documentHTML', 'cookie', 'channelInfo'
|
||||
* @param {Object} [options]
|
||||
* @param {Number} [options.timeout=30000] - Time to wait for each property in milliseconds.
|
||||
* The queries wait for the document to be ready, so a page that never finishes loading
|
||||
* would otherwise hang the query forever.
|
||||
*/
|
||||
async getPageData(props, { timeout = 30000 } = {}) {
|
||||
async getPageData(props) {
|
||||
var actor = this.browsingContext.currentWindowGlobal.getActor("PageData");
|
||||
var data = {};
|
||||
for (let prop of props) {
|
||||
let timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(
|
||||
() => reject(new Error(`Timed out getting '${prop}' from hidden browser`)),
|
||||
timeout
|
||||
);
|
||||
});
|
||||
data[prop] = await Promise.race([actor.sendQuery(prop), timeoutPromise]);
|
||||
data[prop] = await actor.sendQuery(prop);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
|
@ -341,3 +313,21 @@ export class HiddenBrowser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startLeakWarningTimer(browser) {
|
||||
const CHECK_AFTER_SECONDS = 60;
|
||||
|
||||
// We need to use Cu.getWeakReference() to get an xpcIJSWeakReference here -
|
||||
// DOM WeakRefs, paradoxically, keep the browser alive
|
||||
let weakBrowser = Cu.getWeakReference(browser);
|
||||
browser = null;
|
||||
arguments.length = 0;
|
||||
|
||||
setTimeout(() => {
|
||||
let browser = weakBrowser.get();
|
||||
if (browser) {
|
||||
Zotero.debug(`Browser object still alive after ${CHECK_AFTER_SECONDS} seconds - memory leak?`);
|
||||
Zotero.debug('Viewing URI ' + browser.currentURI?.spec)
|
||||
}
|
||||
}, 1000 * CHECK_AFTER_SECONDS);
|
||||
}
|
||||
|
|
@ -23,14 +23,20 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
ChromeUtils.defineESModuleGetters(globalThis, {
|
||||
var EXPORTED_SYMBOLS = ["RemoteTranslate"];
|
||||
|
||||
ChromeUtils.import("chrome://zotero/content/actors/ActorManager.jsm");
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
Zotero: "chrome://zotero/content/zotero.mjs",
|
||||
ZOTERO_CONFIG: "resource://zotero/config.mjs",
|
||||
ActorManager: "chrome://zotero/content/actors/ActorManager.mjs",
|
||||
TranslationManager: "chrome://zotero/content/actors/TranslationParent.mjs",
|
||||
});
|
||||
|
||||
export class RemoteTranslate {
|
||||
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
TranslationManager: "chrome://zotero/content/actors/TranslationParent.jsm",
|
||||
ZOTERO_CONFIG: "resource://zotero/config.js",
|
||||
});
|
||||
|
||||
class RemoteTranslate {
|
||||
_browser = null;
|
||||
|
||||
_id = Zotero.Utilities.randomString();
|
||||
|
|
@ -80,7 +86,8 @@ export class RemoteTranslate {
|
|||
* Set a handler on the proxied Zotero.Translate instance.
|
||||
* The handler function is passed this RemoteTranslate as its first argument.
|
||||
*
|
||||
* Supports all Zotero.Translate handlers.
|
||||
* Supports all Zotero.Translate handlers in addition to newTestDetectionFailed, which can be called from
|
||||
* {@link newTest} if detection fails to confirm the creation of an expected-fail test.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} handler
|
||||
|
|
@ -241,13 +248,11 @@ export class RemoteTranslate {
|
|||
/**
|
||||
* Create a test on the browser's current page.
|
||||
*
|
||||
* @param {Object} [testInit]
|
||||
* @param {boolean | number} [testInit.defer]
|
||||
* @return {Promise<Object | null>} Resolves to the created test object (null on error)
|
||||
*/
|
||||
newTest(testInit) {
|
||||
newTest() {
|
||||
let actor = this._browser.browsingContext.currentWindowGlobal.getActor("Translation");
|
||||
return actor.sendQuery("newTest", { translator: this._translator, testInit, id: this._id });
|
||||
return actor.sendQuery("newTest", { translator: this._translator, id: this._id });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -27,12 +27,16 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
const EXPORTED_SYMBOLS = ['ZoteroProtocolHandler'];
|
||||
|
||||
const ZOTERO_SCHEME = "zotero";
|
||||
const ZOTERO_PROTOCOL_CID = Components.ID("{9BC3D762-9038-486A-9D70-C997AF848A7C}");
|
||||
const ZOTERO_PROTOCOL_CONTRACTID = "@mozilla.org/network/protocol;1?name=" + ZOTERO_SCHEME;
|
||||
const ZOTERO_PROTOCOL_NAME = "Zotero Chrome Extension Protocol";
|
||||
|
||||
import { NetUtil } from "resource://gre/modules/NetUtil.sys.mjs";
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/ComponentUtils.jsm");
|
||||
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
|
@ -42,9 +46,9 @@ const ios = Services.io;
|
|||
// Dummy chrome URL used to obtain a valid chrome channel
|
||||
const DUMMY_CHROME_URL = "chrome://zotero/content/zoteroPane.xul";
|
||||
|
||||
import { Zotero } from "chrome://zotero/content/zotero.mjs";
|
||||
var { Zotero } = ChromeUtils.importESModule("chrome://zotero/content/zotero.mjs");
|
||||
|
||||
export function ZoteroProtocolHandler() {
|
||||
function ZoteroProtocolHandler() {
|
||||
this.wrappedJSObject = this;
|
||||
this._principal = null;
|
||||
this._extensions = {};
|
||||
|
|
@ -63,13 +67,13 @@ export function ZoteroProtocolHandler() {
|
|||
loadAsChrome: false,
|
||||
|
||||
newChannel: function (uri, loadInfo) {
|
||||
return new AsyncChannel(uri, loadInfo, async function () {
|
||||
return new AsyncChannel(uri, loadInfo, function* () {
|
||||
try {
|
||||
var uriPath = uri.pathQueryRef;
|
||||
if (!uriPath) {
|
||||
return this._errorChannel('Invalid URL');
|
||||
}
|
||||
uriPath = uriPath.substring(1);
|
||||
uriPath = uriPath.substr('//attachment/'.length);
|
||||
|
||||
var params = {};
|
||||
var router = new Zotero.Router(params);
|
||||
|
|
@ -85,7 +89,7 @@ export function ZoteroProtocolHandler() {
|
|||
if (!params.itemKey) {
|
||||
return this._errorChannel("Item key not provided");
|
||||
}
|
||||
var item = await Zotero.Items.getByLibraryAndKeyAsync(params.libraryID, params.itemKey);
|
||||
var item = yield Zotero.Items.getByLibraryAndKeyAsync(params.libraryID, params.itemKey);
|
||||
|
||||
if (!item) {
|
||||
return this._errorChannel(`No item found for ${uriPath}`);
|
||||
|
|
@ -94,7 +98,7 @@ export function ZoteroProtocolHandler() {
|
|||
return this._errorChannel(`Item for ${uriPath} is not a file attachment`);
|
||||
}
|
||||
|
||||
var path = await item.getFilePathAsync();
|
||||
var path = yield item.getFilePathAsync();
|
||||
if (!path) {
|
||||
return this._errorChannel(`${path} not found`);
|
||||
}
|
||||
|
|
@ -114,7 +118,7 @@ export function ZoteroProtocolHandler() {
|
|||
Zotero.logError(e);
|
||||
return this._errorChannel(`Resource ${resourcePathParts.join('/')} not found`);
|
||||
}
|
||||
if (!(await IOUtils.exists(path))) {
|
||||
if (!(yield IOUtils.exists(path))) {
|
||||
return this._errorChannel(`Resource ${resourcePathParts.join('/')} not found`);
|
||||
}
|
||||
}
|
||||
|
|
@ -151,7 +155,7 @@ export function ZoteroProtocolHandler() {
|
|||
loadAsChrome: false,
|
||||
|
||||
newChannel: function (uri, loadInfo) {
|
||||
return new AsyncChannel(uri, loadInfo, async function () {
|
||||
return new AsyncChannel(uri, loadInfo, function* () {
|
||||
this.contentType = 'text/plain';
|
||||
|
||||
var path = uri.spec.match(/zotero:\/\/[^/]+(.*)/)[1];
|
||||
|
|
@ -163,7 +167,7 @@ export function ZoteroProtocolHandler() {
|
|||
}
|
||||
catch (e) {
|
||||
if (e instanceof Zotero.Router.InvalidPathException) {
|
||||
return "URL could not be parsed";
|
||||
return "URL could not be parsed";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -178,14 +182,14 @@ export function ZoteroProtocolHandler() {
|
|||
loadAsChrome: true,
|
||||
|
||||
newChannel: function (uri, loadInfo) {
|
||||
return new AsyncChannel(uri, loadInfo, async function () {
|
||||
return new AsyncChannel(uri, loadInfo, function* () {
|
||||
var userLibraryID = Zotero.Libraries.userLibraryID;
|
||||
|
||||
var path = uri.pathQueryRef;
|
||||
if (!path) {
|
||||
return 'Invalid URL';
|
||||
}
|
||||
path = path.substring(1);
|
||||
path = path.substr('//report/'.length);
|
||||
|
||||
// Proxy CSS files
|
||||
if (path.endsWith('.css')) {
|
||||
|
|
@ -266,7 +270,7 @@ export function ZoteroProtocolHandler() {
|
|||
|
||||
try {
|
||||
Zotero.API.parseParams(params);
|
||||
var results = await Zotero.API.getResultsFromParams(params);
|
||||
var results = yield Zotero.API.getResultsFromParams(params);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.debug(e, 1);
|
||||
|
|
@ -316,7 +320,7 @@ export function ZoteroProtocolHandler() {
|
|||
for (let id of searchItemIDs) {
|
||||
if (!searchChildIDs.has(id)) {
|
||||
var children = [];
|
||||
var item = await Zotero.Items.getAsync(id);
|
||||
var item = yield Zotero.Items.getAsync(id);
|
||||
if (!item.isRegularItem()) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -347,7 +351,7 @@ export function ZoteroProtocolHandler() {
|
|||
// Add parents of matches if parents aren't matches themselves
|
||||
for (let id of searchParentIDs) {
|
||||
if (!searchItemIDs.has(id) && !itemsHash[id]) {
|
||||
var item = await Zotero.Items.getAsync(id);
|
||||
var item = yield Zotero.Items.getAsync(id);
|
||||
itemsHash[id] = items.length;
|
||||
items.push(item.toJSON({ mode: 'full' }));
|
||||
}
|
||||
|
|
@ -355,7 +359,7 @@ export function ZoteroProtocolHandler() {
|
|||
|
||||
// Add children to reportChildren property of parents
|
||||
for (let id of searchChildIDs) {
|
||||
let item = await Zotero.Items.getAsync(id);
|
||||
let item = yield Zotero.Items.getAsync(id);
|
||||
var parentID = item.parentID;
|
||||
if (!items[itemsHash[parentID]].reportChildren) {
|
||||
items[itemsHash[parentID]].reportChildren = {
|
||||
|
|
@ -375,7 +379,7 @@ export function ZoteroProtocolHandler() {
|
|||
// for each matching child
|
||||
else {
|
||||
for (let id of searchChildIDs) {
|
||||
var item = await Zotero.Items.getAsync(id);
|
||||
var item = yield Zotero.Items.getAsync(id);
|
||||
var parentID = item.parentID;
|
||||
var parentItem = Zotero.Items.get(parentID);
|
||||
|
||||
|
|
@ -419,7 +423,7 @@ export function ZoteroProtocolHandler() {
|
|||
|
||||
|
||||
var collation = Zotero.getLocaleCollation();
|
||||
var compareFunction = function (a, b) {
|
||||
var compareFunction = function(a, b) {
|
||||
var index = 0;
|
||||
|
||||
// Multidimensional sort
|
||||
|
|
@ -540,6 +544,267 @@ export function ZoteroProtocolHandler() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate MIT SIMILE Timeline
|
||||
*
|
||||
* Query string key abbreviations: intervals = i
|
||||
* dateType = t
|
||||
* timelineDate = d
|
||||
*
|
||||
* interval abbreviations: day = d | month = m | year = y | decade = e | century = c | millennium = i
|
||||
* dateType abbreviations: date = d | dateAdded = da | dateModified = dm
|
||||
* timelineDate format: shortMonthName.day.year (year is positive for A.D. and negative for B.C.)
|
||||
*
|
||||
* Defaults: intervals = month, year, decade
|
||||
* dateType = date
|
||||
* timelineDate = today's date
|
||||
*/
|
||||
var TimelineExtension = {
|
||||
loadAsChrome: true,
|
||||
|
||||
newChannel: function (uri, loadInfo) {
|
||||
return new AsyncChannel(uri, loadInfo, function* () {
|
||||
var userLibraryID = Zotero.Libraries.userLibraryID;
|
||||
|
||||
var path = uri.spec.match(/zotero:\/\/[^/]+(.*)/)[1];
|
||||
if (!path) {
|
||||
this.contentType = 'text/html';
|
||||
return 'Invalid URL';
|
||||
}
|
||||
|
||||
var params = {};
|
||||
var router = new Zotero.Router(params);
|
||||
|
||||
// HTML
|
||||
router.add('library/:scopeObject/:scopeObjectKey', function () {
|
||||
params.libraryID = userLibraryID;
|
||||
params.controller = 'html';
|
||||
});
|
||||
router.add('groups/:groupID/:scopeObject/:scopeObjectKey', function () {
|
||||
params.controller = 'html';
|
||||
});
|
||||
router.add('library', function () {
|
||||
params.libraryID = userLibraryID;
|
||||
params.controller = 'html';
|
||||
});
|
||||
router.add('groups/:groupID', function () {
|
||||
params.controller = 'html';
|
||||
});
|
||||
|
||||
// Data
|
||||
router.add('data/library/:scopeObject/:scopeObjectKey', function () {
|
||||
params.libraryID = userLibraryID;
|
||||
params.controller = 'data';
|
||||
});
|
||||
router.add('data/groups/:groupID/:scopeObject/:scopeObjectKey', function () {
|
||||
params.controller = 'data';
|
||||
});
|
||||
router.add('data/library', function () {
|
||||
params.libraryID = userLibraryID;
|
||||
params.controller = 'data';
|
||||
});
|
||||
router.add('data/groups/:groupID', function () {
|
||||
params.controller = 'data';
|
||||
});
|
||||
|
||||
// Old-style HTML URLs
|
||||
router.add('collection/:id', function () {
|
||||
params.controller = 'html';
|
||||
params.scopeObject = 'collections';
|
||||
var lkh = Zotero.Collections.parseLibraryKeyHash(params.id);
|
||||
if (lkh) {
|
||||
params.libraryID = lkh.libraryID || userLibraryID;
|
||||
params.scopeObjectKey = lkh.key;
|
||||
}
|
||||
else {
|
||||
params.scopeObjectID = params.id;
|
||||
}
|
||||
delete params.id;
|
||||
});
|
||||
router.add('search/:id', function () {
|
||||
params.controller = 'html';
|
||||
params.scopeObject = 'searches';
|
||||
var lkh = Zotero.Searches.parseLibraryKeyHash(params.id);
|
||||
if (lkh) {
|
||||
params.libraryID = lkh.libraryID || userLibraryID;
|
||||
params.scopeObjectKey = lkh.key;
|
||||
}
|
||||
else {
|
||||
params.scopeObjectID = params.id;
|
||||
}
|
||||
delete params.id;
|
||||
});
|
||||
router.add('/', function () {
|
||||
params.controller = 'html';
|
||||
params.libraryID = userLibraryID;
|
||||
});
|
||||
|
||||
var parsed = router.run(path);
|
||||
if (!parsed) {
|
||||
this.contentType = 'text/html';
|
||||
return "URL could not be parsed";
|
||||
}
|
||||
if (params.groupID) {
|
||||
params.libraryID = Zotero.Groups.getLibraryIDFromGroupID(params.groupID);
|
||||
}
|
||||
|
||||
var intervals = params.i ? params.i : '';
|
||||
var timelineDate = params.d ? params.d : '';
|
||||
var dateType = params.t ? params.t : '';
|
||||
|
||||
// Get the collection or search object
|
||||
var collection, search;
|
||||
switch (params.scopeObject) {
|
||||
case 'collections':
|
||||
if (params.scopeObjectKey) {
|
||||
collection = yield Zotero.Collections.getByLibraryAndKeyAsync(
|
||||
params.libraryID, params.scopeObjectKey
|
||||
);
|
||||
}
|
||||
else {
|
||||
collection = yield Zotero.Collections.getAsync(params.scopeObjectID);
|
||||
}
|
||||
if (!collection) {
|
||||
this.contentType = 'text/html';
|
||||
return 'Invalid collection ID or key';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'searches':
|
||||
if (params.scopeObjectKey) {
|
||||
var s = yield Zotero.Searches.getByLibraryAndKeyAsync(
|
||||
params.libraryID, params.scopeObjectKey
|
||||
);
|
||||
}
|
||||
else {
|
||||
var s = yield Zotero.Searches.getAsync(params.scopeObjectID);
|
||||
}
|
||||
if (!s) {
|
||||
return 'Invalid search ID or key';
|
||||
}
|
||||
|
||||
// FIXME: Hack to exclude group libraries for now
|
||||
var search = new Zotero.Search();
|
||||
search.setScope(s);
|
||||
var groups = Zotero.Groups.getAll();
|
||||
for (let group of groups) {
|
||||
search.addCondition('libraryID', 'isNot', group.libraryID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Create XML file
|
||||
//
|
||||
if (params.controller == 'data') {
|
||||
switch (params.scopeObject) {
|
||||
case 'collections':
|
||||
var results = collection.getChildItems();
|
||||
break;
|
||||
|
||||
case 'searches':
|
||||
var ids = yield search.search();
|
||||
var results = yield Zotero.Items.getAsync(ids);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (params.scopeObject) {
|
||||
return "Invalid scope object '" + params.scopeObject + "'";
|
||||
}
|
||||
|
||||
let s = new Zotero.Search();
|
||||
s.addCondition('libraryID', 'is', params.libraryID);
|
||||
s.addCondition('noChildren', 'true');
|
||||
var ids = yield s.search();
|
||||
var results = yield Zotero.Items.getAsync(ids);
|
||||
}
|
||||
|
||||
var items = [];
|
||||
// Only include parent items
|
||||
for (let i=0; i<results.length; i++) {
|
||||
if (!results[i].parentItemID) {
|
||||
items.push(results[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var dateTypes = {
|
||||
d: 'date',
|
||||
da: 'dateAdded',
|
||||
dm: 'dateModified'
|
||||
};
|
||||
|
||||
//default dateType = date
|
||||
if (!dateType || !dateTypes[dateType]) {
|
||||
dateType = 'd';
|
||||
}
|
||||
|
||||
this.contentType = 'application/xml';
|
||||
return Zotero.Utilities.Internal.getAsyncInputStream(
|
||||
Zotero.Timeline.generateXMLDetails(items, dateTypes[dateType])
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Generate main HTML page
|
||||
//
|
||||
var content = Zotero.File.getContentsFromURL('chrome://zotero/skin/timeline/timeline.html');
|
||||
this.contentType = 'text/html';
|
||||
|
||||
if(!timelineDate){
|
||||
timelineDate=Date();
|
||||
var dateParts=timelineDate.toString().split(' ');
|
||||
timelineDate=dateParts[1]+'.'+dateParts[2]+'.'+dateParts[3];
|
||||
}
|
||||
if (!intervals || intervals.length < 3) {
|
||||
intervals += "mye".substr(intervals.length);
|
||||
}
|
||||
|
||||
var theIntervals = {
|
||||
d: 'Timeline.DateTime.DAY',
|
||||
m: 'Timeline.DateTime.MONTH',
|
||||
y: 'Timeline.DateTime.YEAR',
|
||||
e: 'Timeline.DateTime.DECADE',
|
||||
c: 'Timeline.DateTime.CENTURY',
|
||||
i: 'Timeline.DateTime.MILLENNIUM'
|
||||
};
|
||||
|
||||
//sets the intervals of the timeline bands
|
||||
var tempStr = '<body onload="onLoad(';
|
||||
var a = (theIntervals[intervals[0]]) ? theIntervals[intervals[0]] : 'Timeline.DateTime.MONTH';
|
||||
var b = (theIntervals[intervals[1]]) ? theIntervals[intervals[1]] : 'Timeline.DateTime.YEAR';
|
||||
var c = (theIntervals[intervals[2]]) ? theIntervals[intervals[2]] : 'Timeline.DateTime.DECADE';
|
||||
content = content.replace(tempStr, tempStr + a + ',' + b + ',' + c + ',\'' + timelineDate + '\'');
|
||||
|
||||
tempStr = 'document.write("<title>';
|
||||
if (params.scopeObject == 'collections') {
|
||||
content = content.replace(tempStr, tempStr + collection.name + ' - ');
|
||||
}
|
||||
else if (params.scopeObject == 'searches') {
|
||||
content = content.replace(tempStr, tempStr + search.name + ' - ');
|
||||
}
|
||||
else {
|
||||
content = content.replace(tempStr, tempStr + Zotero.getString('pane.collections.library') + ' - ');
|
||||
}
|
||||
|
||||
tempStr = 'Timeline.loadXML("zotero://timeline/data/';
|
||||
var d = '';
|
||||
if (params.groupID) {
|
||||
d += 'groups/' + params.groupID + '/';
|
||||
}
|
||||
else {
|
||||
d += 'library/';
|
||||
}
|
||||
if (params.scopeObject) {
|
||||
d += params.scopeObject + "/" + params.scopeObjectKey;
|
||||
}
|
||||
if (dateType) {
|
||||
d += '?t=' + dateType;
|
||||
}
|
||||
return content.replace(tempStr, tempStr + d);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Select an item
|
||||
|
|
@ -555,14 +820,15 @@ export function ZoteroProtocolHandler() {
|
|||
var SelectExtension = {
|
||||
noContent: true,
|
||||
|
||||
doAction: async function (uri) {
|
||||
doAction: Zotero.Promise.coroutine(function* (uri) {
|
||||
var userLibraryID = Zotero.Libraries.userLibraryID;
|
||||
|
||||
var path = uri.pathQueryRef;
|
||||
if (!path) {
|
||||
return 'Invalid URL';
|
||||
}
|
||||
path = path.substring(1);
|
||||
path = path.substr('//select/'.length);
|
||||
var mimeType, content = '';
|
||||
|
||||
var params = {
|
||||
objectType: 'item'
|
||||
|
|
@ -620,7 +886,7 @@ export function ZoteroProtocolHandler() {
|
|||
return;
|
||||
}
|
||||
|
||||
var results = await Zotero.API.getResultsFromParams(params);
|
||||
var results = yield Zotero.API.getResultsFromParams(params);
|
||||
|
||||
if (!results.length) {
|
||||
var msg = "Objects not found";
|
||||
|
|
@ -655,7 +921,7 @@ export function ZoteroProtocolHandler() {
|
|||
else {
|
||||
col = Zotero.Collections.get(params.scopeObjectID);
|
||||
}
|
||||
await zp.collectionsView.selectCollection(col.id);
|
||||
yield zp.collectionsView.selectCollection(col.id);
|
||||
}
|
||||
else if (params.scopeObject == 'searches') {
|
||||
let s;
|
||||
|
|
@ -667,15 +933,15 @@ export function ZoteroProtocolHandler() {
|
|||
else {
|
||||
s = Zotero.Searches.get(params.scopeObjectID);
|
||||
}
|
||||
await zp.collectionsView.selectSearch(s.id);
|
||||
yield zp.collectionsView.selectSearch(s.id);
|
||||
}
|
||||
// If collection not specified, select library root
|
||||
else {
|
||||
await zp.collectionsView.selectLibrary(params.libraryID);
|
||||
yield zp.collectionsView.selectLibrary(params.libraryID);
|
||||
}
|
||||
return zp.selectItems(results.map(x => x.id));
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
newChannel: function (uri) {
|
||||
this.doAction(uri);
|
||||
|
|
@ -689,7 +955,7 @@ export function ZoteroProtocolHandler() {
|
|||
loadAsChrome: false,
|
||||
|
||||
newChannel: function (uri, loadInfo) {
|
||||
return new AsyncChannel(uri, loadInfo, async function () {
|
||||
return new AsyncChannel(uri, loadInfo, function* () {
|
||||
this.contentType = "text/plain";
|
||||
|
||||
try {
|
||||
|
|
@ -703,6 +969,99 @@ export function ZoteroProtocolHandler() {
|
|||
}
|
||||
};
|
||||
|
||||
var ConnectorChannel = function(uri, data) {
|
||||
var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager);
|
||||
|
||||
this.name = uri;
|
||||
this.URI = ios.newURI(uri, "UTF-8", null);
|
||||
this.owner = (secMan.getCodebasePrincipal || secMan.getSimpleCodebasePrincipal)(this.URI);
|
||||
this._isPending = true;
|
||||
|
||||
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
this._stream = converter.convertToInputStream(data);
|
||||
this.contentLength = this._stream.available();
|
||||
}
|
||||
|
||||
ConnectorChannel.prototype.contentCharset = "UTF-8";
|
||||
ConnectorChannel.prototype.contentType = "text/html";
|
||||
ConnectorChannel.prototype.notificationCallbacks = null;
|
||||
ConnectorChannel.prototype.securityInfo = null;
|
||||
ConnectorChannel.prototype.status = 0;
|
||||
ConnectorChannel.prototype.loadGroup = null;
|
||||
ConnectorChannel.prototype.loadFlags = 393216;
|
||||
|
||||
ConnectorChannel.prototype.__defineGetter__("originalURI", function() { return this.URI });
|
||||
ConnectorChannel.prototype.__defineSetter__("originalURI", function() { });
|
||||
|
||||
ConnectorChannel.prototype.asyncOpen = function(streamListener) {
|
||||
if(this.loadGroup) this.loadGroup.addRequest(this, null);
|
||||
streamListener.onStartRequest(this);
|
||||
streamListener.onDataAvailable(this, this._stream, 0, this.contentLength);
|
||||
streamListener.onStopRequest(this, this.status);
|
||||
this._isPending = false;
|
||||
if(this.loadGroup) this.loadGroup.removeRequest(this, null, 0);
|
||||
}
|
||||
|
||||
ConnectorChannel.prototype.isPending = function() {
|
||||
return this._isPending;
|
||||
}
|
||||
|
||||
ConnectorChannel.prototype.cancel = function(status) {
|
||||
this.status = status;
|
||||
this._isPending = false;
|
||||
if(this._stream) this._stream.close();
|
||||
}
|
||||
|
||||
ConnectorChannel.prototype.suspend = function() {}
|
||||
|
||||
ConnectorChannel.prototype.resume = function() {}
|
||||
|
||||
ConnectorChannel.prototype.open = function() {
|
||||
return this._stream;
|
||||
}
|
||||
|
||||
ConnectorChannel.prototype.QueryInterface = function(iid) {
|
||||
if (!iid.equals(Components.interfaces.nsIChannel) && !iid.equals(Components.interfaces.nsIRequest) &&
|
||||
!iid.equals(Components.interfaces.nsISupports)) {
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* zotero://connector/
|
||||
*
|
||||
* URI spoofing for transferring page data across boundaries
|
||||
*/
|
||||
var ConnectorExtension = new function() {
|
||||
this.loadAsChrome = false;
|
||||
|
||||
this.newChannel = function(uri) {
|
||||
var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager);
|
||||
var Zotero = Components.classes["@zotero.org/Zotero;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
|
||||
try {
|
||||
var originalURI = uri.pathQueryRef.substr('zotero://connector/'.length);
|
||||
originalURI = decodeURIComponent(originalURI);
|
||||
if(!Zotero.Server.Connector.Data[originalURI]) {
|
||||
return null;
|
||||
} else {
|
||||
return new ConnectorChannel(originalURI, Zotero.Server.Connector.Data[originalURI]);
|
||||
}
|
||||
} catch(e) {
|
||||
Zotero.debug(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
zotero://pdf.js/viewer.html
|
||||
zotero://pdf.js/pdf/1/ABCD5678
|
||||
|
|
@ -711,7 +1070,7 @@ export function ZoteroProtocolHandler() {
|
|||
loadAsChrome: true,
|
||||
|
||||
newChannel: function (uri) {
|
||||
return new AsyncChannel(uri, async function () {
|
||||
return new AsyncChannel(uri, function* () {
|
||||
try {
|
||||
uri = uri.spec;
|
||||
// Proxy PDF.js files
|
||||
|
|
@ -729,11 +1088,11 @@ export function ZoteroProtocolHandler() {
|
|||
var [libraryID, key] = uri.substr(pdfPrefix.length).split('/');
|
||||
libraryID = parseInt(libraryID);
|
||||
|
||||
var item = await Zotero.Items.getByLibraryAndKeyAsync(libraryID, key);
|
||||
var item = yield Zotero.Items.getByLibraryAndKeyAsync(libraryID, key);
|
||||
if (!item) {
|
||||
return this._errorChannel("Item not found");
|
||||
}
|
||||
var path = await item.getFilePathAsync();
|
||||
var path = yield item.getFilePathAsync();
|
||||
if (!path) {
|
||||
return this._errorChannel("File not found");
|
||||
}
|
||||
|
|
@ -787,7 +1146,7 @@ export function ZoteroProtocolHandler() {
|
|||
if (!uriPath) {
|
||||
return 'Invalid URL';
|
||||
}
|
||||
uriPath = uriPath.substring(1);
|
||||
uriPath = uriPath.replace(/^\/\/open(-pdf)?\//, '');
|
||||
|
||||
var params = {
|
||||
objectType: 'item'
|
||||
|
|
@ -838,7 +1197,7 @@ export function ZoteroProtocolHandler() {
|
|||
var location = {};
|
||||
|
||||
if (page) {
|
||||
location.pageIndex = parseInt(page) - 1;
|
||||
location.pageIndex = parseInt(page);
|
||||
}
|
||||
if (annotation) {
|
||||
location.annotationID = annotation;
|
||||
|
|
@ -887,8 +1246,10 @@ export function ZoteroProtocolHandler() {
|
|||
this._extensions[ZOTERO_SCHEME + "://attachment"] = AttachmentExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://data"] = DataExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://report"] = ReportExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://timeline"] = TimelineExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://select"] = SelectExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://debug"] = DebugExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://connector"] = ConnectorExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://pdf.js"] = PDFJSExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://open"] = OpenExtension;
|
||||
this._extensions[ZOTERO_SCHEME + "://open-pdf"] = OpenExtension;
|
||||
|
|
@ -1021,14 +1382,14 @@ ZoteroProtocolHandler.init = function () {
|
|||
|
||||
|
||||
/**
|
||||
* nsIChannel implementation that takes an async function that returns a
|
||||
* nsIChannel implementation that takes a promise-yielding generator that returns a
|
||||
* string, nsIAsyncInputStream, or file
|
||||
*/
|
||||
function AsyncChannel(uri, loadInfo, func) {
|
||||
function AsyncChannel(uri, loadInfo, gen) {
|
||||
this.URI = this.originalURI = uri;
|
||||
this.loadInfo = loadInfo;
|
||||
|
||||
this._function = func;
|
||||
this._generator = gen;
|
||||
this._isPending = true;
|
||||
|
||||
// nsIRequest
|
||||
|
|
@ -1050,7 +1411,7 @@ function AsyncChannel(uri, loadInfo, func) {
|
|||
}
|
||||
|
||||
AsyncChannel.prototype = {
|
||||
asyncOpen: async function (streamListener) {
|
||||
asyncOpen: Zotero.Promise.coroutine(function* (streamListener) {
|
||||
if (this.loadGroup) this.loadGroup.addRequest(this, null);
|
||||
|
||||
var channel = this;
|
||||
|
|
@ -1095,17 +1456,17 @@ AsyncChannel.prototype = {
|
|||
var data;
|
||||
try {
|
||||
if (!data) {
|
||||
data = await channel._function();
|
||||
data = yield Zotero.spawn(channel._generator, channel)
|
||||
}
|
||||
if (typeof data == 'string') {
|
||||
//Zotero.debug("AsyncChannel: Got string from generator");
|
||||
|
||||
listenerWrapper.onStartRequest(this);
|
||||
|
||||
let inputStream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
inputStream.setUTF8Data(data);
|
||||
|
||||
let converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let inputStream = converter.convertToInputStream(data);
|
||||
listenerWrapper.onDataAvailable(this, inputStream, 0, inputStream.available());
|
||||
|
||||
listenerWrapper.onStopRequest(this, this.status);
|
||||
|
|
@ -1136,10 +1497,11 @@ AsyncChannel.prototype = {
|
|||
uri.QueryInterface(Ci.nsIURL);
|
||||
this.contentType = Zotero.MIME.getMIMETypeFromExtension(uri.fileExtension);
|
||||
if (!this.contentType) {
|
||||
let sample = await Zotero.File.getSample(uri.spec);
|
||||
let sample = yield Zotero.File.getSample(uri.spec);
|
||||
this.contentType = Zotero.MIME.getMIMETypeFromData(sample);
|
||||
}
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
NetUtil.asyncFetch({ uri: data, loadUsingSystemPrincipal: true }, function (inputStream, status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
reject();
|
||||
|
|
@ -1182,7 +1544,7 @@ AsyncChannel.prototype = {
|
|||
}
|
||||
catch (e) {}
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
// nsIRequest
|
||||
isPending: function () {
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
onload="moveToAlertPosition(); sizeToContent(); document.getElementById('zotero-about').getButton('accept').focus();"
|
||||
inwindowmenu="false">
|
||||
<script>
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);
|
||||
</script>
|
||||
|
|
@ -41,7 +42,7 @@
|
|||
<p id="third-party-software"></p>
|
||||
<p id="get-involved"></p>
|
||||
<script><![CDATA[
|
||||
const { ZOTERO_CONFIG } = ChromeUtils.importESModule('resource://zotero/config.mjs');
|
||||
Components.utils.import("resource://zotero/config.js");
|
||||
|
||||
document.getElementById('changelog').href = ZOTERO_CONFIG.CHANGELOG_URL;
|
||||
|
||||
|
|
|
|||
65
chrome/content/zotero/actors/ActorManager.jsm
Normal file
65
chrome/content/zotero/actors/ActorManager.jsm
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
var EXPORTED_SYMBOLS = [];
|
||||
|
||||
// Register Mozilla actors
|
||||
ChromeUtils.importESModule("resource://gre/modules/ActorManagerParent.sys.mjs");
|
||||
|
||||
ChromeUtils.registerWindowActor("PageData", {
|
||||
child: {
|
||||
moduleURI: "chrome://zotero/content/actors/PageDataChild.jsm"
|
||||
}
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("SingleFile", {
|
||||
child: {
|
||||
moduleURI: "chrome://zotero/content/actors/SingleFileChild.jsm"
|
||||
}
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("Translation", {
|
||||
parent: {
|
||||
moduleURI: "chrome://zotero/content/actors/TranslationParent.jsm"
|
||||
},
|
||||
child: {
|
||||
moduleURI: "chrome://zotero/content/actors/TranslationChild.jsm"
|
||||
}
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("FeedAbstract", {
|
||||
parent: {
|
||||
moduleURI: "chrome://zotero/content/actors/FeedAbstractParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "chrome://zotero/content/actors/FeedAbstractChild.jsm",
|
||||
events: {
|
||||
DOMDocElementInserted: {},
|
||||
}
|
||||
},
|
||||
messageManagerGroups: ["feedAbstract"]
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("ZoteroPrint", {
|
||||
parent: {
|
||||
moduleURI: "chrome://zotero/content/actors/ZoteroPrintParent.jsm"
|
||||
},
|
||||
child: {
|
||||
moduleURI: "chrome://zotero/content/actors/ZoteroPrintChild.jsm",
|
||||
events: {
|
||||
pageshow: {}
|
||||
}
|
||||
},
|
||||
allFrames: true
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("ExternalLinkHandler", {
|
||||
parent: {
|
||||
moduleURI: "chrome://zotero/content/actors/ExternalLinkHandlerParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "chrome://zotero/content/actors/ExternalLinkHandlerChild.jsm",
|
||||
events: {
|
||||
click: {},
|
||||
}
|
||||
},
|
||||
messageManagerGroups: ["feedAbstract", "basicViewer"]
|
||||
});
|
||||
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
// Register Mozilla actors
|
||||
import "resource://gre/modules/ActorManagerParent.sys.mjs";
|
||||
|
||||
ChromeUtils.registerWindowActor("PageData", {
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/PageDataChild.mjs"
|
||||
}
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("SingleFile", {
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/SingleFileChild.mjs"
|
||||
}
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("Translation", {
|
||||
parent: {
|
||||
esModuleURI: "chrome://zotero/content/actors/TranslationParent.mjs"
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/TranslationChild.mjs"
|
||||
}
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("FeedAbstract", {
|
||||
parent: {
|
||||
esModuleURI: "chrome://zotero/content/actors/FeedAbstractParent.mjs",
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/FeedAbstractChild.mjs",
|
||||
events: {
|
||||
DOMDocElementInserted: {},
|
||||
}
|
||||
},
|
||||
messageManagerGroups: ["feedAbstract"]
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("ZoteroPrint", {
|
||||
parent: {
|
||||
esModuleURI: "chrome://zotero/content/actors/ZoteroPrintParent.mjs"
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/ZoteroPrintChild.mjs",
|
||||
events: {
|
||||
pageshow: {}
|
||||
}
|
||||
},
|
||||
allFrames: true
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("ExternalLinkHandler", {
|
||||
parent: {
|
||||
esModuleURI: "chrome://zotero/content/actors/ExternalLinkHandlerParent.mjs",
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/ExternalLinkHandlerChild.mjs",
|
||||
events: {
|
||||
click: {},
|
||||
}
|
||||
},
|
||||
messageManagerGroups: ["feedAbstract", "basicViewer"]
|
||||
});
|
||||
|
||||
// On macOS only, register the Ctrl-Enter handler actor
|
||||
// (No access to Zotero object here)
|
||||
if (AppConstants.platform === "macosx") {
|
||||
ChromeUtils.registerWindowActor("SequoiaContextMenu", {
|
||||
parent: {
|
||||
esModuleURI: "chrome://zotero/content/actors/SequoiaContextMenuParent.mjs",
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/SequoiaContextMenuChild.mjs",
|
||||
},
|
||||
allFrames: true,
|
||||
includeChrome: true
|
||||
});
|
||||
}
|
||||
|
||||
ChromeUtils.registerWindowActor("MendeleyAuth", {
|
||||
parent: {
|
||||
esModuleURI: "chrome://zotero/content/actors/MendeleyAuthParent.mjs"
|
||||
},
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/MendeleyAuthChild.mjs"
|
||||
}
|
||||
});
|
||||
|
||||
ChromeUtils.registerWindowActor("DocumentIsReady", {
|
||||
child: {
|
||||
esModuleURI: "chrome://zotero/content/actors/DocumentIsReadyChild.mjs"
|
||||
}
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue