Compare commits

...

1814 commits
7.0.16 ... main

Author SHA1 Message Date
Tom Najdek
44fe83d98b
Update ftl-tx
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-10-10 00:06:33 +02:00
Dan Stillman
d18d2d1701 Run the Windows CI tests when the Windows build files change
Changes in app/win/ (e.g., updated zotero.exe and updater.exe) that
didn't touch config.sh wouldn't trigger the Windows launch test.
2026-10-09 16:14:16 -04:00
Dan Stillman
b0db83d9dc Keep reconnecting to the streaming server after a 4429 close
A 4429 (Too Many Requests) close stopped streaming until restart along
with the other 44xx codes. Retry it on the normal backoff schedule.
2026-10-09 14:47:10 -04:00
Dan Stillman
a69b55ea96 Mark the first request when using Replace Online Library
Add a restoreToServer=1 query parameter so we can identify restores
server-side if necessary
2026-10-09 14:46:19 -04:00
Dan Stillman
5336c3562b Update English strings for Transifex 2026-10-09 14:41:10 -04:00
Dan Stillman
734e2e6840 Update locales from Transifex 2026-10-09 14:40:23 -04:00
Dan Stillman
b551fb6705 Add Unrelate Items to item context menu
Shown when two or more selected items are related to each other.
Removes relations among the selected items only.

https://forums.zotero.org/discussion/comment/519130/#Comment_519130
2026-10-09 13:28:48 -04:00
Dan Stillman
8cf0203bb9 Use "Relate Items" as the undo label for Relate Items 2026-10-09 13:28:47 -04:00
Dan Stillman
d0a728bb78 Update LibreOffice submodule
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
2026-10-08 14:21:27 -04:00
Dan Stillman
c280964d46 Name the parent folder in data directory access-denied error 2026-10-08 12:53:38 -04:00
Dan Stillman
338ce732bd Show correct access-denied error when data directory can't be created
The OS.File.makeDir() shim swallowed IOUtils errors, so a failure to
create the data directory showed as a generic "database cannot be
opened" error. For the default location, just explain and quit instead
of offering to choose another directory.

https://forums.zotero.org/discussion/134124/zotero-not-working-on-a-mac
2026-10-08 12:50:58 -04:00
Dan Stillman
9cbba8c4d2 Local API: Decode + as a space in form-encoded requests
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
File uploads that encoded spaces as + were stored with literal +
characters in the filename.

https://forums.zotero.org/discussion/134038/
2026-10-06 11:33:29 -04:00
Dan Stillman
fb4bed7522 Fix typo in MDPI challenge comment 2026-10-06 10:52:57 -04:00
Dan Stillman
1e2cc6820a Tweak comments for MDPI challenge workaround 2026-10-06 10:50:08 -04:00
Dan Stillman
09c3c8cd3d Clear MDPI's Akamai bot challenge during Find Full Text
MDPI serves a JS proof-of-work interstitial in place of the article
page, so Find Full Text found nothing. Run the challenge in a hidden
browser when a page's meta refresh points to a registered challenge
host, then retry the page.

Also match meta refresh URL= case-insensitively, since MDPI's is
uppercase, and bound meta refreshes by the redirect limit.

https://forums.zotero.org/discussion/132837/
https://forums.zotero.org/discussion/134079/
2026-10-06 10:36:20 -04:00
Dan Stillman
9071805bdc Shorten the default HTTP retry window
Some checks are pending
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Detect changes (push) Waiting to run
An hour of retries makes sense for syncing, which runs automatically and
can just spin during server maintenance rather than showing errors that
send people to the forums, but it was also inherited by foreground
requests, where it stalled operations the user was waiting on. Sync and
file syncing now ask for the long window explicitly.
2026-10-05 15:31:24 -04:00
Dan Stillman
cdf10bf62b Don't stall the Find Full Text queue on a failing download
A file URL returning a server error was retried for up to an hour inside
the download, and a 429 or Retry-After was waited out there too. Since
the queue processes one item at a time, that blocked every other
selected item. Throttling now goes to Find Full Text's own per-domain
handling, as it did before 10.0, which also needed to read Retry-After
from a fetch Response and parse HTTP-date values.

https://forums.zotero.org/discussion/133703/
2026-10-05 15:25:25 -04:00
Dan Stillman
a6e814417f Stop retrying indefinitely on a repeated Retry-After
Retry-After was honored unconditionally with no attempt limit, so a server
returning 429 or 503 with the header on every request retried forever.
Retry-After waits and backoff intervals now share the errorDelayMax
budget, with each Retry-After counted as at least a second so that a
value of 0 can't loop forever.
2026-10-05 15:23:57 -04:00
Dan Stillman
d81484d2e8 Limit timeout and retries for open-access PDF lookup
This runs during Find Full Text and connector saves, where the default
30-second timeout and hour of 5xx retries are far longer than a user
wants to wait.
2026-10-05 15:09:14 -04:00
Dan Stillman
9b769d3987 Don't retry failing custom Find Full Text resolvers
A custom resolver returning a 429/5xx inherited Zotero.HTTP's default
retry policy, which could cause the whole queue to stall for up to an
hour -- or indefinitely for a Retry-After -- on a dead resolver, despite
the 5-second timeout on the request.

https://forums.zotero.org/discussion/133642/
2026-10-05 15:08:34 -04:00
Dan Stillman
364181f4e7 Send cookies and Referer from HTTP.download()
Since the switch to fetch() in 0fe31b0f04 (Zotero 10.0.0), download
requests didn't use cookies, and the Referer header was silently dropped
as a forbidden header. Sites that check either -- e.g., IEEE Xplore,
which returns a 502 -- failed during Find Full Text.

https://forums.zotero.org/discussion/133703/
2026-10-05 14:18:34 -04:00
Thenewmanator15
ba2d6ecee4
Fix plugin loading on Firefox 153.3 (untrusted URI) (#6058)
As of Firefox 153.3.0esr, Services.scriptloader refuses jar:file: and file:
URIs unless allowUnsafeURL is passed (Mozilla bug 1974213), so no plugin
loads: bootstrap.js fails with "Trying to load untrusted URI", then
"Plugin ... is missing bootstrap method 'startup'".

Pass allowUnsafeURL when loading a plugin's bootstrap.js and prefs.js,
and preference pane scripts, and temporarily enable
security.allow_unsafe_subscript_loads, which covers loads from plugin
code outside the bootstrap scope.

Also add a test that installs a fixture plugin that loads a script from its XPI
and sets a default pref.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-10-05 10:51:07 -04:00
Dan Stillman
15f6a81181 Refresh tag selector when a tag's type changes between views
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
Switching to a view that has the same tags but with different types
(manual vs. automatic) kept the previous list, so with "Show Automatic"
off a tag could show or hide incorrectly.
2026-10-02 12:38:58 -04:00
Dan Stillman
d8382c5c11 Fix manual tags missing from tag selector with automatic tags hidden
If a view contained manual and automatic tags with the same name, only
one was kept, and it would disappear with "Show Automatic" off.

https://forums.zotero.org/discussion/133869/
2026-10-02 12:38:53 -04:00
Dan Stillman
ae50d52589 Ignore invalid display directories in FilePicker
Some checks are pending
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
As of Firefox 140.17/153.4 (Bug 2068406), setting nsIFilePicker's
displayDirectory to a directory that doesn't exist or isn't readable
throws instead of being ignored. This broke callers that pass a saved
path that may be stale (e.g., choosing a PDF/EPUB handler after the old
app's folder was removed, or a missing Scaffold translators directory).
2026-10-01 16:37:58 -04:00
Dan Stillman
4118f47bc3 Mac build: Resolve Firefox source path to absolute path
build-and-unify changes into a temp directory before unifying, so a
relative path (e.g., `.`) broke the mach and objdir references.
2026-10-01 15:20:19 -04:00
Dan Stillman
9e234ee64f Mac build: Add Rust targets to the pinned toolchain
`rustup target add` was run before `rustup default`, so targets were
added to the previous default toolchain rather than $RUST_VERSION.
2026-10-01 15:20:19 -04:00
Dan Stillman
0d0bbc8925 Fix missing action buttons in RTF Scan's Verify Cited Items table
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
The action column's width was given as "32px", which VirtualizedTable
now turns into an invalid CSS width, collapsing the column and hiding
the buttons needed to resolve unmapped and ambiguous citations. Also
restore the accept-match icon on ambiguous-citation candidates.

https://forums.zotero.org/discussion/133740/
2026-09-29 12:51:07 -04:00
Dan Stillman
d7751b93d8 Don't match an item by a trashed child item in a search
A condition that matched a child item in the trash rolled up to its
parent when the search had a top-level (or other ancestor) result level,
or when "Include parent and child items of matching items" was checked.

https://forums.zotero.org/discussion/133836/
2026-09-29 11:34:37 -04:00
Dan Stillman
ebdc1287ac Await saving of recognized parent item before moving attachment under it
A standalone PDF recognized by ISBN wasn't moved under the new parent
item, because the move was attempted before the item had been saved
and had an ID.

https://forums.zotero.org/discussion/133957/
2026-09-29 11:33:12 -04:00
Dan Stillman
34000fb29f Fix partially applied upgrades after a transaction timeout
A userdata upgrade that took more than 5 minutes (e.g., dropping a large
legacy word index) was rolled back by Sqlite.sys.mjs, while its remaining
statements autocommitted and marked the database as upgraded, leaving
steps 126 and 127 missing.

Disable the transaction timeout and replace steps 122-129 (added in
Zotero 7, 9, and 10) with step 130, which checks for each change before
making it. To speed up the upgrade, drop the legacy word index without
overwriting the freed pages, since the full text already exists on disk
and we're just rewriting it in fulltext.sqlite.

https://forums.zotero.org/discussion/133859/zotero-connector-in-chrome-not-working
https://forums.zotero.org/discussion/133965/citation-saving-does-not-work-with-zotero-connector-firefox
2026-09-29 11:11:17 -04:00
Dan Stillman
f8ef8e2119 Match native pop-up button metrics for menulists on macOS 26+
Some checks are pending
CI / Build, Upload (push) Waiting to run
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
Firefox has AppKit draw the menulist's capsule and chevron, but it sizes
the box and positions the label itself, using a fixed dropdown border
and the label's CSS margins. On macOS 27 (and probably 26), that left the
label closer to the edges and the chevron than in a native NSPopUpButton.
It also made the box 26.5px tall. Firefox assumes regular pop-up buttons
are 22px tall and only draws them unscaled in boxes up to 2px taller, so
it drew the control at 22px and scaled the image up, enlarging the
capsule and chevron.

Add padding to match native label insets, and trim the label's vertical
margins to keep the box at 24px, so Firefox draws the control unscaled.
The result measures the same as a native NSPopUpButton.
2026-09-28 14:49:08 -04:00
Dan Stillman
9ea080f896 Update Mac launcher and libmozglue for Firefox 153.3.0esr
Build the launcher with the macOS 26.5 SDK. AppKit chooses control metrics
based on the main executable's SDK, so with the old 15.5 launcher, native
buttons and menulists used pre-Tahoe metrics with built-in margins while
Mozilla's XUL (built with 26.5) stopped compensating for them on macOS 26+
(bug 1992898), leaving labels cramped and controls indented.

Also add custom libmozglue.dylib (#6056) and set source info in mozconfig,
which official builds require and which Mozilla only detects automatically
from Mercurial checkouts.
2026-09-28 13:58:47 -04:00
Dan Stillman
df78b51dab Re-sign Mozilla helper apps in unsigned Mac builds with custom components
Mozilla's helper apps (GPU, content, etc.) use the hardened runtime and
Mozilla's Team ID, so in unsigned builds they couldn't load our custom
libmozglue.dylib and failed to launch. Re-sign them ad hoc.
2026-09-28 13:56:59 -04:00
Abe Jellinek
a64e367160
Patch libmozglue to prevent local voices from clicking on macOS 27 (#6056)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-09-28 11:44:16 -04:00
Dan Stillman
196bec4e7d Update Mozilla patch for fx153 for macOS 2026-09-28 11:28:58 -04:00
Dan Stillman
c694e88ec3 Revert "Fix cramped button and menulist labels on macOS 26+"
This reverts commit fcbc6ec1d5.
2026-09-28 11:27:41 -04:00
Dan Stillman
fcbc6ec1d5 Fix cramped button and menulist labels on macOS 26+
Firefox stopped inflating native controls on Tahoe (bug 1992898), but
still uses the old widget border sizes, so labels nearly touch the edges
of the new capsule-shaped buttons and run into menulist arrows.
2026-09-28 10:31:30 -04:00
Dan Stillman
e863643a43 Use curl instead of wget in build scripts
Some checks failed
CI / Build, Upload (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
curl is already required, so drop wget as a build requirement.
build_autoupdate.sh now checks the HTTP status code for its ETag cache
instead of relying on wget not creating the file on a 304.
2026-09-26 14:31:03 -04:00
Dan Stillman
8c3b967bf9 Support notarytool keychain profiles for notarization
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
If NOTARIZATION_PROFILE is set, notarize_mac_app and notarization_info
authenticate with that stored notarytool profile (e.g., an App Store
Connect API key) instead of an Apple ID and app-specific password,
unlocking the keychain first if needed.
2026-09-25 15:03:45 -04:00
Dan Stillman
53ded5eb44 Update reader submodule 2026-09-25 13:46:40 -04:00
Dan Stillman
cc951f9916 Rebuild Windows zotero.exe/updater.exe stubs from Firefox 153.3.0esr
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
153.3.0esr updated the Chromium sandbox, changing the TargetConfig
interface that xul.dll calls into the launcher's sandbox broker
through. The stubs were still built from 153.0esr, so the 153.3.0esr
xul.dll called the wrong methods and crashed on startup with
"config->SetProcessMitigations(initialMitigations) failed".
2026-09-24 13:37:25 -04:00
Dan Stillman
5237f27060 Update builds to Firefox 153.3.0esr 2026-09-24 09:37:15 -04:00
Martynas Bagdonas
3ff64b658d Fix virtualized table scrollbar width oscillation
The item table mixed fractional and integer widths to calculate scrollbar
padding. At certain pane sizes, changing that padding changed the table
width, causing ResizeObserver to measure and update it again. The loop made
the Info pane and PDF tab flicker and kept CPU busy while idle.

Calculate scrollbar width from matching integer measurements to stop the
feedback loop.

Cause of the reproduced loop: 7df21ed introduced the mixed-width
calculation; f3328b0 made ResizeObserver rerender the table immediately.

Potentially fixes these reports:
- https://forums.zotero.org/discussion/133865/pdf-reader-flickers-shakes-trembles-macos
- https://forums.zotero.org/discussion/133718/bug-after-updating-to-zotero-10-0-2-64-bit-on-windows
- https://forums.zotero.org/discussion/133764/zotero-10-0-2-high-cpu-usage-with-pdf-right-sidebar-open
2026-09-24 09:22:16 +01:00
Abe Jellinek
ed8ca1bfce Item merging: Don't merge web link attachments with different URLs
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
https://forums.zotero.org/discussion/133795
2026-09-23 13:53:08 -04:00
Dan Stillman
22a26bb483 Local API: Fix object writes with API-client-supplied keys
Requests that included a key got a 400 with `'primaryData' not
loaded for item`
2026-09-23 13:21:52 -04:00
Dan Stillman
4e3b208e4a Don't log an error when no bibliography locale is set
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
In the error console in https://forums.zotero.org/discussion/133857/
2026-09-22 16:31:42 -04:00
Dan Stillman
5245b6cf5e Bundle Safari App Extension in release builds
The release script never set SAFARI_APP_EXTENSION, so 10.0 through
10.0.3 shipped with only the web extension, which doesn't run on Big Sur
or Monterey. Only beta builds have been including both.

https://forums.zotero.org/discussion/133871/
2026-09-22 16:14:03 -04:00
Dan Stillman
e061e565b7 Update release script to 10.0 branch 2026-09-22 16:07:56 -04:00
Dan Stillman
8e7639d73d Show virtual rows (e.g., Trash) matching the collection filter
https://forums.zotero.org/discussion/133833/cant-show-trash-via-collections-search
2026-09-21 23:26:03 -04:00
windingwind
7c7560bad1
Fix fcntl() flags not being passed by Subprocess on macOS (#6051)
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
This showed up as devtools failing to start in a -d build on some machines, but any Subprocess.call() could fail because of it.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-09-21 15:05:34 -04:00
Dan Stillman
b6f0f6c0ba Show login manager error when a credential write fails outside the keystore
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
The login manager can fail to store a value even when the OS keystore
works -- e.g., if key4.db is read-only, which Firefox 153 triggers
because NSS now creates a new AES key on the first write. Show the
existing corrupted-logins instructions instead of the keystore alert or
the unencrypted-storage offer, neither of which can help.

Also update the corrupted-logins dialog to add an "Open Profile
Directory" button
2026-09-17 10:37:04 -04:00
Adomas Venčkauskas
213b0d7d76 Update Word for Mac submodule 2026-09-17 12:24:16 +03:00
Dan Stillman
acc4fd5ad2 Pass a temp copy of dragged attachment files on Windows
Allowing only 'copy' for file attachment drags kept File Explorer from
moving files out of 'storage' but locked the cursor at '+' even for
moves within Zotero. Instead, provide the file via a flavor data
provider that copies it to the temp directory when a target asks for it,
so the drag can allow 'copyMove' again and Explorer's move only touches
the copy. A copy's directory is removed once Explorer has moved the file
out of it, at the next drag, or with the temp directory at shutdown.
2026-09-16 22:39:03 -04:00
Dan Stillman
bdb656ad5c Fix dragging file attachments to and from parent items on Windows
Since 56eb77b704, drags of file attachments allow only 'copy' so that
File Explorer doesn't move the file out of storage, but the trees set
dropEffect to 'move' in onDragOver() for drops within Zotero, and OLE
refuses a drop whose dropEffect isn't among the drag's allowed effects.
Have setDropEffect() fall back to an allowed effect and have onDrop()
act on the effect the tree chose, kept in
Zotero.DragDrop.currentDropEffect, rather than on the drop event's
dropEffect.

https://forums.zotero.org/discussion/133765/
2026-09-16 22:38:23 -04:00
Dan Stillman
c30163ed90 Fix row contents shifting when showing a between-rows drop indicator
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
The drop indicator span was inserted before the cells, so the first
cell stopped matching :first-child and picked up inline-start padding.
Insert it after the cells instead.

Regression from 5ca1fbb167, which replaced the .first-column class with
:first-child.
2026-09-16 15:44:05 -04:00
Dan Stillman
1d4b8bc89a Fix WebDAV auth for passwords containing '%' followed by two hex digits
fd812070b6 made _parseURI() decode the URL credentials so that
download() could build its own Basic Auth header from the decoded
values, but request() passed the decoded credentials to xmlhttp.open(),
which percent-decodes them again, so a password like "example%41pass"
was sent as "exampleApass". Re-encode the credentials before passing
them to open().

Fixes #6048
2026-09-16 10:04:14 -04:00
Dan Stillman
3c959e1012 Pass a triggering principal when launching non-HTTP URLs
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
Gecko 140.15 made nsIExternalProtocolService.loadURI()'s triggering
principal mandatory, so Zotero.launchURL() threw NS_ERROR_ILLEGAL_VALUE
for any scheme handled by an external app.

https://forums.zotero.org/discussion/133709/
2026-09-11 14:31:51 -04:00
Dan Stillman
ab34e9031a Handle overlong filenames in getClosestDirectory()
Some checks are pending
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Build, Upload (push) Waiting to run
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
relinkAttachment() calls getClosestDirectory() before showing the file
picker, and a too-long filename caused the OS.File.stat() in
getClosestDirectory() to throw, which prevented the file picker from
appearing after clicking Locate.

https://forums.zotero.org/discussion/133685/
2026-09-10 09:53:46 -04:00
Dan Stillman
913aff342d Say a keyring service may need to be installed on Linux
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
https://forums.zotero.org/discussion/133599/
2026-09-09 14:34:11 -04:00
Dan Stillman
cc700a1e11 Add a More Information link to keystore failures on Linux
https://forums.zotero.org/discussion/133599/
2026-09-09 14:34:10 -04:00
Dan Stillman
e03920890a Start HiddenBrowser loads from the parent process
Some checks are pending
CI / Build, Upload (push) Waiting to run
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
Firefox ESR 140.15 rejects loads from a content process for URLs that
process couldn't load on its own, including blob: URLs created by chrome
code. Full-text indexing loads HTML attachments through such a URL, so
indexing crashed Zotero with "Illegal load attempt of blob: URL from
web". Loads started by the parent are exempt, so start the load there
and use the child actor only to disable content retargeting.

https://forums.zotero.org/discussion/133661/
2026-09-08 17:08:55 -04:00
Dan Stillman
26988646bf Include the error message in startup error dialogs
SpiderMonkey stacks contain only frames, so the startup error messages
that showed just the stack didn't say what had failed. 9b3d7a32e3 added
the message to one of the three, where a ternary-precedence bug then
dropped the surrounding text instead. Format all three the same way.
2026-09-08 16:12:47 -04:00
Dan Stillman
bdaecbb5cc Detect database corruption reported by a transaction's commit
We only ever checked for corruption errors from transactions in
queryAsync(), so a corruption error raised by the COMMIT that mozStorage
runs itself bypassed the check. A 10.0 schema upgrade -- which heavily
exercises the database -- that failed due to corruption showed "Database
upgrade error" and a single Sqlite.sys.mjs frame instead of the prompt
offering to restore from a backup.

Two users reported this, but it's not clear what triggered it --
corruption usually occurs during a statement, which we did catch. There
may have been some statement transaction whose corruption error was
caught and ignored rather than being left to abort the transaction,
causing SQLite to then block the commit. That's what the test does, and
it fails without the fix.

https://forums.zotero.org/discussion/133611/
2026-09-08 16:01:23 -04:00
Dan Stillman
a43c1d5ad5 Update reader submodule 2026-09-08 09:37:42 -04:00
Dan Stillman
fc17dcd24a Tell the user to set up syncing again if credentials can't be decrypted
Some checks failed
CI / Build, Upload (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
If the keystore's secret is replaced -- say, after a keyring reset --
the stored value won't be able to be decrypted again, so tell them to
try logging in again instead of trying to fix their keyring.

https://forums.zotero.org/discussion/133603/
2026-09-04 18:12:31 -04:00
Dan Stillman
1086d80cec Update reader submodule 2026-09-04 16:06:00 -04:00
Dan Stillman
92dfac8c64 Don't make an unloaded tab reopenable with Undo Close Tab
unload() closes and re-adds the tab, and the close recorded an undo-close
history entry, so Undo Close Tab could open a duplicate of a tab that was
still open.
2026-09-04 15:30:33 -04:00
Dan Stillman
e88b7ee10f Don't clear the hidden flag when refreshing retractions data
_addEntry() wrote the row with REPLACE INTO, listing only itemID and
data, so flag reverted to its default of 0. It's called for every match
on every update, not just new ones, so a retraction the user had hidden
came back as soon as the list changed and Zotero restarted.
2026-09-04 15:07:15 -04:00
Dan Stillman
53bcd30a5f Don't report a window's observers as leaked while it's still closing
A window is marked as closed before its unload handlers run, so the tab
events fired from ZoteroPane.destroy() warned about every observer that
the closing window hadn't torn down yet. The leak check now also requires
the window to be detached from its docShell, and it covers functions. It
caught an observer that outlived its window -- the sync-reminder API key
observer, which is now unregistered on teardown.
2026-09-04 14:40:24 -04:00
Dan Stillman
22a1b33a42 Remove library tree event listeners on teardown
Listeners added to the item and collection trees' event bindings
survived unregister() and held ZoteroPane and the views for as long
as the tree objects did. makeClassEventDispatcher() now provides
clearEventListeners(), which the trees call on unregister, so callers
don't need to remove their own listeners. The collection tree focus
listener, a plain DOM listener, is removed in destroy().
2026-09-04 14:28:11 -04:00
Dan Stillman
0e46a5356b Fix tag selector observer leaks on uninit
Hiding the tag selector destroyed the TagSelectorContainer but left
its media-query listener registered, keeping the destroyed instance
alive for the lifetime of the window and firing setState() on the
unmounted component when the display density changed. The fontSize
pref observer id was also overwritten by the uiDensity registration,
so it was never unregistered.
2026-09-04 14:28:11 -04:00
Dan Stillman
d0a32a3b4f Poll for expected tag selector state in flaky tests
Selecting a collection doesn't wait for the tag selector to re-render,
so waitForTagSelector() could be resolved by the initial render instead
of the awaited change, and the assertions could run against a stale tag
list.
2026-09-04 14:28:11 -04:00
Dan Stillman
b3b6e7f33d Reopen closed reader and note tabs at their original position
The undo-close hooks read the index from the wrong object, so reopened
reader and note tabs were always appended at the end.
2026-09-04 14:24:47 -04:00
Dan Stillman
4d94810def Don't send a conditional request for an invalidated retractions cache
The cached retractions list is saved with the current data version from
the code, which we can bump to make clients discard their cached data
and refetch it. We've never actually done that, but if we did, it
would've hit a bug: init() would've started the refetch but still sent
the cached ETag, so if the list itself hadn't changed, the server
would've returned 304 and nothing would have been updated. The stale
version then would have survived, so the refetch would have been retried
on every startup.
2026-09-04 13:27:12 -04:00
Dan Stillman
54cea6cc37 Update Retraction Watch reason descriptions
Retraction Watch has renamed, merged, and added reason terms since these
were added, leaving only 65 of the 102 entries matching a term still in
use. 90% of retracted items had at least one reason that didn't show a
description, and 21% showed none at all. Rebuilt from the current
Appendix B list, which covers every reason in the data.
2026-09-04 13:12:43 -04:00
Dan Stillman
f3810befde Show file access error for NS_ERROR_FILE_FS_CORRUPTED
Windows returns this for ERROR_FILE_CORRUPT/ERROR_DISK_CORRUPT. It's not
in xpc.msg, so the exception has an empty name and isn't in
Components.results, and it was falling through to the generic file sync
error instead of the file access error with the path. Show a message
about the reported corruption in place of the usual permissions advice.

https://forums.zotero.org/discussion/133592/
2026-09-04 12:37:49 -04:00
Jakob Nylin
e3da52bebc
Match duplicate books by ISBN-10/13 equivalence (#6028)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-09-03 12:42:04 -04:00
Martynas Bagdonas
9086156a91 Update document-worker submodule 2026-09-03 18:20:11 +03:00
Dan Stillman
e56fad828d Throw from removed selection methods in pre-release builds
The shims from 5697ee0af7 keep old plugins working for users, but
beta/dev/source builds should still throw to get the attention of
developers and (hopefully breakage-tolerant) beta users.
2026-09-02 22:34:19 -04:00
Dan Stillman
0e00c1ce51 Center single-paragraph items pane status messages when they wrap
Status messages were left aligned for the multi-paragraph intro text,
but in a narrow window, that made single lines that wrapped sit
off-center, so center those instead.
2026-09-02 22:16:13 -04:00
Dan Stillman
5697ee0af7 Shim removed selection methods when a single row is selected
Plugins written for earlier versions call methods like
CollectionTree#getSelectedSearch(), which now throw. We did this
intentionally to make old code more obviously broken, even with single
selection, so developers would fix their code, but we're not yet
blocking plugins that illegitimately declared compatibility with a
future version, so some haven't been updated and are breaking Zotero.
With a single row selected, the pre-10 functions can technically
continue to work, so for now, restore them and just warn and name the
plugin instead of throwing. They still need to throw if multiple rows
are selected, since plugins that haven't been updated can't safely act
on a selection that might span collections or libraries.

https://forums.zotero.org/discussion/133565/
2026-09-02 22:16:13 -04:00
Dan Stillman
fac6c8ebf6 Name the responsible plugin when the items list fails to load
A plugin that monkey-patches a method in the item list's load path --
say, Zotero.CollectionTreeRow.prototype.getItems() -- can throw and
leave the pane showing only "Error loading items list", with nothing in
the error report to identify it. Update the message to name the culprit.

https://forums.zotero.org/discussion/133565/
2026-09-02 22:16:12 -04:00
Dan Stillman
08ed64f17f Fix "false" in Added By/Modified By columns for trashed collections
The row values were computed with `row.isItem && …`, so collection and
search rows in the trash got the boolean `false`, which the table then
rendered as the text "false".

https://forums.zotero.org/discussion/133560/
2026-09-02 12:18:12 -04:00
Dan Stillman
9e28eb0d39 Don't clear the saved session if startup fails
Zotero.Session.save() runs from a quit-application-granted observer
registered before the database checks, but session.json is only read
after them, so a startup error -- an incompatible database from a newer
version, say -- meant that quitting overwrote the file with an empty
state and all open tabs were lost.

https://forums.zotero.org/discussion/133542/
2026-09-01 22:56:41 -04:00
Dan Stillman
661843b03f Fix sync error due to invalid attachment filename
A stored-file path of 'storage:/' -- left behind by the 128 schema step,
which skipped paths with no basename -- triggered an
NS_ERROR_FILE_UNRECOGNIZED_PATH that aborted the whole
checkForUpdatedFiles() loop, so no files synced in the library. Skip an
attachment that throws instead of failing the whole library.

Also apply the setter's directory-path rule in getFilePath[Async]() to
avoid errors elsewhere.

https://forums.zotero.org/discussion/133523/synchronize-issue
2026-09-01 22:52:54 -04:00
Dan Stillman
a0ce7b3256
Show recently used collections in the Add to Collection menu (#6036)
Collections are now tracked as they're selected, added to, or dropped
on, and the five most recent usable targets are listed above the
full collection hierarchy by full path.
2026-09-01 21:59:20 -04:00
Dan Stillman
4a8a88dd41 Fix error using a CSL 0.8 style
transformToDocument() needs a load group, which it takes from the source
document or from the window that created the XSLTProcessor. Neither of
those has existed since 0f2690eb75 in Zotero 8 stopped taking
XSLTProcessor from the hidden window, so the CSL 0.8 → 1.0 upgrade threw
NS_ERROR_FAILURE. In Word, this showed as "Zotero encountered an error
while updating your document."

https://forums.zotero.org/discussion/133543/
2026-09-01 15:45:48 -04:00
Martynas Bagdonas
b4f1deb998 Add note editor indentation strings 2026-09-01 12:06:28 +03:00
Dan Stillman
984a72a3d9 Fix the startup error shown when the database can't be written to
Regression from b27c4cb023 in 10.0, which added pragma commands that
write to the database. If those failed because the database was
read-only, we would show a raw SQLite error instead of the message about
permissions.

https://forums.zotero.org/discussion/133521/error-for-zotero-10-for-mac
2026-08-31 14:23:53 -04:00
Dan Stillman
e679473db1 Remove citeproc-rs
citeproc-rs is no longer maintained, and people who had enabled the
hidden pref were hitting errors.

This also drops the free() call on CSL engines, which only existed to
free the citeproc-rs wasm driver.

https://forums.zotero.org/discussion/133515/
2026-08-31 12:38:05 -04:00
Dan Stillman
30425e024d Revoke the server API key if it can't be stored locally
The login session creates a key on the server before we try to store it,
so each attempt that failed to save the key left an active key behind.
2026-08-31 12:21:52 -04:00
Dan Stillman
c19177a469 Reset the account pane if the login session can't be stored
If storing the API key failed, the pane kept showing the spinner and
"Waiting for login…" until the user clicked Cancel, even though the
login attempt was already over.

https://forums.zotero.org/discussion/133418/
2026-08-31 12:21:29 -04:00
Dan Stillman
ace9fa0227 Report the real cause of an OS keystore failure
Every failure shows "User canceled OS unlock entry" no matter what went
wrong. Test the store for the actual state, and give callers a message
describing what couldn't be accessed.
2026-08-31 12:21:29 -04:00
Dan Stillman
57c8c81872 Don't show credential dialogs during automatic syncs
Both the keystore fallback prompt and the migration alert are reachable
from the credential read paths that a sync uses, so don't show if it's
an automatic sync.
2026-08-31 11:55:58 -04:00
Dan Stillman
892898040b Fall back to saving credentials without OS keystore encryption
Some Linux systems have no Secret Service running, and if users can't
change that (e.g., a managed system), storing an API key fails and login
never completes. Offer to store credentials unencrypted instead, and try
to encrypt them on a later read if the keystore becomes usable.

https://forums.zotero.org/discussion/133418/
2026-08-31 11:44:43 -04:00
Dan Stillman
d276ec19c5 Fix the window lookup for OS keystore alerts
'zotero:main' isn't a registered window type, so the lookup always
returned null and the alerts were never shown.

https://forums.zotero.org/discussion/133418/
2026-08-30 14:52:16 -04:00
Dan Stillman
bccaf46ff2 Advanced search: Reopen the conditions menu to the selection on macOS
Firefox 153 renders a menulist's popup as a native macOS menu, which
can't be opened to a submenu, so revealSelectedCondition() has done
nothing on macOS since the upgrade. Opt this popup out of native
rendering.
2026-08-28 14:16:11 -04:00
Dan Stillman
fa94212496 Advanced search: Use the shared find-as-you-type for the conditions menu
The conditions menu had its own find-as-you-type, which
Utilities.Internal.addMenuFindAsYouType() now provides for any menu.
Matching walks the menu rather than a cached list of every condition, so
typing can no longer select a condition the menu doesn't offer --
Collection and Saved Search are removed when the search spans multiple
libraries.

An attachment or annotation condition is shown with a short label inside
its submenu, so pass the full name to match on, which is what the
menulist shows once the condition is selected.
2026-08-28 14:16:10 -04:00
Dan Stillman
c8b841df8a Advanced search: Show subcollections in submenus
The Collection value menu was a flat list of every collection in the
library, with subcollections set apart by an indent (or hyphens before
606d8f19ba). Build it with Utilities.Internal.createMenuForTarget()
instead, using the new 'filter' feature to limit to collections. The new
FAYT helper preserves matching on subcollections.

If a subcollection is selected, open the path down to the subcollection
when opening the menu. (As of fx153, macOS renders popups as native
menus, which can't be opened to a submenu, so revert to non-native menus
there.) A collection in a submenu can't be a menulist's selected item,
so the condition holds the value and sets the menulist's label and icon
itself, with the collection's path as a tooltip.
2026-08-28 14:13:26 -04:00
Dan Stillman
41353f01cf Add find-as-you-type to createMenuForTarget()
A collection in a submenu can't otherwise be reached from the keyboard.
2026-08-28 14:02:41 -04:00
Dan Stillman
dffcbb553f Add a find-as-you-type helper for menus
A menulist's built-in find-as-you-type searches only the direct children
of its menupopup, so an item in a submenu can't be reached from the
keyboard. addMenuFindAsYouType() matches on every item in the menu
instead.

customElements.js opens a focused menulist when a space is pressed,
which would end a search partway through a name, so defer that while a
search is in progress.
2026-08-28 14:02:41 -04:00
Dan Stillman
eb33fa5835 Move the checkmark to the activated target in createMenuForTarget()
The target menu isn't rebuilt between openings, so the checkmark stayed
on the target the menu was built with. In the New Collection dialog,
whose menu belongs to a menulist, that would leave two checkmarks if you
clicked a different collection and then reopened the menu.
2026-08-28 13:43:37 -04:00
Dan Stillman
07745b634b Add a filter option to createMenuForTarget()
A caller can leave out targets it doesn't want, with an excluded target
replaced by its subcollections.
2026-08-28 11:40:26 -04:00
Dan Stillman
c098f5e84d Show the icon on the checked item in a collection menu on macOS
On macOS, the current target in the New Collection dialog and the Add To
menus showed a checkmark in place of its folder icon -- type="checkbox"
makes nsMenuItemX::SetupIcon() skip the item. The `checked` attribute
alone marks it without suppressing the icon. Windows and Linux draw the
check in place of the icon either way.
2026-08-27 21:02:03 -04:00
Dan Stillman
1d1d32a211 Update builds to Firefox 153.1.0esr
Some checks failed
CI / Test () (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
This also fixes a broken reader with existing profiles due to a Cache
API DB schema version bump in 140.14.0esr that wasn't in 153.0esr.
2026-08-27 15:41:27 -04:00
Dan Stillman
c2052cfff1 Don't add items in imported collections to the import collection
Items are added to the collection created for an import as they're
saved, before the imported collection hierarchy exists, so an item only
in a subcollection ended up in the top collection as well as its own.
Remove those once the hierarchy has been created.

When everything imported belongs to a single top-level collection, that
collection is used for the import rather than being nested inside a
collection named after the file.

https://forums.zotero.org/discussion/133174/
2026-08-27 14:15:19 -04:00
Dan Stillman
61174e2158 Export selected collections as collections
Exporting a collection included only its subcollections, so an item
directly in the collection came through with no collection at all, and
an item in both the collection and a subcollection came through in only
the subcollection.

Exporting a selection of multiple collections exported a flat list of
items with no collections at all.

We now include the selected collection(s), with one exception: if a
saved search is also selected, we export a flat item list, since a
search can't be exported as a collection.
2026-08-27 14:15:19 -04:00
Dan Stillman
e29d8f151e Try to fix Safari extension breakage after app updates
Forcing a Gatekeeper assessment seems to fix the extension when it's
broken. General theory: the system does an assessment while the app is
doing an in-place update, calculates a signature mismatch between the
parent app and the appex (or within one of the bundles?), and caches
that forever, so forcing a reassessment fixes it.
2026-08-27 11:45:03 -04:00
Dan Stillman
8e4ec83abd Rename _checkClientVersion() to _handleClientVersionChange()
The function runs upgrade tasks and records the current version.
2026-08-27 11:38:47 -04:00
Dan Stillman
3b93d33b35 Fix WebDAV downloads with non-ASCII characters in the password
HTTP.download() built the Basic auth header with btoa(), which throws
on code points above 255, so every file download failed immediately
with a TypeError. Requests that go through XMLHttpRequest were
unaffected, since Necko builds the header itself, UTF-8 encoded.

https://forums.zotero.org/discussion/133454/synchronization-error-after-upgrading-to-10-0-1
2026-08-27 10:46:00 -04:00
Dan Stillman
0c9ba2d05c Fix corruption of non-ASCII characters in encrypted credentials
Mozilla's OSKeyStore.encrypt() encodes the string as UTF-8 before
encrypting, but its decrypt() returns the decrypted bytes as a binary
string without decoding them, so a WebDAV password containing non-ASCII
characters came back mojibake and authentication failed.

https://forums.zotero.org/discussion/133465/problem-login-into-webdav-server-with-10-0-1
2026-08-27 10:45:45 -04:00
Dan Stillman
b6efbe880e Pass save options through in Zotero.Collection::removeItems()
addItems() passes its options to Zotero.Item::save(), but removeItems()
dropped everything but skipEditCheck, so callers couldn't batch the
resulting notifications.
2026-08-26 15:24:57 -04:00
Dan Stillman
ff93139cce Localize the search syntax's operators and other words
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
Operators can be typed as the Advanced Search shows them, which every
locale already translates, and new keyword messages cover the join
words, "no"/"has", the units of a relative date, and the range forms.

Each range form is given as an example with its two ends filled in, so
that a locale can say it its own way and each form keeps its own words
(e.g., no "between 1970 to 2000").
2026-08-26 15:19:44 -04:00
Dan Stillman
4a250cd20f Support date and count ranges in the search syntax
"year is between 1970 and 2000", "year:1970-2000", "1970..2000", and
"1970 to 2000" all match values within the range, inclusive of both
ends. Ends can be a year, a month ("added between 2024-02 and
2024-06"), a day ("date:2020-03-01..2020-03-15"), or a count ("number
of tags between 2 and 5").
2026-08-26 15:17:55 -04:00
Mynacol
d153397151
Use versioned libc.so instead of /bin/ln on Linux (#6030)
* Fix js-ctypes-based symlinking on Linux by using `libc.so.6` instead of `libc.so` in `OS.File.unixSymlink()` and `Zotero.File.createSymlink()`
* Use that instead of `/bin/ln`, which doesn't exist on NixOS
* Replace `/bin/ln` with `Zotero.File.createSymlink()` in symlinked-database test

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-08-26 13:02:48 -04:00
Dan Stillman
5d20c692e8 Don't use FSEvents when storage isn't on a local volume
FSEvents is backed by a per-volume journal that only local volumes
have. On a network mount the stream is created and started
successfully but never delivers events, so the watcher would report
that nothing had changed for as long as it was used. Check the volume
with statfs() and fall back to scanning.
2026-08-26 10:36:54 -04:00
Dan Stillman
a1ea7037c2 Fix locally missing attachments never being downloaded
Since f21e1b2d32, a full local file scan no longer runs periodically and
on every manual sync, so locally missed attachments stayed marked for
upload and were skipped as unavailable instead of being downloaded.
"Reset File Sync History" marked every attachment for upload, including
files that had never been downloaded, so the forced download check added
in 404fc41b88 found nothing to download.

Missing files are now marked for download when the upload queue is
filled, and in at-sync-time mode they're downloaded in the same sync.
The reset marks them for download directly, and downloads are no longer
skipped just because there were no remote storage changes.

https://forums.zotero.org/discussion/133414/
2026-08-26 10:36:54 -04:00
Dan Stillman
6d8198bbfa Don't offer a nonexistent file when dragging an attachment
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
getFilePath() and getFile() return a path whether or not the file
exists, so dragging an attachment that hadn't been downloaded handed
the drop target a path that didn't exist. Missing files also went
unreported by the drag data provider.
2026-08-25 15:25:33 -04:00
Dan Stillman
56eb77b704 Fix dragging attachments to File Explorer
Firefox 140.14 in Zotero 10.0 made the drag transferable's data
principal null for chrome-initiated drags, so the file-promise stream
that File Explorer used couldn't be created and the drop failed with
"Unspecified error". On Windows the promise was just a file:// URL for
the attachment itself, so hand over the file directly instead, and
force a copy so that File Explorer doesn't move it out of storage.

https://forums.zotero.org/discussion/133399/
https://bugzilla.mozilla.org/show_bug.cgi?id=2054665
2026-08-25 15:25:33 -04:00
Dan Stillman
e69ee69218 Fix Software Update window stuck on "Applying update…"
If an update had already finished downloading when the download page was
shown, the page waited for an update-staged notification even when
staging wasn't possible -- e.g., a default Windows installation in
Program Files, which isn't writable -- so it never advanced past
"Applying update…". It now checks whether staging is actually in
progress.

Not yet tested in an updatable build
2026-08-25 11:09:20 -04:00
Adomas Venčkauskas
baa47e3bb9 Remove bibliography when last citation is deleted. Closes #3474 2026-08-25 15:55:11 +03:00
Dan Stillman
753dbf557a Fix stuck "Loading items" at startup
Some checks are pending
CI / Build, Upload (push) Waiting to run
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
The collections tree selects a row as soon as it's initialized, which
raced with the items tree's initialization. If the collections tree won,
onCollectionSelected() threw on the missing items view and no items were
ever loaded.

https://forums.zotero.org/discussion/133379/
https://forums.zotero.org/discussion/133380/
2026-08-24 10:39:10 -04:00
Dan Stillman
786cdea884 Remove dead transaction rollback path
Some checks failed
CI / Detect changes (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test () (push) Has been cancelled
CI / Test (macOS NFS) (push) Has been cancelled
CI / Test (Windows arm64) (push) Has been cancelled
CI / Test (Windows x64) (push) Has been cancelled
rollbackAllTransactions() called transactionInProgress() and
rollbackTransaction(), neither of which has existed since nested
transaction support was removed in 14d435b8d8, so it would have thrown
had either of its two callers still been reachable. Both are in code
long since replaced: Zotero.Sync.Server, which uses the synchronous
Zotero.DB.columnQuery(), and an error handler in Zotero.Sync.Storage
that nothing calls.

Drop it along with _transactionNestingLevel, _transactionRollback, and
_shutdown, which nothing reads.
2026-08-22 15:09:37 -04:00
Dan Stillman
f2a42bec15 Retry note saves that time out waiting for the database
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Any error while saving a note prompted the user to restart Zotero, even
a transaction timeout caused by a long-running operation elsewhere.
Nothing has been written when the wait times out, so retry, unless newer
note content has been handed to the editor in the meantime.

https://forums.zotero.org/discussion/133298/
2026-08-21 16:31:18 -04:00
Dan Stillman
070ae8b615 Don't hold the database to optimize the full-text index
Editing a note flagged it stale, and the background drain indexed it and
then ran an FTS5 'optimize' -- a single statement that rewrites the
content index and can hold the shared database for over a minute --
because the queue was empty again. Note saves waiting on the connection
hit the transaction timeout and told the user to restart Zotero.

Merge the index in bounded steps instead, so no statement runs long
enough to keep other queries waiting, and only after enough items have
been indexed to be worth it.

https://forums.zotero.org/discussion/133298/
2026-08-21 16:31:18 -04:00
Dan Stillman
e8055dfdf2 Open the database only once when callers arrive concurrently
_getConnectionAsync() checked for an existing connection and then awaited
several filesystem operations before assigning one, so callers arriving in
that window each opened their own. Only the last was kept, and the rest
stayed open and unreachable, holding a mozStorage thread apiece until
shutdown.
2026-08-21 16:28:36 -04:00
Dan Stillman
9e45191b22 Stop observing idle after the database is closed permanently
A permanently closed connection kept its idle observer, so it went on
being notified and attempting backups for the life of the process. This
affects plugin databases, which are closed permanently when the plugin
shuts down.
2026-08-21 16:28:35 -04:00
Dan Stillman
7243648ed7 Register the DB backup idle observer only once per connection
The observer was added on every open with no matching removal, so each
reopen left behind another registration that received its own idle
notification. On macOS, where the periodic backup closes and reopens the
connection, the registrations accumulated and multiplied the work done
on each idle.

Addresses #6027
2026-08-21 16:28:35 -04:00
Dan Stillman
85e1cbc75c Don't back up an unchanged database
currentDBTime and lastBackupTime became Date objects when the backup was
made asynchronous in 2014, so comparing them with == has tested object
identity and returned false ever since. The interval check that follows
measures from the backup file's mtime, which a copy inherits from the
database file, so an unchanged database was backed up on every idle.

Fixes #6027
2026-08-21 16:28:35 -04:00
Dan Stillman
ad98e84d24 Fix Any Field searches at a non-item result level
Any Field expands to a generic 'field' condition, which the cross-level
code treated as matching only on top-level items. At an attachment
result level it therefore matched attachments whose parent item had the
value, instead of attachments with the value themselves. Since the
condition stands in for every field, it's now treated as matching at any
level those fields live at.
2026-08-21 12:56:05 -04:00
Dan Stillman
77a3a8815e Add a query syntax to the quick search
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Test (Windows arm64) (push) Blocked by required conditions
CI / Test (Windows x64) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Zotero.SearchQuery turns a query like `by:smith after:2020 tag:"to read"
crispr` or `creator is smith and (tag is foo or bar)` into a
Zotero.Search, matching whatever text is left over using the current
search mode. Anything that doesn't look like a clause is free text, so a
DOI or a title with a colon in it is matched literally.

The search box syntax-highlights the parts of recognized conditions and
offers autocomplete for condition names, for the values of conditions
that have a fixed set of them (like item type), and for tags and
creators from the selected libraries.
2026-08-20 16:09:20 -04:00
Dan Stillman
766cb079a0 Only defer the quick search while a quotation mark is unclosed
Searching was deferred whenever the query contained a double quotation
mark, with an items-pane message to press Enter. Quoted phrases are no
longer expensive now that Everything mode uses full-text search, so
defer only while an unclosed quote means the phrase is still being
typed, and search as soon as it's closed. Enter still forces a search
mid-quote.
2026-08-20 16:09:20 -04:00
Dan Stillman
0ee3e4052e Don't prompt to save an unchanged saved search
https://forums.zotero.org/discussion/133302/zotero-10-questions-about-search
2026-08-20 15:38:13 -04:00
Dan Stillman
0746c665a9 Don't match dates without a sortable year in "is before" searches
Values without a parsable year are stored with a 0000-00-00 sort key,
and the guard meant to exclude them from comparisons checked the full
multipart value, which is always greater than '0000-00-00'.
2026-08-20 13:19:23 -04:00
Dan Stillman
73f4273e2d Make Original Date a date field
Update the global schema to 45, resolve a field's date type through its
base-field mapping in ItemFields.isDate() (to cover priorityDate), and
convert stored values of date-type fields to multipart dates on schema
upgrade.
2026-08-20 12:57:46 -04:00
Dan Stillman
04796dff22 Use date operators for all date-type item fields in searches
Only Date and Accessed were routed to the datefield condition, so other
date fields (e.g., Filing Date) offered only text operators and
couldn't be compared as dates. Text operators are still accepted on
date fields so existing saved searches keep loading and running.
2026-08-20 12:57:46 -04:00
Dan Stillman
5f978c71ca Detect Windows-on-ARM in dir_build under an emulated shell
The GitHub runner image installs the x64 build of Git for Windows, so
its bash and everything run from it execute under x64 emulation on
Windows on ARM, where uname -m reports x86_64 and the staged build got
the wrong architecture. uname -s still reports the machine, as
MINGW64_NT-10.0-26200-ARM64, so use that instead. If the image ever
installs a native arm64 Git for Windows, uname -m will be correct and
this can go.
2026-08-20 12:34:41 -04:00
Dan Stillman
c220866d9a Use a platform-appropriate path in file access error test
The hardcoded POSIX path isn't valid on Windows.
2026-08-20 12:34:41 -04:00
Dan Stillman
6ce33bcd81 Use forward slashes in ZIP entry names on Windows
The ZIP format requires forward slashes, and nsIZipWriter stores
whatever it's given, so entries for files in subdirectories were
written with backslashes.
2026-08-20 12:34:41 -04:00
Dan Stillman
f5a2985a34 Handle unparseable paths in getClosestDirectory()
On Windows, stat rejects a POSIX-style path with
NS_ERROR_FILE_UNRECOGNIZED_PATH rather than a not-found error.
2026-08-20 12:34:41 -04:00
Dan Stillman
cdb13af13b Wait for the browser process in Windows test runs
zotero.exe is the Mozilla launcher process, which spawns the browser
process and exits, so the test runner checked for the success file and
cleaned up the profile while the app was still starting, and passing
runs were reported as failures that ended at arbitrary points.
2026-08-20 12:34:41 -04:00
Dan Stillman
32640e2019 Wait for test success file to be written before quitting
The write is asynchronous, and on Windows the app quit before it
finished, making passing test runs report failure.
2026-08-20 12:34:40 -04:00
Dan Stillman
22b3daf845 Don't require rsync in build.sh
rsync isn't available on Windows, so fall back to cp for directory
copies.
2026-08-20 12:34:40 -04:00
Dan Stillman
aa49b48915 Don't use rsync and find in prepare_build
Neither is reliably available on Windows, so copy the source tree with
Python when rsync is missing and delete the CSL locale support files
with a directory walk.
2026-08-20 12:34:40 -04:00
Dan Stillman
c7cffdd26c Move prebuilt reader/note-editor files without the shell
The shell glob in the mv breaks on Windows paths, so every Windows
build silently fell back to building the submodules from source.
2026-08-20 12:34:40 -04:00
Dan Stillman
da705c36ec Add a timeout to the SMB and NFS setup steps in CI
The apt mirrors regularly hang for many minutes, which was taking out
random jobs until the job timeout.
2026-08-20 12:34:40 -04:00
Dan Stillman
b930ddb863 Add Windows test jobs for Gecko and network-filesystem changes
Boots the Windows x64 and arm64 builds, which use custom-built Firefox
components rather than stock Mozilla binaries, and runs 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, which previously required manual Windows testing.
2026-08-20 12:34:40 -04:00
Dan Stillman
847dffbf18 Update CI to Node 24
Include the version in the node_modules cache keys so that a version
change starts fresh caches.
2026-08-20 12:34:40 -04:00
Dan Stillman
96972db8c6 Don't install xvfb in Linux CI
xvfb comes with the GitHub runner image.
2026-08-20 12:34:39 -04:00
Dan Stillman
dac1ad489f Update utilities submodule
Some checks are pending
CI / Detect changes (push) Waiting to run
CI / Test () (push) Blocked by required conditions
CI / Test (macOS NFS) (push) Blocked by required conditions
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
And add test for citing date strings wrapped in double quotes

https://forums.zotero.org/discussion/133277/
2026-08-19 15:28:34 -04:00
Dan Stillman
ed8049dffc Run network-filesystem tests only for relevant changes, and on macOS
Run the smb/nfs test shards and a new macOS NFS job only when the
changes touch the Gecko version or the network-filesystem database
handling, plus on manual and monthly scheduled runs, via a 'changes'
job feeding the test matrix. The macOS job uses the native NFS server
with a loopback mount and runs the real code path that crashed on
network data directories on macOS.
2026-08-19 11:13:15 -04:00
Dan Stillman
e1759daa00 Show in-window message in style preview instead of progress popup
Since 30b5bf4bb9, the popup opens as a dependent window of the main
window, so it appeared behind the Style Preview window and, on Windows,
brought the main window to the front.

https://forums.zotero.org/discussion/133296/
2026-08-19 10:54:56 -04:00
Dan Stillman
4e532a17a2 Skip symlink tests on filesystems without symlink support
CIFS mounts can't create real symlinks without special mount options,
so skip the symlinked-database and broken-symlink tests when a created
symlink doesn't exist or isn't visible as one.
2026-08-19 09:37:38 -04:00
Dan Stillman
ad1bec62f2 Use offline database backups on network filesystems on Linux
The online backup API writes the backup file through SQLite's default
VFS, whose locking can hang on network filesystems -- its lock upgrades
conflict with the SMB byte-range lock mapping on CIFS mounts and retry
forever -- so fall back to an offline backup, which copies the file
with the connection closed. Extends getFileSystemInfo() to Linux using
statfs f_type magic numbers.
2026-08-19 09:37:38 -04:00
Dan Stillman
18f6fa50d5 Add CI test shards with the data directory on SMB and NFS shares
Adds 'smb' and 'nfs' entries to the test matrix that run the DB and
file test suites with the test data directory on a loopback CIFS or
NFS mount, covering the network-filesystem database handling from
bba85a3939. The test harness places the data directory under TMPDIR,
so connection initialization itself runs against the share, which is
where network-filesystem failures (startup crashes and hangs) occur.
The step timeout turns a startup hang into a failure.
2026-08-19 09:37:37 -04:00
Martynas Bagdonas
db1216d3e2 Fix annotations not appearing when imported while reader is opening
Fixes https://forums.zotero.org/discussion/133270/bug-embedded-pdf-annotations-and-highlights-fail-to-render-in-zotero-reader
2026-08-19 10:06:31 +03:00
Dan Stillman
b6837a3a57 Fix doubled Index Statistics numbers after searching in preferences
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
The preferences search feature moves label value attributes into text
content for highlighting, so stats refreshes that set the value
attribute rendered the new number in front of the stale text.

https://forums.zotero.org/discussion/comment/516909/#Comment_516909
2026-08-18 17:42:34 -04:00
Dan Stillman
bba85a3939 Fix database access on network filesystems on macOS and Linux
On macOS, SQLite chooses locking methods based on the filesystem
containing the database, and network filesystems (e.g., SMB, NFS),
read-only volumes, and filesystems without byte-range locking get
methods without shared-memory support, which WAL requires. Opening a
database with an adjacent WAL file on those crashes -- Mozilla's VFS
wrapper hides the missing shared-memory methods from SQLite's WAL
support check -- so the first Zotero 10 run converted the database to
WAL and every launch after that segfaulted during connection
initialization.

On macOS, mirror SQLite's method selection and use a rollback journal
when shared memory isn't available, converting an existing WAL database
before opening it: an empty WAL by reverting the header format versions
in place, and a non-empty WAL by replaying it into a temporary copy on
local disk that replaces the database file only after passing an
integrity check. A WAL file next to an already-converted database
(e.g., from an interrupted conversion) goes through the same
conversion. Also use openNotExclusive during integrity checks and
corruption recovery, which otherwise fail on SMB shares with an I/O
error from the exclusive open lock.

This corrects 22055d92b7, which passed openNotExclusive on all
platforms for an open failure described as affecting macOS and Linux,
and expected locking_mode=EXCLUSIVE to keep the WAL index in heap
memory with no -shm file. Neither claim held up: mozStorage opens the
WAL while initializing the connection, before any pragma can run, so
the index uses shared memory unless the exclusive VFS is in use, and
the exclusive open works on Linux CIFS mounts -- where non-exclusive
access instead made SQLite's lock-upgrade sequence trip over the SMB
byte-range lock mapping, leaving startup hung and the database never
created. So pass openNotExclusive only on macOS. On Linux this restores
unix-excl, which performs all locking under a single held lock and
keeps the WAL index in heap memory; Windows has no distinct exclusive
VFS and is unaffected.

https://forums.zotero.org/discussion/133258/
2026-08-18 14:38:37 -04:00
Dan Stillman
5757396197 Fix changelog URL generation for two-digit major versions
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
The short version was derived from the first three characters of the
version string, so 10.0 produced a detailsURL of "10._changelog".
2026-08-17 12:10:35 -04:00
Dan Stillman
5a6c9245f0 Fix locale update script under rpl 2.x
rpl 2.1.0 guesses each file's character encoding instead of assuming
UTF-8, and aborts on some locale files, leaving the script's later
steps unrun. Pin the encoding, and use -exec + so a failure in one of
these passes actually stops the script.

Also drop the ⏎ replacement, which hasn't matched anything since 2013
and would now insert real newlines rather than literal \n, truncating
.properties entries.
2026-08-17 09:57:58 -04:00
Abe Jellinek
844bd87824 Update reader submodule
Some checks failed
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
2026-08-14 13:41:03 -04:00
Dan Stillman
e0f9710c46 Update utilities submodule
edtf.js is replaced with a built-in parser in date.js, so it no longer
needs to be loaded at startup.
2026-08-14 10:44:23 -04:00
Maximilian Schmidt
b8fdee865a
Fix zero local data object versions (#6016)
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-08-13 11:23:05 -04:00
Dan Stillman
29a4d9d140 Skip focus traversal tests when the pane window can't be activated
The traversal waits on focus/blur events, which Gecko fires only while
the window is active, so a window that can't be activated -- another
app focused during a local run, or intermittent deactivation in CI --
timed out after 10 seconds instead of reporting that it couldn't run.
2026-08-13 10:43:51 -04:00
Dan Stillman
472a0bf856 Update utilities submodule
And add a test for citing a spelled-out BCE date, which citeproc-js
rendered as AD.
2026-08-13 10:01:09 -04:00
Tom Najdek
68bec97114
Add support for importing from encrypted Mendeley Desktop DB (#6019) 2026-08-13 09:33:41 -04:00
Dan Stillman
4a6a8220ff Fix error from header element in updater window
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
attributeChangedCallback() runs before the content has been rendered.
2026-08-12 17:18:52 -04:00
Dan Stillman
f53d4b5eca fx153: Fix update window getting stuck on "Looking for changes"
Firefox 153 removed the wizard's extra1 and extra2 buttons, so onLoad()
threw while caching their labels and never got to starting the update
check or adding the page handlers. Create the buttons ourselves instead.
2026-08-12 17:18:52 -04:00
Dan Stillman
1d6158dfd8 Remove Linux i686 build support 2026-08-12 16:36:41 -04:00
Dan Stillman
5df473cf96 Fix grep warnings about stray backslashes during MAR builds 2026-08-12 16:29:20 -04:00
Dan Stillman
e7767c5bfa Don't build Linux i686 updates for Zotero 11
i686 builds are gone as of Zotero 11, so a full build failed trying to
package a linux-i686 MAR from a staging directory that no longer exists.
Skip the architecture entirely, and don't download it for older FROM
versions when building incrementals.
2026-08-12 16:29:20 -04:00
Dan Stillman
8d01216e5c Fix flaky search tests that used common words
Tests asserted exact search results for words like "one" and "two", so
a leftover item from an earlier test whose random title contained one
of the words as a substring could match and fail the assertion. Use
distinctive random strings instead.
2026-08-12 14:24:22 -04:00
Dan Stillman
8ac1273acb Support citing EDTF dates in the Date field and Extra
EDTF dates -- ranges ("2021/2026"), uncertain/approximate dates
("2004-06~"), and BCE dates ("-0429") -- and common equivalent notations
("1995-1996", "2021-22", "~1995", "ca. 1995", "429 BCE") are now passed
to citeproc-js as CSL date ranges, circa flags, and negative years.
Previously, such dates were mangled or dropped entirely unless entered as
CSL variables in Extra.

CSL date variables in Extra get the same parsing.

Other date handling in the client doesn't understand EDTF yet: the y/m/d
indicator in the date field doesn't reflect EDTF parsing, date searches
only match a range by its start date, and BCE dates still can't be
sorted.

Addresses #637
2026-08-12 14:11:26 -04:00
Dan Stillman
399a63ade2 Fix CI failures from items-in-view count updates
The debounced count update from fe35127f50 went through itemSelected()
and fired on any notifier change, so a pending timer could bump the
itemSelected spy after a test reset it, failing "shouldn't select a
modified item" in CI. Emit rowCountChange only when the row count
actually changed, and re-render the item pane message directly.
2026-08-12 13:25:34 -04:00
Dan Stillman
d9d52de516 Check that local app/ matches commit being built before deploying
The build-and-deploy scripts run scripts and config from the local
checkout but build source files from the tip of the remote branch, so
a stale or wrong-branch checkout could silently build with the wrong
Gecko version or omni patches.
2026-08-12 12:46:30 -04:00
Dan Stillman
a9e765e37f Remove 8.0 release build script 2026-08-12 12:46:30 -04:00
Abe Jellinek
fdec96911c Update reader submodule
Some checks are pending
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Test (shard 1) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-08-11 13:42:41 -06:00
Abe Jellinek
ad7159834c Reader: Add support code for popup position persistence 2026-08-11 13:42:20 -06:00
Adomas Venčkauskas
e5508e870c Actually return 400 and throw when multipart body is malformed.
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Closes #6009
2026-08-10 16:16:00 +03:00
Dan Stillman
896582ab32 Hash files incrementally in md5Async() instead of blocking main thread
Some checks are pending
CI / Build, Upload (push) Waiting to run
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
md5Async() read and hashed the entire file synchronously on the main
thread. Read the file via an input stream pump instead, with file I/O
off the main thread and hashing done a segment at a time.
2026-08-09 22:11:42 -04:00
Dan Stillman
c1cdd038a2 Select item explicitly in item-pane refresh test
The test relied on the automatic selection of a newly created item,
which can lose the race with a view switch (e.g., from another library
selected by a previous test), leaving the info pane empty.
2026-08-09 22:09:02 -04:00
Dan Stillman
fe35127f50 Update items-in-view count when rows change without a selection
Notifier events that added or removed rows (e.g., items downloaded
during a sync) didn't update the no-selection message in the item pane,
so the count went stale until the selection changed. The row provider
now emits a rowCountChange event, and the pane re-renders the count,
debounced, when nothing is selected.
2026-08-09 14:49:21 -04:00
Dan Stillman
b8cbdc8c95 Document in-memory state handling for transaction rollbacks 2026-08-09 14:33:22 -04:00
Dan Stillman
04321fc627 Restore cached user name on transaction rollback
Zotero.Users.setName() updated the in-memory cache even if the
transaction was later rolled back, so a retry (e.g., of a failed sync
download batch) would skip rewriting the users row.
2026-08-09 14:29:48 -04:00
Dan Stillman
3630b904d7 Process downloaded objects in batched transactions
Saving each downloaded object in its own transaction made large
download syncs extremely slow, since every object required a separate
disk commit. Objects are now saved in batches sized by the existing
notifier batch ramp, and a failed batch is reprocessed individually so
that an error still rolls back only that object's save. The cached
parent item is reloaded on rollback, since it would otherwise keep
collections added via newParentItemCollections in memory and the retry
would skip writing them.
2026-08-09 14:22:21 -04:00
Dan Stillman
646fbfae65 Don't treat error in commit callback as a rollback
An error thrown from a commit callback rejected executeTransaction()
even though the transaction had been committed, so callers would treat
saved data as rolled back, and rollback callbacks (e.g., the notifier
reset) ran against committed data. Commit-callback errors are now
logged instead, and any error after a successful commit skips rollback
callbacks and is marked with 'committed' on the error object.
2026-08-09 14:22:21 -04:00
Dan Stillman
46603ca4eb Discard queued commit callbacks on transaction rollback
If a transaction was rolled back, callbacks queued via
addCurrentCallback('commit') stayed queued and ran after the next
successful commit, potentially applying in-memory cache updates for
rolled-back data.
2026-08-09 14:22:20 -04:00
Dan Stillman
1d279fba5b Add executeTransactionWithForcedRollback() test helper
Runs a function within a transaction that's rolled back at the end,
with rollback callbacks guaranteed to have run by the time it returns.
Replaces try/catch blocks in existing tests that threw and caught a
marker string from executeTransaction() to force a rollback.
2026-08-09 14:22:20 -04:00
Dan Stillman
45867cea54 Update reader submodule
Some checks failed
CI / Build, Upload (push) Has been cancelled
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
2026-08-07 21:02:49 -04:00
Dan Stillman
c87328ada9 Fix leading space in creator autocomplete values
the all-creators mode (fieldmode 2) concatenated
firstname || ' ' || lastname, so a two-field creator without a first
name produced ' last', which advanced search fields inserted with the
space.
2026-08-07 11:03:57 -04:00
Dan Stillman
8f6dc583b8 Skip startup full-text index purge and optimize when unnecessary
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
purgeOrphanedContent() ran on every startup, unlike the other
data-object purges, which are gated on the purge.* prefs.
startQueueDrain() also ran an FTS5 'optimize' even when the queues were
empty.

Gate the former on purge.items and the latter on having indexed
something.
2026-08-06 15:06:40 -04:00
Dan Stillman
3af8cea1af Name annotation types by type alone in the Advanced Search
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
The menu labeled Annotation Type listed "Highlight annotation" and "Image
Annotation", from the strings the reader announces annotations with. Use
the short names, which existed for two of the six.
2026-08-05 15:02:21 -04:00
windingwind
09249bfb73
fx153: Interface should load with nsIID instead of string (#6008) 2026-08-05 14:18:32 -04:00
Dan Stillman
8fb2870f93 Restore dxcompiler.dll on Windows for WebGPU and leave WebGPU pref on
mozinference (which is currently CPU-only) is better for many tasks, but
plugins may want WebGPU for some features (e.g., chatbots), so just
follow Firefox, which currently enables it by default for Windows and
Apple Silicon macOS. Adds 5.8 MB compressed to the Windows installer.
2026-08-05 14:10:04 -04:00
Dan Stillman
9da57a9fe3 Search: Allow binding a group whose conditions match at any level
Binding is meaningful for a condition that matches at every level -- a
tag bound to an attachment means the tag is on the attachment -- but a
group carrying one lost the binding as soon as the search was
serialized, so "items with an attachment tagged foo" couldn't be built.
2026-08-05 13:14:37 -04:00
Dan Stillman
b83342d8e0 Include numeric cell values in row accessibility labels
The row label builder skipped values that weren't strings, so a numeric
cell -- a note count, or a pre-1000 year (which is parsed with
parseInt()) -- announced only its column
name.

Also switch to formatColumnName() while we're here, so row labels
resolve the column name the same way the header does.
2026-08-05 13:09:58 -04:00
Dan Stillman
2f90893e59 Fix clearing the previous sort column's persisted direction
The guard tested the newly clicked column's settings entry while the
delete targeted the previously sorted column's, so the old direction
survived when the new column had no entry, and clicking threw when the
old column had none.
2026-08-05 13:02:24 -04:00
Dan Stillman
8052b8f9e2 Fix system-handler detection for PDFs/EPUBs on Windows 11
Windows 11 now stores default-app choices for protected file types
(e.g., .pdf) in a new UserChoiceLatest registry key [1], so the manual
registry lookup returned a stale value -- typically Edge -- instead of
the user's actual choice. Resolve the handler via nsIMIMEService, which
queries the effective association from the OS shell, and return false
for Microsoft Store apps, which have no launchable executable, so that
the file is opened via ShellExecute (albeit without a page number).

https://forums.zotero.org/discussion/133026/default-pdf-reader-stuck-on-edge

[1] https://kolbi.cz/blog/2025/04/20/userchoicelatest-microsofts-new-protection-for-file-type-associations/
2026-08-05 11:56:45 -04:00
Dan Stillman
aaeb9c6c9a fx153: Don't bundle unused Firefox runtime files
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Skip crashhelper (all platforms -- the crash reporter is disabled) and
desktop-launcher and nmhproxy.exe (Windows).

Also skip dxcompiler.dll (Windows, 22 MB), which is loaded only by
WebGPU, now also disabled via pref. Nothing renders with WebGPU, and
local ML -- including any future plugin API -- should go through
Firefox's inference libraries (mozinference/onnxruntime), which are
compiled CPU-only.
2026-08-04 14:42:40 -04:00
Dan Stillman
adc5648d29 fx153: Restore pass-through clicks on toolbarbutton dropmarkers
XULButtonElement only opens a type=menu button's menu when the button
is the event's original target, which worked before because xul.css
gave .toolbarbutton-menu-dropmarker 'pointer-events: none'. That rule
was removed along with the dropmarker element, so clicks on our
recreated dropmarker did nothing.
2026-08-04 14:42:39 -04:00
Dan Stillman
300305a9fb fx153: Set menu icons via --menuitem-icon for in-content menus
The menu icon element is now an <html:img> that renders the 'image'
attribute or 'content: var(--menuitem-icon)', and 'list-style-image' is
ignored, so iconic menuitems showed broken-image placeholders on
Windows and Linux. list-style-image is kept for native macOS menus,
which still read it.
2026-08-04 14:42:39 -04:00
Dan Stillman
97de8858fe fx153: Use font glyphs for Windows titlebar buttons
Firefox 153 removed the chrome://browser/skin/window-controls/ SVGs and
now draws the caption buttons with Segoe Fluent Icons glyphs (falling
back to Segoe MDL2 Assets on Windows 10), which broke the Windows build.
Port the new browser.css button styling and stop keeping the removed
directory in build.sh.
2026-08-04 14:42:06 -04:00
Dan Stillman
770f44568c Update Windows build to Firefox 153.0esr 2026-08-04 12:55:27 -04:00
Dan Stillman
a2a419c6da Match field values without regard to case or accents for 'is'
An exact-match condition compared with SQLite's case-sensitive '=' and
skipped the normalized shadow columns, so 'publication is "review of
finance"' missed "Review of Finance" while every other kind of search
matched it.
2026-08-03 18:59:01 -04:00
Dan Stillman
0ae2e42ca2 Use proper accent color for the selected autocomplete row
In an autocomplete popup, focus remains in the text field, so the
inactive selection color was being used instead.
2026-08-03 18:59:01 -04:00
Dan Stillman
1dffe21de0 fx153: Don't embed source-repo info in custom Windows builds
Some checks failed
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
Better fix than d5aacafe3f -- we don't need source-repo info in our
builds at all, so just disable MOZ_INCLUDE_SOURCE_INFO rather than
synthesizing it.
2026-08-03 18:59:01 -04:00
Dan Stillman
d5aacafe3f fx153: Stamp the git source repo and changeset for Windows packaging
Firefox's official-build source-repo.h generator only auto-detects
Mercurial, so building from our git checkout left the file empty and
packaging failed with "no preprocessor directives found". Derive
MOZ_SOURCE_REPO and MOZ_SOURCE_CHANGESET from git in the mozconfig so
configure records them for any invocation, including a manual
./mach package.
2026-08-03 18:32:11 -04:00
Dan Stillman
ea30bbd2af Update version to 11.0 2026-08-03 14:14:05 -04:00
Dan Stillman
72115c3cb4 Set Rust default toolchain before adding the target
Some checks failed
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
app/win/build added the cross-compilation target to whatever toolchain
was default before switching to RUST_VERSION, so after a Rust version
bump the new toolchain lacked the aarch64/i686 target and the arm and
win32 builds failed at configure. Set the default first.
2026-08-03 13:06:24 -04:00
Dan Stillman
6f3e4b5499 fx153: Update Mozilla patch for Windows 2026-08-03 12:33:59 -04:00
Dan Stillman
e4ee85296c fx153: Update Rust version for Mozilla builds 2026-08-03 12:09:02 -04:00
Dan Stillman
b5ceb6fa65 fx153: Don't assign oncommand attribute strings to .oncommand
Bug 1974578 added oncommand to GlobalEventHandlers for the Invoker
Commands API, so the assignment in _initImportedNodesPostInsert() -- an
inert expando write until now -- started setting the WebIDL event
handler, and [LegacyTreatNonObjectAsNull] turns a string into null. That
wiped the handlers compiled when the pane fragment was imported, leaving
every inline oncommand handler in the preferences panes dead.

importNode() compiles the attribute handlers itself, so the loop can
just be removed.
2026-08-03 11:48:42 -04:00
Dan Stillman
35a70117b0 fx153: Restore the toolbarbutton dropmarker
Bug 1980856 removed both wantdropmarker support and the rule that gave
.toolbarbutton-menu-dropmarker its image, once Firefox's last consumer
stopped using it, so the New Item, Add Attachment, and New Note buttons
lost their chevrons, along with the ones in advanced search, the feed
settings dialog, and Scaffold.

Draw it with a background, since a <dropmarker> doesn't render
list-style-image, and use chevron-6 to match the quick search dropmarker
and the menu marker on our own buttons. The rule also moves out of the
toolbarbutton block, since a plain <button> can host one too.
2026-08-03 11:48:42 -04:00
Dan Stillman
030cd3dc5e fx153: Drop the hover background on native search clear buttons
It runs into the field's focus ring, and we don't use hover effects on
comparable buttons.
2026-08-03 11:48:42 -04:00
Dan Stillman
503bb03aec fx153: Hide the native clear button in search fields
Firefox now creates a clear button for <input type="search"> in chrome
documents regardless of layout.forms.input-type-search.enabled (bug
1655503), which doubled up with the one we draw ourselves.
2026-08-03 11:48:42 -04:00
Dan Stillman
3430ad241b fx153: Keep our own Assets.car in the Mac app
Firefox 153 ships an asset catalog with its own AppIcon, which the merge
from Firefox's Resources copied over ours, so the app got the Firefox
icon.
2026-08-03 11:48:42 -04:00
Dan Stillman
00cad2ced0 Remove 32-bit Linux build support
Mozilla stopped publishing linux-i686 builds after Firefox 144, so there's
no 153 runtime to build against.
2026-08-03 11:48:42 -04:00
Dan Stillman
2bd9342188 Don't inject a platform wizard stylesheet on macOS
There's no mac wizard.css -- the shared one from the global list is all
macOS needs, as on Linux -- so this only produced a missing-file warning
whenever a <wizard> was created.
2026-08-03 11:48:42 -04:00
Dan Stillman
d43dc2fa37 Fail the xulrunner download on an HTTP error
Without -f, curl saves the error page and the download appears to succeed,
so a missing build fails later with a confusing "Unrecognized archive
format" from tar.
2026-08-03 11:48:42 -04:00
Dan Stillman
78d21617af fx153: Hide the about:addons sidebar
The sidebar customizations all broke: the category buttons are
moz-page-nav-buttons now rather than button[name="..."], and the Settings
and Support links moved out of <sidebar-footer> into a secondary nav. The
sidebar also has a heading now that duplicates the main pane's "Manage
Your Plugins", so hide the whole column instead of the pieces.
2026-08-03 11:48:41 -04:00
Dan Stillman
7793b469c9 fx153: Hide the Help button label on macOS
-moz-mac-help-button draws the question mark itself, and the Help label is
now painted over it at the bottom of the settings panes. Firefox hides the
label the same way for the Page Info help button (bug 2009625).
2026-08-03 11:48:41 -04:00
Dan Stillman
8110ab4eea Add a newline after the Zotero prefs in the built prefs file
Babel strips the trailing newline from defaults/preferences/zotero.js, so
the platform-specific prefs appended after it shared a line with the last
Zotero pref.
2026-08-03 11:48:41 -04:00
Dan Stillman
45e38322e1 fx153: Keep backup-constants.mjs
SelectableProfile.sys.mjs now statically imports BackupService, which
statically imports chrome://browser/content/backup/backup-constants.mjs,
so startup failed once SelectableProfileService was instantiated as a
command-line handler.
2026-08-03 11:48:41 -04:00
Dan Stillman
2eba3398e9 fx153: Restore Settings in the macOS application menu
nsMenuBarX now looks for a menuitem with the id menu_settings on Ventura
and later, falling back to menu_preferences only on older versions, so
declare both the way Firefox does.
2026-08-03 11:48:41 -04:00
Dan Stillman
01b7d0667c fx153: Move to nsIPKCS11Token for the NSS key database
Bug 2037682 removed nsIPK11TokenDB in favor of getting the internal key
token directly from @mozilla.org/security/internalkeytoken;1, and bug
2043434 replaced initPassword()/needsUserInit with changePassword().
2026-08-03 11:48:41 -04:00
Dan Stillman
a32dd2b959 fx153: Don't freeze built-ins in the translation sandbox
Bug 2017957 made Cu.Sandbox freeze built-ins by default for
system-principal sandboxes, so the translation framework's assignments to
Promise silently failed and loading the framework threw on
Zotero.Promise.method.
2026-08-03 11:48:41 -04:00
Dan Stillman
6fbe5e4135 fx153: Match XUL boolean attributes in selectors without a value
Bug 2008041 made disabled, checked, hidden, collapsed, and selected
boolean attributes, so their value is empty and [disabled="true"] no
longer matches. Nothing sets any of them to "false", so matching on
presence alone is equivalent.
2026-08-03 11:48:40 -04:00
Dan Stillman
567cc2d43a fx153: Set XUL hidden/collapsed as boolean attributes
Bug 2008041's change to boolean attributes also covers hidden and
collapsed, whose UA selectors became [hidden] and [collapsed], so
setAttribute('hidden', false) now hides the element. Switch the setters
that can be passed a falsy value to toggleAttribute(), read them with
hasAttribute(), and match the [collapsed=true] selectors in our own
stylesheets to the new presence-only form.
2026-08-03 11:48:40 -04:00
Dan Stillman
f245f5c2b2 fx153: Set XUL checked/disabled as boolean attributes
Bug 2008041 made them html-style boolean attributes, so presence alone
means true and setAttribute('disabled', false) now disables the element.
Switch the sites that can be passed a falsy value to toggleAttribute(),
read them with hasAttribute(), and drop a disabled="false" from markup.
Sites that only ever pass true are left alone.
2026-08-03 11:48:40 -04:00
Dan Stillman
bcd8ad20ec fx153: Use command events for checkbox changes
Bug 2009806 moved XUL checkbox click handling into C++ and dropped the
CheckboxStateChange event, so listen for command instead. The event now
only fires on user interaction, so the tests have to click the checkboxes
rather than assign to .checked.
2026-08-03 11:48:40 -04:00
Dan Stillman
eac3838f00 fx153: Use ChromeUtils.predictRemoteTypeForURI() in HiddenBrowser
Bug 2011326 moved remote type prediction into C++ and removed
E10SUtils.predictOriginAttributes() and getRemoteTypeForURI(). The
resulting TypeError meant nothing ever loaded in the hidden browser.
2026-08-03 11:48:40 -04:00
Dan Stillman
bca6537e6d fx153: Allow eval() in the parent process
Translators are evaluated with the system principal, which worked until
now only because nsContentSecurityUtils::IsEvalAllowed() exempted any
profile with "JS hacks" present -- which for us meant
xpinstall.signatures.required being false. Bug 2038660 dropped that
exemption, so ask for eval explicitly.
2026-08-03 11:48:40 -04:00
Dan Stillman
5ea6243384 fx153: ownerGlobal -> documentGlobal
Bug 2033243 renamed the attribute and moved it from EventTarget to Node.
2026-08-03 11:48:40 -04:00
Dan Stillman
3bb3b4f2c9 fx153: Move off the removed synchronous nsILoginManager methods
findLogins() now throws, and removeLogin()/modifyLogin()/removeAllLogins()
are Async-suffixed and return promises. Without this, Zotero couldn't read
the stored API key and showed the corrupted-logins-database error on
startup.

There's no synchronous read left, so Zotero.Sync.Runner.enabled -- which is
consulted from places that can't await -- now reads a value that
hasCredentials() caches.
2026-08-03 11:48:39 -04:00
Dan Stillman
1c005c7e21 fx153: Re-enable chrome: DTD loading
Bug 2043845 put the FORCE_ALLOWED_DTD content policy type behind a pref
that's off for everything but Thunderbird, so NS_NewChannel() refused the
load with NS_ERROR_CONTENT_BLOCKED and the XML parser silently skipped the
entity. Custom elements whose markup uses DTD entities then failed to
parse with "not well-formed XML".
2026-08-03 11:48:39 -04:00
Dan Stillman
14b43c9559 fx153: Disable Firefox's baseline CSP for chrome: documents
Bug 2038660 applies "script-src chrome: resource: moz-src:" to every
chrome: document, which blocks inline <script>s and inline event
handlers, so the main window loaded but ran none of its scripts.

We should move our inline scripts and event handlers into separate files
so that we can drop the pref.
2026-08-03 11:48:39 -04:00
Dan Stillman
b7232f130f fx153: Don't remove the Pocket actor, removed from Firefox 2026-08-03 11:48:39 -04:00
Dan Stillman
c192127ab1 fx153: Take over <search-textbox>, removed from Firefox
Mozilla removed the widget and its stylesheet in bug 1968916 without a
drop-in replacement, so bring both into Zotero. The two rules that lived
in Firefox's UA sheets (text-shadow and -moz-window-dragging) move to
_search.scss, and the aria-autocomplete removal that fetch_xulrunner
used to patch in is now part of the widget itself.
2026-08-03 11:48:39 -04:00
Dan Stillman
d701c43862 fx153: Restore autocomplete popup sizing from the anchor element
Firefox now sizes autocomplete popups to their content (bug 2045828), so
add the --panel-width calculation back instead of adjusting it.
2026-08-03 11:48:39 -04:00
Dan Stillman
06848ce17f fx153: Update about:addons patches for the ES module split
aboutaddons.js and aboutaddonsCommon.js were split into
aboutaddons-utils.mjs plus per-component modules under components/, and
the addon details template moved out of aboutaddons.html into
addon-details.mjs.

The unsigned-addon warning is now suppressed through Firefox's own
isUnsignedWarningMessageDisabled(), which is otherwise gated on
MOZILLA_OFFICIAL.
2026-08-03 11:48:38 -04:00
Dan Stillman
408179ad09 fx153: Anchor the actor-removal patterns in ActorManagerParent
AboutTranslations and the PictureInPicture actors moved out of the
JSWINDOWACTORS literal into conditional assignments after it.

Anchor every pattern to the start of the line while we're here, since
'PictureInPicture: {' matched the MozTogglePictureInPicture event name in
the new file and silently deleted everything through the next '  },'.
2026-08-03 11:48:38 -04:00
Dan Stillman
242bbccda5 Update Mac and Linux builds to Firefox 153.0esr 2026-08-03 11:48:38 -04:00
Dan Stillman
fdd3434349 Update English strings for Transifex 2026-08-03 11:12:15 -04:00
Dan Stillman
c9d5aa3c8e Use general strings for file renaming prompt buttons
The invalid-template prompt had its own strings for "Go Back" and
"Reset to Default".
2026-08-03 11:08:55 -04:00
Dan Stillman
d54327a045 Fix retracted-item icon spacing in items list
Some checks failed
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
The gap after the icon came from whitespace baked into the legacy
cross.png, so it disappeared when the icon switched to the
edge-to-edge cross.svg and the title abutted the icon.
2026-08-01 10:42:29 -04:00
Mynacol
b5ea455c1f
Fix release build on x86 mac (#5926)
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
While trying to [fix](https://github.com/NixOS/nixpkgs/pull/519431) another issue in the Zotero package for nixpkgs, I discovered a new bug. When setting a non-`source` update channel, the build fails on MacOS with x86 architectures.

The build fails with:
```
source not found twice in ChannelPrefs

source

stringWithCString:encoding:
```
Which definitely means it is caused by the `strings` command [here](https://github.com/zotero/zotero/blob/main/app/mac/set-channel-prefs-channel#L29).

When manually executing the strings command on the file, I can repeat getting only one `source` result on x86 mac, while getting two on arm64 or linux. After digging around, I tried with the flag `-arch all`, and both `source` values are found on x86 mac. Full command: `strings -n 3 -arch all $binary`.
2026-07-31 13:41:33 -04:00
Adomas Venčkauskas
9c03a93e00
Fix tree multi-selection collapse/expand behaviour (#5983)
Closes #5974.

Additional fixes for broken item tree behaviour when multiple items are
selected, and changing focus with ctrl/cmd-arrow keys.

Aligned Collection Tree/Virtualized Tree collapse/expand behaviour when
multiple containers are selected, one of them is focused, and arrow key
left-right is pressed, to the behaviour in Item Tree - now all of them
are collapsed/expanded.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-31 13:25:01 -04:00
Martynas Bagdonas
a7cae87be8
Improve undo/redo in the note editor (#3398)
- Fix "redo" command not enabled in the note editor
- Centralize note editor undo and redo commands
- Update note editor submodule

Co-authored-by: Tom Najdek <tom@doppnet.com>
2026-07-31 14:22:53 +02:00
Dan Stillman
4ec5ba9c27 Don't fail translation on open-access lookup error 2026-07-30 20:59:07 -04:00
Dan Stillman
7abcf23ef7 Move PMC lookup after open-access lookup
The PMC lookup in #5906 was added before the OA lookup, but the OA
lookup often returns a PMC URL of its own (including, at least now, for
the DOI given in the PR), with a direct file URL rather than a page to
scrape, so try it first and fall back to the PMCID-derived URL only when
it turns up nothing.
2026-07-30 20:56:28 -04:00
Dan Stillman
8bebaf0ead Fix open-access lookup for PMCID items during in-app translation
Since #5906, the 'oa' resolver list can start with a plain URL object
for items with a PMCID, but _getOpenAccessPDFURLs() and
saveOpenAccessAttachment() called resolvers[0](), which threw for those
items -- skipping the lookup silently in the first case and failing the
save in the second.
2026-07-30 20:55:16 -04:00
Dan Stillman
80658e928e Handle download challenge on PubMed Central
PMC now returns a proof-of-work challenge page in place of the PDF.
2026-07-30 16:49:59 -04:00
Dan Stillman
4388f6da20 Drop redundant charset extraction in blobToText()
readAsText() already reads the charset from the blob's type when no
encoding is passed, so pulling it out first had no effect. The regex
was also missing a quantifier and captured a single character, which
readAsText() discarded as an unrecognized label.
2026-07-30 15:49:43 -04:00
Dan Stillman
9ed00c843d Don't update read status when using Show File
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Show File triggered a file 'open' notification, so revealing a file
moved its item to the top of Recently Read. Use a separate 'reveal'
event, which file syncing still observes to queue a modification check.

https://forums.zotero.org/discussion/133006/
2026-07-30 13:43:41 -04:00
Dan Stillman
08880f7a0e Update citeproc.js to include Juris-M/citeproc-js#286
Ignore unknown style-level attributes instead of failing to load the
style, so that additive style-level attributes can be adopted in future
CSL versions without breaking deployed processors [1].

[1] https://discourse.citationstyles.org/t/faster-csl-specification-updates/2050
2026-07-30 09:43:01 -04:00
Tom Najdek
c39f16891f Extend the template engine and add template validation (#5989)
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
- Fix multiple potential scenarios causing a template engine crash
 - Add support for specifying string literals in the template engine
 - Validate `if/else/elseif` order and matching clause closures
 - Validate to ensure every `{{` is properly closed with a matching `}}`
 - When a template is invalid, display a warning, do not offer batch-renaming tools, do not update synced setting
 - When a template is invalid, prompt the user to fix or reset the template when

Closes #5965
2026-07-29 13:30:12 -04:00
Tom Najdek
efcfaec5d4 Move templating engine to a standalone, lazy-loaded module (#5989) 2026-07-29 13:30:03 -04:00
Dan Stillman
dc6d55a137 Remove collectionTreeRow from the plugin menu context
Menu plugins reading it acted on one arbitrary row of the selection.
Reading it now throws and names collectionTreeRows, which the context
has already provided since multi-collection selection landed.

The context now copies property descriptors rather than values, since
copying values would evaluate the throwing collectionTreeRow getter for
every menu.
2026-07-29 13:22:58 -04:00
Dan Stillman
783da0e66e Don't offer Remove from Collection if saved search is selected
Collections and saved searches can be selected together, and the
search's items don't need to be in any of the collections, which would
result in search-only items being moved to the trash.
2026-07-29 13:12:47 -04:00
Dan Stillman
1d97f6448e Replace ItemTree#collectionTreeRow with a validated view mode
The view-wide branches (trash, duplicates, feeds, Recently Read) read
the first selected row, which multi-collection selection preserved with
a getter rather than updating. setCollectionTreeRows() now derives the
kind of view the selection adds up to, throwing if the rows disagree,
and those branches test it.
2026-07-29 13:12:47 -04:00
Dan Stillman
5d1a2cba32 Remove the singular collection tree selection getters
Multi-collection selection left .collectionTreeRow and similar in place
to reduce breakage, but that would just leave plugins and other callers
potentially broken when multiple rows were selected. All singular
getters now throw and say what to use instead. getSelectedLibraryIDs()
was added to replace getSelectedLibraryID().
2026-07-29 13:12:47 -04:00
Dan Stillman
e45871fa9d Don't treat a selected saved search as a collection
Collections and saved searches can be selected together, so a
collection-item change called getDescendents() on rows that don't have
it and compared search IDs against collection IDs.
2026-07-29 12:55:41 -04:00
Dan Stillman
c92c75eb36 Mark read attachments in every selected library
Recently Read rows can span libraries, but read attachments were marked
as matches for the first row's library only, leaving the rest as grayed
context rows.
2026-07-29 12:55:40 -04:00
Dan Stillman
3da869b2e6 Keep library headers in place when the items list is re-sorted
Header and spacer rows stayed in the row list during a sort and were
ordered as if they were items, so a column sort could move them out of
position.
2026-07-29 11:33:15 -04:00
Dan Stillman
85a33e1586 Don't treat library header and spacer rows as items
Code walking the items list assumed every row was an object, so the
headers and spacers shown whenever more than one row is selected got
picked up as items: getSortedItems() passed them to export and report
generation, restoring from the trash called item methods on them, and
the item pane counted them in "N items in this view".
2026-07-29 11:33:14 -04:00
Dan Stillman
3bf74a3e46 Local API: Actually exempt file uploads from Zotero-Server-ID
The requireServerIDOnWrite flag was added to the base endpoint class but
never set on the upload receiver, so uploads sent without the header
were still rejected with 428.
2026-07-29 10:18:45 -04:00
Dan Stillman
ae98136ec8 Save uploaded objects to the sync cache on an unchanged response
The cache-existence check didn't await getCacheObject(), so the
promise was always truthy and no cache object was ever saved for
objects the server reported as unchanged. Without a cached version,
later remote changes to those objects produced avoidable conflicts.
2026-07-29 00:06:42 -04:00
Dan Stillman
2fb8d69a93 Auto-resolve lastRead conflicts without a cached version
lastRead conflicts were auto-resolved only when a cached version of
the object was available, so differing read times could still trigger
the conflict resolution window.
2026-07-28 23:56:29 -04:00
Abe Jellinek
681c48f1c2 Local API: Don't validate Zotero-Server-ID for file uploads
Some checks are pending
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
https://github.com/urschrei/pyzotero/issues/344#issuecomment-5108140508
2026-07-28 12:34:40 -06:00
Dan Stillman
caeffa9f41 Citation dialog: Hide the citation preview until the citation has items
Show the preview area and its toggle button only once there is
something to preview, instead of showing a "Preview" placeholder in an
empty dialog.
2026-07-28 12:29:59 -04:00
Dan Stillman
b66519cb4c Citation dialog: Hide the list layout divider when the list is empty
With nothing in the list, the layout's divider sat directly above the
bottom area's divider, showing a doubled line below the input field.
2026-07-28 12:18:21 -04:00
Dan Stillman
db5e7edb49 Citation dialog: Show number of items the + button will add in a tooltip
The citation dialog's + column now has a tooltip that reflects how
many items a click will add based on the current selection, e.g.,
"Add 3 Items to Citation".
2026-07-28 12:18:13 -04:00
Dan Stillman
7925b4e9ac Citation dialog: Add all selected items on + click in Library mode
Clicking the + button on a row that is part of a multi-item selection
now adds all selected items to the citation, matching the existing
list-mode behavior. Clicking + on an unselected row still adds only
that item.
2026-07-28 12:16:41 -04:00
Dan Stillman
93d66e3999 Citation dlg: Don't enter locator-typing mode after adding multiple items
With focus moving back to the input after a multi-item add, a typed
number was applied as a page locator to every just-added item. It now
starts a search instead. Single-item adds still enable the typed-locator
shortcut.
2026-07-28 12:16:11 -04:00
Dan Stillman
938c414e0c Citation dialog: Fix post-add scroll crash that left focus in the items list
_scrollItemTreeToRow() parsed the row index from the wrong rowID
segment, so it threw on every call, and adding items from the items list
never returned focus to the input as intended.
2026-07-28 12:16:01 -04:00
Dan Stillman
8526a022ec Move local API authorization prompt button labels to general strings 2026-07-28 10:04:02 -04:00
Tom Najdek
75b78b410b
Ensure emoji detection works as intended with Unicode 17 implementation (#6004) 2026-07-28 10:02:35 -04:00
Abe Jellinek
a37a9e782a Add Zotero-Server-ID, require for writes, validate when provided
Every local API response includes a stable per-database server ID so
that clients can partition cached data, especially local object
versions, by Zotero instance. Write requests must include the ID, and
when it's provided on any request it must match the current server.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-28 09:57:44 -04:00
Abe Jellinek
77f2432103 Local API: Support write requests
Add support for item, collection, and search writes (POST/PUT/PATCH/
DELETE), tag deletion, full-text writes, and the three-phase file
upload flow. Writes require a local API key granted by the user via a
confirmation dialog (POST /api/local/authorize) and follow the web
API's concurrency model, with If-Unmodified-Since-Version and
per-object version preconditions checked against local versions.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-28 09:57:44 -04:00
Abe Jellinek
9dd17a212e Track data object versions locally
Add a clientVersion column for items, collections, searches, and
libraries, incremented once per library per transaction on every
object save or deletion. The local API reports these versions instead
of synced versions -- in object JSON, format=versions, since=
filtering, and Last-Modified-Version -- since synced versions don't
reflect local changes and are 0 for unsynced objects. Group metadata
responses keep reporting the synced group version, which has no local
counterpart.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-28 09:57:43 -04:00
Dan Stillman
12114f6a86 Fix binary file corruption in multipart/form-data parsing
_decodeMultipartData() trimmed each part body, mangling file content that
begins or ends with a whitespace byte (e.g., a PDF ending in a newline).
2026-07-28 09:57:43 -04:00
Dan Stillman
ce20a5f228 Ship legacy Safari App Extension alongside the web extension on Mac
Developer ID-signed Safari web extensions load only in Safari 18.4 and
later -- older Safari blocks them at the code-signing layer -- so macOS
11 and 12 users, whose Safari versions top out at 16.6 and 17.6, lost
the connector with the web extension conversion.

Embed the prebuilt legacy App Extension ($SAFARI_APP_EXTENSION, set by
the deploy scripts) at Contents/PlugIns/ZoteroSafariExtension.appex with
the historical bundle identifier, and move the web extension to
Contents/PlugIns/ZoteroSafariWebExtension.appex with a new
.SafariWebExtension identifier. The web extension's
SFSafariAppExtensionBundleIdentifiersToReplace key causes Safari
versions that can load it to hide the App Extension and migrate its
enabled state, while older Safari shows only the App Extension.

Also remove the removed-files entries for the App Extension's
resources, which would otherwise delete them on update.

https://forums.zotero.org/discussion/132925/
2026-07-28 07:24:27 -04:00
Dan Stillman
91d7e73d26 Strip com.apple.FinderInfo xattrs from files in Mac disk images
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
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. codesign --verify --strict rejects FinderInfo as
detritus, and it can cause Safari to fail to load the web extension.
(Reported by a user with Safari 17.6 on macOS 12. No other reports from
the beta, so maybe not universal.) The Safari App Extension apparently
wasn't affected, since it shipped with the same attributes for years.

Convert the hybrid image to a read-write image, mount it, strip the
attributes from its files, and compress from that. The volume header is
untouched, so the open-folder flag that makes Finder open the volume
window on mount is preserved.

Only fresh installs from the DMG were affected. The updater writes fresh
files without the attributes, so copies updated in place were clean.
(Notably, this is the reverse of the post-update extension breakage for
which the standard advice has always been to delete Zotero.app and
redownload. Following that advice is exactly what resulted in a broken
copy here.)

https://forums.zotero.org/discussion/132925/
2026-07-27 15:31:18 -04:00
Dan Stillman
566115dc7c Use a fixed menu padding on Windows and Linux
Some checks failed
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
The toolkit's default menuitem padding is em-based, so it balloons
with the UI font size in menulist dropdowns (e.g., the item type
menu), which inherit that font, forcing long lists to scroll. macOS
uses a fixed value on all menus and is unaffected. Match that on
Windows and Linux, slightly roomier, so every menu stays compact
regardless of font size.

https://forums.zotero.org/discussion/132797/request-for-compact-view-in-dropdown-menus
2026-07-24 13:30:36 -04:00
Dan Stillman
512a2444d8 Add timeout to HiddenBrowser::getPageData()
The queries wait for the document to be ready, so a page that never
finished loading would hang the caller -- e.g., full-text indexing --
forever.
2026-07-24 13:11:50 -04:00
Dan Stillman
9eea7d9a0c Disable JavaScript when indexing HTML files
Page scripts otherwise run during indexing and can hang it. JavaScript
was disabled here in 2a7f31813e for the same reason, but that was lost
in the fx-compat rewrite (13adfd131c).

https://forums.zotero.org/discussion/132901/
2026-07-24 13:11:50 -04:00
Dan Stillman
3a54a5ecb1 Fix allowJavaScript option in HiddenBrowser
allowJavaScript: false didn't disable scripts for a system-principal
document, such as a blob: URL created from chrome code, which runs
scripts regardless of the allowJavascript flag. Sandbox the document
instead, forcing a null principal and blocking script execution.
2026-07-24 13:11:50 -04:00
Dan Stillman
0b32e93cb1 Batch retraction refresh notifications instead of one per item
The retraction checker fired a separate 'refresh' notification for
every matched item on each check, including already-known retractions
that hadn't changed. At startup this produced a burst of events that
forced observers like the item tree to rebuild repeatedly. Batch each
operation's changes into a single notification, and notify only for
items whose retraction state actually changed.
2026-07-24 10:56:36 -04:00
Dan Stillman
a81af0c268 Don't show Extra migration progress window unnecessarily
The candidate scan counted items whose Extra content parsed as fields
but couldn't actually be migrated (e.g., fields already set on the
item), so the window could appear on every global schema update without
changing anything.

Also use a dedicated headline instead of "Upgrading database…", and show
the window only if the migration is still running after 500 ms.
2026-07-23 23:21:01 -04:00
Martynas Bagdonas
ea4b301f73 Avoid SDT test fixture updates on version bumps
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-07-23 20:24:21 +03:00
Dan Stillman
4f825ad50c Don't throw getting the filename of a stored file with a corrupt path
A stored file's path is normally 'storage:<filename>', which is parsed
as a string, but some ancient libraries apparently have relative paths
('../.../foo.pdf') that fell through to PathUtils.filename() and threw
NS_ERROR_FILE_UNRECOGNIZED_PATH, breaking syncing.

Resolve stored-file leaves as strings regardless of prefix and reserve
PathUtils.filename() for linked files, whose paths are genuine absolute
paths.

https://forums.zotero.org/discussion/132861/zotero-sync-issue-id-1987012678
2026-07-23 11:12:12 -04:00
Dan Stillman
6fb30346e6 Bundle Safari web extension instead of Safari App Extension
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
The connector is now a Safari web extension, so the bundled appex is a
static wrapper stub whose placeholder resources are replaced at build
time with a connector build via $SAFARI_EXT_RESOURCES. The stub only
needs to be rebuilt when the native wrapper project changes, not for
connector releases.
2026-07-22 15:11:23 -04:00
Dan Stillman
a4b39b91f3 Exclude trashed items from tag autocomplete suggestions
https://forums.zotero.org/discussion/132875/some-tag-suggestions-appearing-capitalised
2026-07-22 11:46:52 -04:00
Dan Stillman
146520aed5 Reload quick search when changing the search mode
Changing the quick-search mode dropdown didn't re-run the current
search, since the filter was applied without a mode and the unchanged
search text made setSearch() report no change.

Regression from 5ca1fbb167

https://forums.zotero.org/discussion/132873/beta-quick-search-isnt-reloaded-when-changing-search-mode
2026-07-22 11:14:23 -04:00
Dan Stillman
cddb19bf36 Only reject and strip actual directory paths in stored-file filenames
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
ee68452b treated any backslash as a directory separator, in both the
attachmentPath setter and the userdata 128 migration. But backslashes
are technically valid on Linux/macOS and appear in real filenames (due
to LaTeX in titles, etc.), so that would wrongly throw on or truncate
them. Only a forward slash (never valid in a filename) or a Windows
absolute path (drive-letter or UNC prefix) reliably indicates a leaked
directory path, so treat just those as paths and leave bare backslashes
alone. We should make sure we're running getValidFileName() everywhere
to avoid saving those.
2026-07-21 16:02:40 -04:00
Dan Stillman
e81e72dccf Fix negated Title/Creator/Year and Any Field advanced searches
These conditions expanded into an OR-group across their underlying
fields, so a "does not contain"/"is not" operator matched almost every
item: any item missing one of the fields satisfied that field's negated
condition. Use an AND-group for negative operators, so the value must be
absent from every field.

https://forums.zotero.org/discussion/132835/
2026-07-21 14:15:45 -04:00
Dan Stillman
40974e493d Fix Cmd/Ctrl-A not selecting all libraries when feeds are visible
Selecting all libraries pulled in feed rows, which belong to a different
visibility group and so can't be shown together, collapsing the
selection back to a single row. Constrain the all-libraries scope to the
current selection's visibility group. Also preventDefault() the shortcut
so it no longer falls through to the cmd_selectAll key command, which
threw NS_ERROR_FAILURE.

https://forums.zotero.org/discussion/132843/beta-cannot-select-all-libraries
2026-07-21 14:04:00 -04:00
Dan Stillman
621c0fbb91 Fix storageLocal test broken by ee68452b0
The download-rename test used a backslash as its sample invalid
character, which now throws in the attachmentFilename setter. Use a
colon instead, which still tests download-time filtering.
2026-07-21 11:08:42 -04:00
Dan Stillman
ee68452b05 Disallow slashes in stored-file attachment paths
Someone ended up (via a plugin, presumably) with stored-file attachments
with a full path after 'storage:', which broke file syncing. Throw when
setting a stored-file path containing a slash, and strip paths from
existing filenames in a schema update step. No particular reason to
think that the file with that basename will exist in the storage dir,
but at least it will be looking for the right file and not be totally
broken.

Separately, the dataserver will clean up filenames with full paths and
block going forward.

https://forums.zotero.org/discussion/132822/reference-sychronization-error
2026-07-21 10:40:59 -04:00
Dan Stillman
5877952954 Add accented stop-words to abbreviations word list
Accented stop-words weren't matched by the existing unaccented
entries (e.g., "fur"), so automatic journal abbreviation kept and
capitalized them: "Jahrbuch für Heimatkunde" became "Jahrb. Für
Heimatkunde" instead of "Jahrb. Heimatkunde".

https://groups.google.com/g/zotero-dev/c/uP18QEKe2JU/m/AsoGWxd9AwAJ
2026-07-20 22:52:51 -04:00
Tom Najdek
ccb592ba68
Advanced Search: Use dedicated WCAG-compliant colors for a warning (#5990)
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-07-20 23:37:23 +02:00
Abe Jellinek
ddcdea3795 Read Aloud: Reword highlight granularity to "Highlight current" 2026-07-20 13:43:39 -04:00
Abe Jellinek
14c6167a4c Update reader submodule 2026-07-20 10:50:56 -04:00
Abe Jellinek
5a785c4e0f Read Aloud: Support highlight granularity customization & timestamps 2026-07-20 10:50:56 -04:00
Dan Stillman
ccbeab61e2 Stop full-text index queue drains that aren't making progress
Some checks failed
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
If queued items can't be indexed and removed for any reason, the
startup and background drains would otherwise run forever. Stop after
three passes without the queues shrinking; the items are picked up
again on the next trigger.
2026-07-18 14:33:21 -04:00
Dan Stillman
59c1801a10 Fix looping re-extraction of full-text queue items with no text
If a queued attachment was missing its cache file and re-extraction
produced no text, nothing was recorded in the index state, so the
content-index queue selected the same items forever and the startup
drain never finished.

https://forums.zotero.org/discussion/132808/
2026-07-18 14:33:13 -04:00
Dan Stillman
0dc256166a Check content-index drain time budget between items
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
The budget was only checked after each 50-item chunk, so a chunk of
large documents could tie up the main thread for longer than maxTime.
2026-07-17 14:07:38 -04:00
Dan Stillman
8437de1a4f Clarify Index Statistics labels
Say "Attachments indexed"/"Partially indexed" instead of
"Indexed"/"Partial", reverse "File or full-text content not
available", and list notes last so the attachment rows are contiguous.
2026-07-17 13:58:42 -04:00
Abe Jellinek
4a7c359ae2
Add Clear All Last Read Dates… to Recently Read context menu (#5879) 2026-07-17 13:52:37 -04:00
Dan Stillman
f171e7b5d8 Clear read-only attribute on Windows if file removal fails
A stale read-only file in the temp directory caused the bundled styles
update to fail at every startup, which also broke Quick Copy and note
translators. Try to clear it automatically in
Zotero.File.removeIfExists().

https://forums.zotero.org/discussion/132781/
https://forums.zotero.org/discussion/132798/
2026-07-17 13:19:25 -04:00
Martynas Bagdonas
c233fcf5f0 Fix reopening reader tabs during queued close notifications
Fixes https://forums.zotero.org/discussion/comment/515471/#Comment_515471
2026-07-17 20:11:49 +03:00
Dan Stillman
30b5bf4bb9 Keep progress popups from floating over other apps' windows
The progress popup was opened with alwaysontop, which is system-wide,
so a long-running popup (e.g., full-text indexing) stayed on top of
other apps' windows after switching away from Zotero. Open it as a
dependent window instead, which keeps it above only the Zotero window.
2026-07-17 12:45:24 -04:00
Dan Stillman
d739fed03e Tweak wording in full-text indexing progress window 2026-07-17 12:41:30 -04:00
Dan Stillman
bcfa43b7e9 Fix routing of search terms mixing CJK and non-ASCII words
A term mixing CJK with non-ASCII words (e.g., Cyrillic plus Japanese)
was routed to the CJK index with only its CJK characters, matching
every document that contained those. Treat any non-CJK letter or digit
as making a term mixed-script, which falls back to a literal scan of
the cached text.
2026-07-17 12:41:30 -04:00
Dan Stillman
0ce289a7fe Use a word index for full-text content search
Replace the trigram FTS5 index for attachment content with a unicode61
word index, so terms match whole words with the final token as a prefix
("archive" matches "archives", but "ion" doesn't match "condition"), as
in the pre-FTS5 word index. A multi-word phrase gets adjacent-token
candidates from the index and is then verified against the cached text
of just those items, since FTS5 ignores what separates adjacent tokens;
the verification treats whitespace and hyphen runs as equivalent
(they're frequently extraction layout or styling) but requires other
punctuation to match literally. Notes keep the trigram index and CJK
matching is unchanged; the index database version is bumped so the
index is rebuilt.

Follow-up to #5979
2026-07-17 12:41:19 -04:00
Dan Stillman
228799774f Sign both per-arch JNA .jnilibs in LibreOffice plugin during Mac build
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
JNA 5.x ships per-architecture macOS native libraries instead of a
single com/sun/jna/darwin/ directory, so the notarization re-signing
step failed with "filename not matched".

This step can be removed entirely once the plugin ships a jna.jar
without the macOS natives, which are never loaded.
2026-07-16 12:54:47 -04:00
Dan Stillman
f8f83cceac Update global schema (locales) 2026-07-16 12:29:53 -04:00
Dan Stillman
10b1dbb785 Update locales from Transifex 2026-07-16 12:28:02 -04:00
Dan Stillman
994d557e92 Say "Migrating" instead of "Updating" user data tables in debug output
"Updating" appears all over debug output, so this makes schema
migrations easier to search for.
2026-07-16 11:46:47 -04:00
Dan Stillman
fb20a84ab7 Handle stale WAL files in corrupted-database recovery
SQLite replays a leftover -wal file (e.g., from a force-quit) into
whatever file next occupies the database path, so copying a backup
over zotero.sqlite produced a corrupted-database error, and the
automatic restore recreated the same mismatch and failed every time.

When corruption is detected, check if the database file is valid without
its journal files, and if so, save a verified copy and restart, swapping
the copy in at the next startup before the database is reopened. (The
copy is made before shutdown because SQLite automatically checkpoints
the WAL into the database file when the last connection closes, which
would write the stale WAL data into the file.) Otherwise, move journal
files along with the .damaged file to clear them from the main path
before restoring from the automatic backup or creating a new database.

Since a mismatched WAL can cause subtle data damage without errors, also
run a full integrity check at startup after an unclean shutdown. Skip
the explicit close-time WAL checkpoint once corruption has been flagged,
and truncate the WAL during idle maintenance to limit stale WAL data.
2026-07-16 11:43:31 -04:00
Dan Stillman
a2c7e0c00e Support saving and restoring main-window progress meter state
showZoteroPaneProgressMeter() now returns a token capturing the
previous message, meter mode and position, and owner, and
restoreZoteroPaneProgressMeter() restores that state if no other
operation has changed the display since, with nested tokens restoring
in reverse order.
2026-07-16 11:43:31 -04:00
Dan Stillman
6c5d160895 Fix citing and locale selection for locales without a CSL locale
The CSL locales repository replaced locales-sr-RS.xml with script
variants, so citing with a stored or default 'sr-RS' locale loaded no
terms and failed with an et-al error. Resolve unavailable locales to
the closest available CSL locale (sr-Cyrl-RS for sr-RS) when rendering
and in locale selectors.

https://forums.zotero.org/discussion/comment/515598/#Comment_515598
2026-07-16 11:28:21 -04:00
Dan Stillman
d3b04e7748 Date forced backups from the backup time
Backup copies preserve the database file's mtime, and the backup
interval is measured from the backup file's mtime, so a forced
pre-update backup of a long-idle database could be rotated out a day
early. Regular backups still keep the database mtime, which the
unchanged-database check relies on.
2026-07-15 22:48:08 -04:00
Dan Stillman
404fb4c3ae Fix backup before user-initiated DB repair
The backup before repairing integrity-check errors from the Advanced
pane has thrown a TypeError since backUpDatabase() started requiring an
options object in 8.0, aborting the repair. Also force the backup so a
recent automatic backup can't cause it to be skipped.
2026-07-15 22:43:24 -04:00
Dan Stillman
d78cceac71 Don't repeat forced pre-update backup after a failed schema update
If a schema update committed the userdata upgrade but kept failing in a
later step, each restart forced another rotation backup of the
now-upgraded database, deleting the pre-upgrade backup after numBackups
restarts. Record the pending target versions in the database when
making a forced backup and skip the backup while they're unchanged, so
the pre-update backup survives the normal rotation period.
2026-07-15 22:42:26 -04:00
Dan Stillman
cae3ab6160 Fix layout of main-pane progress bar with text label
E.g., "Upgrading database…"
2026-07-15 18:24:21 -04:00
Dan Stillman
73c45f8ec9 Show "Upgrading database…" based on upgrade duration
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Replace the manually maintained minorUpdateFrom flag, which was easy to
forget to update when adding a slow upgrade step and couldn't account
for database size or hardware. The message is now shown for any upgrade
still running after 500 ms.
2026-07-15 17:58:43 -04:00
Dan Stillman
ea2ee7391d Force a regular backup before all DB upgrades, and drop versioned backups
Versioned backups (zotero.sqlite.<version>.bak) were kept until the
next userdata upgrade, potentially bloating the data directory by
gigabytes, while minor upgrades made no backup at all. Now any userdata
upgrade, integrity check, or global schema update forces a rotation
backup. Since versioned backups ignored backup.numBackups, setting that
to 0 now fully disables backups, including before upgrades.
2026-07-15 17:43:29 -04:00
Dan Stillman
02fb0e92ed Rework the Index Statistics preferences pane
Show indexing progress as a bar (items in the search index out of those
that will be) that resolves to "up to date", alongside
Indexed/Partial/Notes counts and a "File or full-text content not
available" count for attachments with no local file or full-text
content. While the pane is open, trigger indexing directly so it
advances as the user watches.

Remove the Rebuild Index and Clear Index buttons. Rebuild Index marked
all content unsynced and re-uploaded it, triggering a server reindex and
re-download on other devices, and the per-item reindex paths now cover
what needed it -- raising a length limit re-extracts only the affected
items, and unindexed items are indexed automatically. Clear Index wiped
the local index and extracted-text cache files, a destructive action
that shouldn't be exposed.
2026-07-15 15:36:40 -04:00
Dan Stillman
3ec407d861 Add full-text searching of note content
Note content is indexed into fulltext.sqlite, making note searches
accent- and case-insensitive and matching the note's plain text rather
than its HTML markup. To avoid re-indexing on every auto-save, a save
flags the note for background indexing, and searches match a flagged
note from its normalized text in memory until it's indexed.

Closes #378
2026-07-15 15:36:40 -04:00
Dan Stillman
5ade25f5f5 Drop the unused savedSearchConditions required column (#5962)
The condition `required` flag was removed in #5962, but the column was
kept so older clients could still read the database. The full-text
search changes bump the userdata compatibility version, locking out
those clients, so the column can now be dropped.
2026-07-15 15:36:40 -04:00
Dan Stillman
7c2a1d127d Add full-text content search via FTS5
Index attachment content into a contentless trigram FTS5 table in a
separate, attached fulltext.sqlite, normalized so matching is accent-
and case-insensitive. For content containing CJK characters, a companion
'ascii'-tokenized table holds bigrams so 1-2 character CJK queries, which
the trigram tokenizer can't match, still work. The extracted text still
lives in the .zotero-ft-cache files, so the index is fully derived and
rebuildable.

Use the FTS index for the fulltextContent condition, falling back to the
cached-text scan for queries too short to index, and point quick
search's content matching at the FTS index in place of the now-removed
word index. (One side effect: quick search now matches attachment
content by substring rather than by word.)

Already-extracted content is migrated into the index at startup, slowing
down on active usage. A background queue then extracts not-yet-indexed
attachments gradually when Zotero is idle. Attachments with no local
file or full-text content are recorded as missing. Content downloaded
via sync is processed into the index immediately when the sync finishes,
rather than waiting for idle like it did before, so it's searchable
immediately in on-demand file-download mode.

The index DB is tied to the main DB via the local user key and rebuilt
if they don't match (e.g., after a delete-and-resync). We compact it by
running FTS5's 'optimize' command once the indexing queue drains, and we
vacuum the attached database when necessary to reclaim disk space.

Closes #2038, #2044
Addresses #1595
2026-07-15 15:36:40 -04:00
Dan Stillman
e2536ba299 Add DBConnection hooks for auxiliary attached databases
Code that attaches its own database to the main connection needs to hook
into that connection's lifecycle. Add onIdle() to run maintenance (e.g.,
vacuuming) during the main database's idle maintenance, and
addCorruptionHandler() to handle a corruption error for the attached
database (after confirming that the main database is actually corrupt,
so a corrupt attached database doesn't trigger main-database recovery).
2026-07-15 15:36:40 -04:00
Dan Stillman
ae46954167 Add DBConnection.loadExtension()
Load a bundled SQLite extension (e.g., FTS5) by name. mozStorage
disables generic extension loading but allows specific bundled ones.
Extensions are registered per connection, so a loaded extension is
remembered and re-loaded automatically after a reconnect, before
onConnect() callbacks run.
2026-07-15 15:36:40 -04:00
Dan Stillman
da88c63231 Make the collection tree filter accent-insensitive
Addresses #1300
2026-07-15 15:36:40 -04:00
Dan Stillman
984c0a7f8c Make the tag selector filter accent-insensitive
Addresses #1300
2026-07-15 15:36:40 -04:00
Dan Stillman
3bd8d641b8 Make item search accent-insensitive
Search now ignores accents, so "seance" matches "séance" and vice versa.

Text is normalized with Unicode NFKD compatibility decomposition (which
also handles typographic ligatures, superscripts, full-width forms,
etc.) plus a small map for letters NFKD leaves alone (ø, œ, æ, ß, ...)
and the fraction slash, via Z.Utilities.Internal.normalizeForSearch().
The HTML tags we support in item fields are stripped, so markup isn't
matched (#81). Typographic quotes (#29, #1876) and dashes are folded to
ASCII.

Each searchable column gets a normalized shadow column --
itemDataValues.valueNormalized, tags.nameNormalized,
creators.firstNameNormalized/lastNameNormalized, and
itemAnnotations.textNormalized/commentNormalized -- populated at write
time and matched via COALESCE(normalized, raw) LIKE. NULL is stored when
normalizing only changes case, so plain-ASCII values are only stored
once. This covers the contains/doesNotContain/beginsWith operators in
both quick search and Advanced Search.

The new columns are local-only derived data and aren't synced. Older
clients will ignore them, so this doesn't break DB compatibility.
Existing rows are backfilled after the startup sync by
Zotero.Schema.populateNormalizedSearchColumns(), which should only take
a few seconds on most databases.

Closes #29, #81, #1300, #1876
2026-07-15 15:36:40 -04:00
Dan Stillman
d0f8d438b5 Clarify naming of the full-text sync content processor
Rename the sync-delivered full-text content processor's methods and
state to "sync content" (registerSyncContentProcessor,
processSyncedContent, indexSyncedContent, getSyncedContentCacheFile,
etc.), so it reads distinctly from the local index-building queues added
for content search. No behavior change.
2026-07-15 15:36:40 -04:00
Dan Stillman
cf09a0547c Regenerate SDT test pack fixture for pdf processor version 3
The document-worker bump raised SDT_PROCESSOR_VERSIONS.pdf from 2 to 3,
so the test's "valid" pack fixture (embedding version 2) was rejected as
stale-processor during generation, failing "should generate the pack when
missing."
2026-07-15 15:01:22 -04:00
Dan Stillman
c6595d6456 Update CSL locales 2026-07-15 13:08:36 -04:00
Dan Stillman
4f45db5009 Update translators submodule 2026-07-15 12:52:54 -04:00
Dan Stillman
933e207013 Update document-worker submodule 2026-07-15 12:52:00 -04:00
Dan Stillman
0ba89211ce Fix race between lastRead update and content-type fix on file open
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
viewAttachment() fired the 'open' notification before launchFile() saved
a sniffed content type, so AttachmentReadObserver's concurrent lastRead
save could reload the item and discard the pending change, leaving the
attachment with a blank or incorrect content type.
2026-07-14 14:01:07 -04:00
Dan Stillman
bdea584a17 Update LibreOffice submodule 2026-07-14 13:39:07 -04:00
Dan Stillman
1a5e32a7b6 Update document-worker submodule 2026-07-14 11:05:43 -04:00
Dan Stillman
142f77b094 Update reader submodule (SDT) 2026-07-14 11:05:29 -04:00
Dan Stillman
6e35bd1497 Focus the pane window before the focus traversal tests
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
The #focus() Tab/Shift-Tab tests wait on focus/blur events, which fire
only while the window is active. It intermittently loses activation in
CI's Xvfb, so call win.focus() in beforeEach when the window isn't
active.
2026-07-13 17:28:00 -04:00
nexdep
2c6185d8f1
Add Attachment Storage Type search condition (#5875)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-07-13 15:57:37 -04:00
Dan Stillman
89880e3284 CI: Record which test drops the main window's activation
Temporary diagnostic for the intermittent shift-tab focus-test timeout.
A central afterEach in runtests.js tracks when
Services.focus.activeWindow stops being the main window; the shift-tab
test logs the last-active and first-inactive test in its timeout
message, to identify what leaves the pane inactive in CI.
2026-07-13 15:27:39 -04:00
Dan Stillman
5a8badae7b Revert "Activate window in focus traversal tests, fixing intermittent timeouts"
This reverts commit 03a610e696.

activate() foregrounds a window via the window manager, which CI's Xvfb
doesn't have, so activateZoteroPane() in beforeEach just hangs there
instead of fixing anything. Restore the temporary open-window logging to
try to catch what leaves the pane window inactive.
2026-07-13 15:15:13 -04:00
Dan Stillman
28867883a1 Don't let embedded note images match searches in the items list
An image pasted into a note is stored as a hidden attachment, so a
matching search condition (e.g., "# of Tags" is 0) would pull the
image's parent item into the results with no visible match.
2026-07-13 14:32:40 -04:00
Dan Stillman
cdb9134537 Add "# of Notes", "# of Attachments", and "# of Annotations" search conditions
Notes and attachments are counted on regular items, and annotations on an
attachment or across a regular item's attachments; other rows are excluded
rather than always matching with a count of 0. Trashed children aren't
counted.
2026-07-13 14:18:59 -04:00
Dan Stillman
7cdd74bd2b Add "# of Tags" advanced search condition
Closes #158
2026-07-13 13:38:22 -04:00
Dan Stillman
24fe50cfd5 Add "is empty"/"is not empty" advanced search operators
Available for text, date, and number fields and creators. Previously only
possible via a doesNotContain hack with an empty value.
2026-07-13 13:38:22 -04:00
Dan Stillman
c181aac7f6 Citation dialog: Fix grouping of selected and open items
The front-most tab's item appeared under Selected Items instead of Open
Documents, and the library tab's actual selection wasn't shown. Selected
Items now always reflects the item tree selection, and an item that's both
selected and open stays under Open Documents (sorted after the front-most
tab) so that suggestions specific to open documents (e.g., the current page
number, planned in #6001) can apply to it. Notes open in tabs now appear
under Open Documents when adding a note.
2026-07-13 11:41:19 -04:00
Dan Stillman
582333397d Citation dialog: Allow typed locator after Cmd/Alt-Tab away and back
https://forums.zotero.org/discussion/132673/
2026-07-12 18:40:06 -04:00
Dan Stillman
5fe4c901ed Update stale downloadUpdate() usage in update dialog
Some checks failed
CI / Test (shard 1) (push) Has been cancelled
CI / Test (shard 2) (push) Has been cancelled
CI / Test (shard 3) (push) Has been cancelled
CI / Test (shard 4) (push) Has been cancelled
CI / Utilities Tests (push) Has been cancelled
CI / Build, Upload (push) Has been cancelled
downloadUpdate() is now async, takes a single argument, and returns a
result code rather than a state string, so the "failed" check could
never match.
2026-07-10 15:49:19 -04:00
Dan Stillman
8eb71a588d Fix download progress in update dialog
nsIProgressEventSink's onProgress()/onStatus() no longer take a context
argument.

Fixes #3130
2026-07-10 15:49:18 -04:00
Dan Stillman
dea2fb8491 Fix build_for_deploy dying silently on an empty incrementals file 2026-07-10 15:26:52 -04:00
Dan Stillman
6225931f93 Clear the quick search from every selected collection when prefilling
Opening the Advanced Search from the quick search cleared the search
text only on the first selected row, while the quick search had applied
it to all of them, so with a multi-collection selection the other
collections stayed filtered by the now-invisible text -- both while the
advanced search was open and after closing it.
2026-07-10 11:45:29 -04:00
Dan Stillman
540735785d Prompt before discarding saved-search edits via the Advanced Search toggle
With the saved-search editor open, Cmd/Ctrl-Shift-F replaced it with the
temporary Advanced Search pane and the search field's close button
closed it, in both cases discarding the editor's working copy with no
way to recover the edits. Move the collection-switch save prompt into a
shared helper and show it before replacing or closing the editor.
Choosing Save on a collection switch now continues the switch directly
instead of relying on the save's modify notification to re-sync the view.
2026-07-10 11:45:29 -04:00
Dan Stillman
4188ef0ebc Don't drop an annotationAuthor value while its author menu is loading
onConditionSelected() populates the Author value menu asynchronously,
and until it finished, the row's default textbox was still the visible
value control, so serializing the row (running or saving the search)
replaced the loaded author with an empty value. Serialize the stored
value while the menu is pending, and invalidate the population if a
newer condition selection takes over meanwhile.
2026-07-10 11:45:29 -04:00
Dan Stillman
ccc7d0a5ad Fix focus target when a keyboard removal prunes a group
removeRow() remembered the removed row's index within its own group,
but when the removal emptied the group and pruned it, the index was
applied to the ancestor's children, focusing an unrelated row. Track the
pruned group's slot in its parent instead.
2026-07-10 11:45:29 -04:00
Dan Stillman
3a34c6a18f Run a focused button's action on Enter in Advanced Search
The pane-level Enter handler ran the default action (save in the saved
editor, submit in the temporary pane) regardless of the event target, so
Enter with focus on the Cancel button saved and closed the editor, and
Enter on Clear ran the search. Click the focused button instead, which
the platform otherwise only does for Space.
2026-07-10 11:45:29 -04:00
Dan Stillman
a6c3ad51ef Keep a group's same-entity binding visible when it no longer qualifies
The "in the same annotation" binding menu was hidden as soon as a group
no longer had two conditions at the bound level, but the group's stored
binding was still saved and applied. After switching one of two bound
annotation conditions to another type, the group looked like a plain
"Match all of the following" group but still required a single
annotation to satisfy all of its conditions, and no control was left to
see or clear the binding.

Show the menu whenever the group is bound, drop the binding only once no
condition at its level remains, and refresh the menus before
collectGroup() in updateSearch() so the first save after such an edit
doesn't serialize stale state.
2026-07-10 11:45:28 -04:00
Dan Stillman
1e0cf49a16 Don't refresh the items list when clearing an unset advanced search
setAdvancedSearch() invalidated the row unconditionally, unlike
setSearch()/setTags(), so merely opening or closing the Advanced Search
pane (which applies a null filter) re-queried and rebuilt the items
list. A passed search is still always applied, since its conditions may
have been edited in place, and setFilter() still waits for a refresh
already in flight so that callers see the settled view.
2026-07-10 11:45:28 -04:00
Dan Stillman
cc28fad6da Roll a standalone attachment's annotations up to the attachment itself
_rollUpAnyToLevel() only followed an annotation's parent when the
attachment itself had a parent, so in a search for top-level items, a
tag on a standalone attachment's annotation matched nothing.
2026-07-10 11:45:28 -04:00
Dan Stillman
d95d8f294d Fix serialization of the result level seeded for a migrated childNote
The condition seeded by _loadConditions() used `mode: undefined`, but
toJSON() only omits the "/mode" suffix when the mode is exactly false
(what parseCondition() returns), so a saved search migrated from the
obsolete childNote condition serialized -- and synced -- the condition
as "resultLevel/undefined".
2026-07-10 11:45:28 -04:00
Dan Stillman
c43f028c96 Remove dead code left over from the Advanced Search dialog
onLibraryChange() has no callers since library changes re-render the
pane, and .active/doCommand() belonged to the removed dialog's search
box.
2026-07-10 11:45:28 -04:00
Dan Stillman
8daed576ba Make first top-level joinMode win in combineConditions()
_buildQuery() already uses the first top-level joinMode for the
full-text post-filter; with multiple top-level joinMode conditions (only
creatable via the API), combineConditions() used the last.
2026-07-10 11:45:28 -04:00
Dan Stillman
2c62010c4c Fix misplaced comma 2026-07-10 11:45:28 -04:00
Abe Jellinek
0a67f4967c Read Aloud: Update skip button tooltip text
#5998
2026-07-10 11:17:23 -04:00
Abe Jellinek
64165eabf6 ItemTree#selectItems(): Clear advanced search if item doesn't match
Some checks are pending
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Build, Upload (push) Waiting to run
Extends existing handling for tag selection and quicksearch to handle
advanced search as well.

Fixes #5996 (not just for zotero://select but for all selectItems()
callers)
2026-07-09 14:11:36 -04:00
Dan Stillman
243ef0e28f Fix historical comments from 606d8f19ba 2026-07-09 13:12:43 -04:00
Dan Stillman
606d8f19ba Advanced search: Indent subcollections in Collection condition menu
Instead of just prefixing the labels with "-", indent the whole row,
including the icon. This looks better and fixes FAYT on subcollection
names.

https://forums.zotero.org/discussion/132561/
2026-07-09 13:09:24 -04:00
Dan Stillman
03a610e696 Activate window in focus traversal tests, fixing intermittent timeouts
The #focus() Tab/Shift-Tab tests dispatch synthetic keydowns and wait on
a focusin event to sync with the collections search field's async
(setTimeout) focus. Gecko only fires focus/blur events while the window
is active, so when the pane window wasn't active -- it opens in the
background on macOS/Windows and can lose activation on Linux/CI -- the
focusin never fired and the test hung until Mocha's timeout. Activate
the window in beforeEach, as the item pane and tags box focus tests
already do.

Also removes the temporary open-window logging added in e1f19f34f5.
2026-07-09 11:00:24 -04:00
Dan Stillman
c3787495cc Don't show empty-library message for empty search with no groups
hasItems() also now short-circuits with LIMIT 1 rather than counting all rows

https://forums.zotero.org/discussion/132635/zotero-beta-empty-database-message-when-no-results
2026-07-09 10:38:43 -04:00
Dan Stillman
e1f19f34f5 CI TEMP: Debugging for shift-tab focus test timeouts 2026-07-09 10:32:53 -04:00
Dan Stillman
1c9bf88888 Unpack update archives for different architectures in parallel
Some checks are pending
CI / Test (shard 1) (push) Waiting to run
CI / Test (shard 2) (push) Waiting to run
CI / Test (shard 3) (push) Waiting to run
CI / Test (shard 4) (push) Waiting to run
CI / Utilities Tests (push) Waiting to run
CI / Build, Upload (push) Waiting to run
2026-07-08 12:10:46 -04:00
Dan Stillman
39a1c56f6c Cache update diffs and compressed files across builds
Cache patches and compressed files by content hash so that they're reused
across FROM versions and releases. Cache entries are touched when used and
expire after 60 days.

The cache key includes a tag covering the xz/mbsdiff versions and the
compression options, so entries are invalidated when the toolchain changes.
2026-07-08 12:10:45 -04:00
Dan Stillman
d07dd66c21 Parallelize update MAR generation
Run the per-file mbsdiff/xz work in make_incremental_update.sh and
make_full_update.sh through a parallel job pool, largest files first, and
assemble the manifests serially afterward, producing byte-identical MARs.

Set UPDATE_PACKAGING_JOBS to override the number of parallel jobs.
2026-07-08 12:10:45 -04:00
Dan Stillman
3ae3bd41dc itemPaneTest: Scroll to attachments pane instead of relying on pin
The pinned pane doesn't reliably trigger a render of an out-of-view pane in
this state, so scroll to the pane the way the surrounding tests do.
2026-07-08 10:50:32 -04:00
Dan Stillman
4314494455 Include callback source in waitForCallback timeout errors
The stack for these errors is stripped as internal frames, so the error
didn't indicate which wait timed out.
2026-07-08 10:50:32 -04:00
Dan Stillman
f9e711a9b2 itemPaneTest: Wait for window resize in attachments pane beforeEach
Tests that shrink the window to test visibility-based rendering restore the
height on their last line without waiting for it to apply, so the next test
could start rendering while the window was still 100px tall. The attachments
box's async render is skipped for panes outside the viewport and only retried
on scroll, so the pane never rendered and assertions on it failed or timed
out.
2026-07-08 10:50:32 -04:00
Dan Stillman
77a3cd82a9 itemPaneTest: Wait for attachments box render of the correct item
waitForPreviewBoxRender() without an item id returns immediately if the box
still holds the previous item's completed render, so on a slow machine the
row assertions could run before the new item's async render started.
2026-07-08 10:50:32 -04:00
Dan Stillman
49e284bab0 recognizeDocumentTest, translatorsTest: Initialize translators in before()
EPUB recognition translates the EPUB's RDF metadata, and
Zotero.Translators.save() requires translators to be loaded. The full test
run relied on an earlier test file having initialized translators.
2026-07-08 10:50:32 -04:00
Dan Stillman
7d33215a37 recognizeDocumentTest: Restore translate stub in afterEach after failure
Tests restore the stub at the end of the test body, so a failure left
Zotero.Translate.Search.prototype.translate wrapped and retries failed with
"Attempted to wrap translate which is already wrapped".
2026-07-08 10:50:32 -04:00
Dan Stillman
dfd745c5d5 Log test failures that will be retried
The reporter only showed the final failure, so if a retry failed differently
(e.g., from a stub left wrapped by the first failure), the original error was
lost.
2026-07-08 10:50:32 -04:00
Dan Stillman
1fe9cad5f8 citationDialogTest: Initialize styles in before()
Zotero.Cite.getLocatorString() requires Zotero.Styles.init() to have run.
The full test run relied on an earlier test file (bibliographyTest) having
initialized styles.
2026-07-08 10:50:32 -04:00
Dan Stillman
62a431f42b CI: Don't run duplicate jobs for same-repo pull requests
Pushes to branches in this repo already run CI, so run pull_request jobs only
for PRs from forks, which don't trigger push runs here. The push run's checks
still appear on the PR, since checks are attached to the head commit. (This
skips merge-commit testing for same-repo PRs -- the push run tests the branch
as-is.)
2026-07-08 10:50:32 -04:00
Dan Stillman
4c392c4764 CI: Run tests in 4 parallel shards
Each shard job builds and runs a quarter of the test files via runtests.sh -p.
The deployment ZIP upload and utilities tests move to separate jobs. With the
default fail-fast matrix behavior, a failure in one shard cancels the others.
2026-07-08 10:50:32 -04:00
Dan Stillman
bd25413213 runtests.sh: Add -p option to run a shard of the test files (e.g., -p 2/4)
The sorted file list is split into contiguous chunks of roughly equal total
file size, using size as a stand-in for run time, so new test files are
included automatically and slow test files are spread across shards rather
than landing in the same shard by chance. Contiguous chunks preserve the
alphabetical run order of a full run, so files keep the same preceding files
as in a full run except at chunk starts, and a shard can be reproduced locally
by passing its first and last files to -s and -e.
2026-07-08 10:50:32 -04:00
Dan Stillman
094d5cc2b6
Update staged builds in place for faster dev builds (#5994)
After a full build, dir_build saves a manifest of build/ files to
staging/.build-manifest. On subsequent runs, if all changed files are
ones that build.sh copies into omni.ja unmodified (chrome/, components/,
resource/, and test/ when tests are staged), zip just those files into
the staged omni.ja instead of rebuilding, taking rebuilds from ~15
seconds to ~0.3 seconds on an M1 Mac. Files are prescreened by size and
mtime so that only changed files need to be hashed. Zotero .ftl files
are also updated at their localization/<locale>/ paths, and test files
are also copied to the staged tests/ directory.

Any other change triggers an automatic full rebuild: files transformed
by build.sh (defaults/, chrome.manifest, version, translators/, styles/,
mozilla .ftl files, CSL locales), removed files, changes to build inputs
in app/ (detected via a size/mtime fingerprint, with xulrunner runtimes
covered by the hash-* files written by fetch_xulrunner), or requesting
tests or devtools that the staged build doesn't include.

Other changes:

- dir_build no longer takes -q and always skips omni.ja compression and
  optimization, which only matter for distribution builds made via
  build.sh. Use -f (dir_build or build_and_run) to force a full rebuild.
- build_and_run now always rebuilds. -r is deprecated, and -n skips the
  rebuild and just launches the app.
- build_and_run no longer passes -purgecaches. Startup caches are
  invalidated automatically when the BuildID changes, which now happens
  whenever omni.ja is modified (including via add_omni_file), so
  relaunching an unchanged build can use the startup cache.
- build_and_run and runtests.sh invoke js-build directly instead of via
  'npm run', which saves ~270ms of npm overhead per build.
- The Word integration dylib is now ad-hoc-signed by dir_build, and only
  on full rebuilds, since incremental updates don't invalidate the
  existing signature. This also covers test builds, which were never
  signed before.
- dir_build removes broken symlinks left in build/ when source files are
  deleted, which previously broke rsync in prepare_build.
2026-07-08 10:07:58 -04:00
Martynas Bagdonas
225e91c5e7 Tabs: Fix audio status validation
Validate the status argument instead of the unrelated title identifier, so invalid audio status updates fail with the intended error.
2026-07-08 14:52:31 +03:00
Martynas Bagdonas
2b7f46227f Reader: Reliably deactivate the docShells of hidden reader tabs
The reader now releases its rendered pages while its document is
hidden, which requires document.visibilityState to be truthful: it's
driven by docShellIsActive, which also controls event-loop throttling
for background tabs. The flag was maintained by scattered one-off
writes and went stale on the transitions they didn't cover: a tab
opened in the background ran at foreground priority (unthrottled rAF
and timers) with a 'visible' document until the next tab switch, and a
hidden tab kept an active docShell forever after its Read Aloud
playback was paused or stopped — in both cases keeping the reader from
ever releasing memory.

Replace the writes with a single _updateDocShellActivity() that
re-derives the state — active only while the tab is selected or
playing Read Aloud — called from the three places its inputs change:
tab creation, tab selection, and Read Aloud status changes.
2026-07-08 14:51:07 +03:00
Dan Stillman
55eceab3b3 Update English strings for Transifex
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
2026-07-05 11:23:51 -04:00
Dan Stillman
e2db3c7966 Citation dialog: Handle citation processor errors gracefully
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
If the preview fails, log the error and show "Preview unavailable"
instead of breaking the item-add flow with an unhandled rejection.

If io.sort() fails during accept, log and continue unsorted -- the
insert runs the same processor operation, so a real failure triggers the
document-update error dialog instead of a stuck progress window.
Skipping the sort doesn't affect the inserted citation, which the
processor sorts itself. It only determines the stored item order, and
with it the initial bubble order if the citation is edited later -- the
reopened dialog re-sorts once cited data has loaded.
2026-07-03 16:01:50 -04:00
Dan Stillman
cddf0ea237 Update citeproc.js to include Juris-M/citeproc-js#281
Fixes HTML appearing in citations after an error during citation preview

https://forums.zotero.org/discussion/132539/
2026-07-03 15:53:12 -04:00
Dan Stillman
057966000c Advanced search: Don't move focus to the results when running the search
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Behavior regression in #5658

https://forums.zotero.org/discussion/132526/10-0-beta-8-number-of-results-in-the-new-advanced-search
2026-07-02 12:59:25 -04:00
Dan Stillman
e416ea2dfa Fix repeated save prompt after trashing the saved search being edited
Deleting a search in the collections pane moves it to the trash rather
than erasing it, so the check that closes the saved-search editor when
the edited search is deleted didn't catch it. The save-changes prompt
then appeared on the next selection change, and canceling couldn't
restore the removed row, so the prompt reappeared on every selection
until choosing Don't Save. Treat a trashed search like a deleted one
and close the editor without prompting.
2026-07-02 12:44:07 -04:00
Dan Stillman
2f364f2fab Allow creating a saved search with collections or searches selected
Previously a search could be saved only at a library root. Now the Save
Search button is enabled when collections and/or saved searches within
a single editable library are selected, and the selection is added to
the saved search as collection/savedSearch conditions -- an 'any' group
of them when more than one row is selected. If the search's own join
mode is 'any', its existing conditions move into an 'any' group of
their own so the scope conditions apply to every result instead of
joining the OR. A 'recursive' condition is included per the
recursiveCollections pref.

https://forums.zotero.org/discussion/132528/beta-cannot-create-a-saved-search-from-a-collection
2026-07-02 12:38:52 -04:00
Dan Stillman
a48336350a Remove .swp file committed by accident 2026-07-02 10:54:04 -04:00
Martynas Bagdonas
af704dc119 Update document-worker submodule and report SDT progress 2026-07-02 15:04:31 +03:00
Dan Stillman
26c30d8ecd Allow browsing an attachment's annotations in search results
With "Hide Non-Matching Annotations" enabled, an attachment displayed as
empty and non-expandable if no annotations matched the search, so
searching by any non-annotation condition made it impossible to expand
attachments to browse their annotations. Now only hide the non-matching
annotations when the attachment actually has a matching one.

https://forums.zotero.org/discussion/132519/beta-advanced-search-cannot-expand-annotations-of-search-results
2026-07-01 22:13:38 -04:00
Tom Najdek
95ee1c616c
Fix xulrunner_hash to read the hash from openssl output on LibreSSL/macOS
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Previous command only worked with OpenSSL's labeled output where the hash is the second field; the update also handles LibreSSL (macOS), which prints just the bare hash.
2026-07-01 20:14:30 +02:00
Dan Stillman
40b272c46b Add -b (build-only) flag to beta_build_and_deploy 2026-06-30 17:44:49 -04:00
Dan Stillman
1d8cdb13c1 Update Windows build to Firefox 140.12.0esr 2026-06-30 16:41:38 -04:00
Dan Stillman
163c27ca79 Keep Advanced Search button visible when the item pane is wide
When the item pane is dragged wide, the items pane is squeezed and the
quick search wrapper kept its intrinsic width and overflowed, pushing the
trailing Advanced Search button out under the item pane. Let the wrapper
shrink so the button stays within the pane.

Fixes #5982
2026-06-30 15:57:06 -04:00
Dan Stillman
d28bdd8d6e Update Mac and Linux builds to Firefox 140.12.0esr
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-06-30 15:09:55 -04:00
Abe Jellinek
279e25c57e
Preferences: Scroll first search result into view (#5976) 2026-06-30 14:21:30 -04:00
Dan Stillman
13bd8c9d8d
Group attachment and annotation conditions into Advanced Search submenus (#5981) 2026-06-30 14:20:47 -04:00
Tom Najdek
7f30b0e47d
Respect skipDateModifiedUpdate of the operation when undoing it, fix #5971 2026-06-30 18:23:23 +02:00
Abe Jellinek
f3328b02ee
Item/collection tree: Resize using ResizeObserver, not debounce (#5977) 2026-06-30 08:44:18 -04:00
Adomas Venčkauskas
9cffc1c7c2 Update LibreOffice submodule 2026-06-30 14:35:38 +03:00
Dan Stillman
36d025f6a9 Update item count on every items-view refresh
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
The 'items in this view' count failed to update when the result set
changed without a selection change (e.g., quick search, tag filter, or
sort with nothing selected). The listener that refreshes the item pane
on refresh was registered in 8277277948 to fire only once, on initial
load, so later refreshes never recomputed the count.

This is all a regression from the items tree refactor (cbbff600a6),
which stopped running itemSelected() on every refresh.
2026-06-29 15:12:22 -04:00
Tom Najdek
2d56936bfe
Fix surplus space left behind after editing Extra in batch mode, fix #5969 2026-06-29 18:42:25 +02:00
Tom Najdek
2006cdac6d
Improve the behavior of read-only fields in batch edit mode, fix #5968
- Fix a bug where focusing on a read-only field with a common value across items would clear the field and display a "Multiple" placeholder instead
- Fix read-only date fields always showing "Multiple" in batch edit mode, even when values are genuinely different
- Read-only fields are now focusable in batch edit mode
2026-06-29 17:17:33 +02:00
Dan Stillman
02b19d0cde Match multi-level search condition against any ancestor level for descendant results
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
A field that exists at more than one level (e.g., Title, on both
top-level items and attachments) targeting a descendant result level
only matched the closest ancestor, so searching annotations by Title
found nothing, since it checked the parent attachment's title rather
than the top-level item's. Map down from each ancestor level and union
them, testing the predicate once so its bound parameters aren't
duplicated.

Addresses #5978
2026-06-26 16:53:52 -04:00
Dan Stillman
340ba92513 Show "Search subcollections" only with a Collection condition
The option only affects Collection conditions, so hide it -- and don't
emit the 'recursive' condition -- when the search has none.
2026-06-26 16:29:34 -04:00
Dan Stillman
6c7c661fb1 Advanced search: Separate Saved Search from the Collection condition
Saved searches were folded into the "Collection" condition's value menu
in 2016 (9c52ebdf8b), for reasons I can't totally remember. Give Saved
Search its own condition again.
2026-06-26 16:24:49 -04:00
Dan Stillman
a1f36f750e Advanced search: Fix extra padding on nonexistent icon in hint button
Addresses #5978
2026-06-26 15:55:32 -04:00
Dan Stillman
fe340283c1 Crop Advanced Search label so buttons stay visible when window is narrow
Addresses #5978
2026-06-26 15:55:32 -04:00
Dan Stillman
0e587176e8 Advanced search: Match condition value field height to drop-down menus
Addresses #5978
2026-06-26 15:55:32 -04:00
Dan Stillman
d4507544af Advanced search: Reduce the condition row's control gap to 6px
Addresses #5978
2026-06-26 15:55:32 -04:00
Dan Stillman
98308c65b9 Advanced search: Drop the extra padding on the 'Find' line
Add a bit more inline padding to the whole pane to compensate, so the Find
line and conditions box aren't too close to the left edge.

Addresses #5978
2026-06-26 15:54:49 -04:00
Dan Stillman
f7e67f6643 Advanced search: Match the level-warning text size to the binding hint
Addresses #5978
2026-06-26 15:53:38 -04:00
Dan Stillman
cf7ee984ea Advanced search: Hide obsolete Child Note condition and migrate to Note
Addresses #5978
2026-06-26 15:53:07 -04:00
Dan Stillman
3875f411e5 Advanced search: Show binding hints and warnings on condition select
Not just on value entry, but add an exception for a new row created with
the + button

Addresses #5978
2026-06-26 15:52:34 -04:00
Dan Stillman
680452cc59 Advanced search: Update condition grouping icon and add ungroup button
Addresses #5978
2026-06-26 15:52:16 -04:00
Dan Stillman
3544848e5f Keep an edited search condition value when switching condition types
Addresses #5978
2026-06-26 15:50:58 -04:00
Dan Stillman
b56c153125 Fix Attachment Last Read search condition by matching at attachment level
Addresses #5978
2026-06-26 15:50:17 -04:00
Dan Stillman
2717aafedd Add annotation type, color, and author search conditions
Add annotationType, annotationColor, and annotationAuthor conditions,
each tagged `level: 'annotation'` so the cross-level search logic maps
and negates them correctly. The value fields are drop-down menus: the
annotation types, the reader's color palette, and the library's
existing annotation authors.

Ported from #5839. The PR kept a negated annotation condition (e.g.,
"Annotation Color" "is not" "yellow") from matching every non-annotation
item by checking whether the condition name contained "annotation". This
does the same using the condition's level, which the cross-level logic
already handles, so the existing annotationText and annotationComment
conditions are covered too.

Fixes #5837
2026-06-26 13:08:15 -04:00
Dan Stillman
cb2110e1c1 Move the annotation conditions into the main search menu
Addresses #5978
2026-06-26 12:48:48 -04:00
Dan Stillman
2239dd20a4 Update global schema version for search-condition-group gate (#5962)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-06-25 16:14:38 -04:00
Dan Stillman
60807d552c Remove the search condition required flag (#5962)
The flag forced a condition to be ANDed even in "any" mode, but
condition groups now express that directly. It was never exposed in the
search UI and nothing seems to have been using it.

addCondition()/updateCondition() now throw if passed a truthy
`required`. Not dropping the column now to preserve DB compatibility.
2026-06-25 16:14:38 -04:00
Dan Stillman
ccf6f18643 Prefill Advanced Search from the quick search (#5962)
Opening Advanced Search from a non-empty quick search reproduces the
current quick search mode as editable conditions, one per word (or quoted
phrase) joined with "all":

- Title/Creator/Year: a single Title, Creator, Year condition per word,
  with the result level set to item (the mode matches only top-level items)
- All Fields & Tags: a single Any Field condition per word
- Everything: Any Field + Full Text Content as an "any" group per word
  (relies on grouped full-text composing correctly in SQL)

The Title/Creator/Year mode needs a condition to map to, so add a "Title,
Creator, Year" search condition that expands to the same field set as the
quick search mode (title, publication title, short title, court, year,
citation key, creator), mirroring how Any Field matches All Fields & Tags.
Like Any Field, it expands at query-build time, so the saved search stores
a single condition and its sub-fields don't need their own entries in the
condition menu.
2026-06-25 16:14:38 -04:00
Dan Stillman
786b85bf2d Expand Any Field in place so it nests correctly (#5962)
An Any Field condition expands into field/tag/note/creator conditions for
the term. Splice the expansion in right after the condition so it stays at
the same nesting depth, rather than appending it to the end of the
processing queue, which would emit it at the top level instead of within
its group. (Top-level Any Field is unaffected.)
2026-06-25 16:14:38 -04:00
Dan Stillman
0529c9a579 Match Any Field search condition to the All Fields & Tags quick search mode (#5962)
Add annotation text and comments to the conditions the Any Field
condition expands to, matching the fields covered by the All Fields &
Tags quick search mode, as the comment already says is intended. Key
detection and quoted-phrase splitting still differ, since those depend
on the quick search string parsing that Any Field doesn't do.
2026-06-25 16:14:38 -04:00
Dan Stillman
092295da22 Add result-level controls to the Advanced Search builder (#5962)
- Reword the header as one sentence with a result-level menu ("Find
  [attachments] matching [all] of the following:")
- Provide a per-group menu to bind the group's descendant conditions to
  the same attachment, note, or annotation (e.g., one annotation that is
  both red and contains a given word, not two different ones)
- Show a hint that offers to group ungrouped sibling conditions (e.g.,
  two annotation conditions at the top level, to bind them to one
  annotation)
- Show a warning when conditions can't combine at the chosen result
  level (e.g., an annotation condition with a note result level)
- Remove the two legacy checkboxes:
  - "Show top-level items" becomes result level = top-level item and is
    migrated on save
  - "Include parent and child items", which has no result-level
    equivalent, keeps working, stays editable, and round-trips on
    searches that already have it, but it isn't offered on new searches
    and is removed on save if unchecked
2026-06-25 16:14:38 -04:00
Dan Stillman
8b5a77a75e Support cross-level conditions and a result level in search (#5962)
Give a search a result level -- top-level item, attachment, note, or
annotation -- and map every condition to that level, so one search can
mix conditions that match at different levels of the item hierarchy
(e.g., a top-level item with a given author and a red annotation on one
of its PDFs). Each condition carries the level(s) it matches at: a match
is mapped up to an ancestor or down to a descendant, level-agnostic
conditions (tags) roll up to the result level, and fields that exist on
both items and attachments (title, url, accessDate) match natively at
either. The result level is stored as a `resultLevel` marker condition
alongside the join mode.

This also removes the temporary annotation-parent hacks, which the
general cross-level mapping replaces.
2026-06-25 16:14:38 -04:00
Dan Stillman
1341b6d5cc Style Advanced Search conditions as a divided, indented list (#5962) 2026-06-25 16:14:38 -04:00
Dan Stillman
cdc70d1280 Compose grouped full-text conditions in SQL (#5962)
A fulltextContent condition was evaluated as a global post-filter keyed
on the search's top-level join mode -- correct for a top-level
condition, but not for one inside a group, which must combine with its
siblings under the group's own join mode. The new condition grouping UI
allows fulltextContent to be placed within groups, and we need to do so
to prefill the advanced-search pane from an "Everything" quicksearch.

Materialize a grouped fulltextContent into an itemID set and emit it as an
ordinary itemID IN/NOT IN predicate, so combineConditions composes it
under the group's join mode. Top-level fulltextContent keeps the existing
post-filter unchanged.

This also removes the quicksearch full-text post-filter special case. A
quick search puts its full-text in per-word "any" groups, so the
_hasQuicksearch flag was needed to make the post-filter union those
matches rather than intersect them under the top-level "all" join. Now
that the grouped full-text is composed in SQL it never reaches the
post-filter, so the flag is gone.
2026-06-25 16:14:38 -04:00
Dan Stillman
5904f0e4bf Diff saved-search conditions as an ordered list (#5962)
With condition groups, the position of conditions and the pairing of
groupStart/groupEnd markers are meaningful, but conditions were diffed
as an unordered member set (compared by value, with additions appended).
A sync-conflict merge could reorder conditions or add/drop group
markers, corrupting the group structure.

Diff them as a single ordered unit instead, like creators. This changes
how concurrent edits to a search are reconciled: rather than merging the
two sides' conditions member by member, a conflicting edit now resolves
wholesale -- the remote condition list replaces the local one (searches
auto-merge to the remote version); a one-sided change still applies that
side's full list. Discarding one side of a rare simultaneous edit is
acceptable and avoids silently corrupting a grouped search's structure.

This was the only user of SearchConditions.equals(), so remove it.
2026-06-25 16:14:38 -04:00
Dan Stillman
d808988540 Add condition grouping UI to Advanced Search (#5962)
Render the search as a tree of groups: a root group plus nested
search-condition-group elements, each with its own join-mode menu and a
remove control. Each condition row gets a "( )" button that wraps it in
a new group in place, so further conditions can be added to combine with
it under a separate join mode. Switch the builder to rebuild-from-tree --
the DOM is the source of truth and the search's flat conditions (with
groupStart/joinMode/groupEnd markers) are regenerated on each edit, so
the old conditionID-as-index tracking is gone.
2026-06-25 16:14:38 -04:00
Dan Stillman
c01def2a85 Support nested condition groups in saved searches (#5962)
Replace the flat anySQL/quicksearch-block assembly in _buildQuery with a
recursive tree of AND/OR groups, built and reduced by a new
Zotero.Search.combineConditions() helper. groupStart/groupEnd markers
delimit nested groups and a joinMode marker sets each group's mode, so a
saved search can combine conditions with arbitrary nesting and per-group
join modes. The per-condition SQL generation is unchanged.

For example, a search built as

    search.addCondition('joinMode', 'all');
    search.addCondition('title', 'contains', 'foo');
    search.addCondition('groupStart', 'true', '');
    search.addCondition('joinMode', 'any');
    search.addCondition('tag', 'is', 'x');
    search.addCondition('tag', 'is', 'y');
    search.addCondition('groupEnd', 'true', '');

means "title contains 'foo' AND (tag is 'x' OR tag is 'y')". The 'true'
operator on the group markers is an unused placeholder -- they carry no
value, but a condition's operator can't be empty.

The quick search (matching multiple words) and the Any Field condition
previously had their own special handling in the query builder; they now
use the same grouping as everything else, so that code is gone. Behavior
for existing non-grouped searches is unchanged; new tests cover nested
groups and combineConditions() directly.
2026-06-25 16:14:38 -04:00
Abe Jellinek
9a4317daa7 Preferences: Trim search term
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Fixes #5975
2026-06-25 09:35:47 -04:00
Dan Stillman
8f96a6d191 Enable Reset File Sync History for read-only libraries
https://forums.zotero.org/discussion/132375/switching-file-sync-settings-for-group
2026-06-25 09:34:47 -04:00
Dan Stillman
aab912e4ee Support Select All (Cmd/Ctrl-A) in the collection tree
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
A plain select-all would mix uncombinable rows, so scope it to the
current selection's type: a library selection expands to all library
roots; a collection selection to every collection sharing a parent with
a selected one (so multi-level/multi-parent selections expand within
each branches); and a Recently Read selection to Recently Read in every
library. Other rows -- saved searches, Unfiled, Trash, etc. -- have no
useful expansion, so the tree's key handler leaves the selection
untouched instead of clearing it.
2026-06-24 14:23:08 -04:00
Dan Stillman
d7660f2f27
Add local-cache versioning for Read Aloud audio (#5967)
Version Read Aloud cache keys with the server-provided cacheVersion so
a version bump misses stale entries and re-fetches correct audio, prune
obsolete entries once per session, and skip caching responses sent with
Cache-Control: no-store.
2026-06-24 11:21:09 -04:00
Dan Stillman
962e1334c8 Inset item tree focus ring so its top isn't clipped
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Regressed by f4adb452ee, which dropped the items-tree body's top padding
to 0 for sticky section headers; the scroll container's overflow then
clipped the top of the first row's focus ring, which drew outside the
row box.
2026-06-23 09:35:25 -04:00
Tom Najdek
40cb1cfa84
Fix stale itemType in _previousData (#5964)
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
This fixes a problem where, on subsequent item type changes, a stale itemType is reported and the filename is not updated.
2026-06-19 16:34:23 -04:00
Dan Stillman
35317982f5 Make the sticky library header opaque to pointer events
The pinned sticky section header was opaque to paint but had
pointer-events: none, so clicks, drag-starts, and drops fell through to
the item occluded underneath it.

Make the opaque content capture pointer events instead. Clicks no-op
(header rows aren't selectable), drags don't start (header rows aren't
draggable), and drops on the header are rejected so they're a no-op
rather than acting on the list underneath.

Also drop the index > 0 exception when skipping non-selectable rows in
_onSelection(), so clicking a non-selectable row at the top (i.e., the
first library's header) is a true no-op instead of scrolling to the top.

Also skip non-selectable rows in handleActivate(), since the header can
now be double-clicked; without this it would try to open the library as
an item.

Fixes #5960
2026-06-19 16:07:53 -04:00
Dan Stillman
bdf79336f9 Account for sticky section header when scrolling a row into view
Arrow-key navigation scrolled the newly selected row flush with the top
of the view, leaving it hidden behind the pinned sticky section header.
Reserve a row's worth of space at the top so the row lands below the
pinned header.

Fixes #5959
2026-06-19 14:28:40 -04:00
Tom Najdek
e71bd89e0d Add support for undo/redo (#5823)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-06-19 14:12:54 -04:00
Tom Najdek
c92148c3ec Add batch editing support for fields (#5823)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-06-19 14:12:54 -04:00
Dan Stillman
8d7f5a39f9 Don't include trashed items in Advanced Search outside the trash
#5658 added includeDeleted to the Advanced Search outside the trash, so
trashed items kept matching: trashing a result removed its row, but
re-running the search brought it back. Exclude deleted items by default,
the same as a quick search, and only include them when viewing the trash,
where the scope returns only deleted items.

Fixes #5956
2026-06-19 14:07:43 -04:00
Dan Stillman
ff094330f5 Don't show all trashed collections/searches during a search in trash
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
When viewing the trash, trashed collections and saved searches were appended
to the items list unconditionally, so every advanced search (and quick search)
in the trash matched all of them. Now they're filtered by name during a quick
search and excluded entirely when an advanced search or tag filter is active,
since they can't match item-level conditions or tags.

Fixes #5957
2026-06-19 13:43:53 -04:00
Dan Stillman
4a318617d6 Don't crash on unbalanced {{endif}} in template engine
An {{endif}} without a matching {{if}} popped the base level off the
stack, leaving it empty and throwing "level is undefined" on the next
part. A malformed filename format template could therefore break file
attaching entirely.

https://forums.zotero.org/discussion/132296/pdfs-have-stopped-attaching-after-upgrade-to-zotero-9
2026-06-19 13:05:19 -04:00
Tom Najdek
6092b0335c
Fix auto-renaming not triggering when parent item type changes 2026-06-19 15:04:56 +02:00
Dan Stillman
35c8448582 Advanced search: Move focus to next "-" button when removing row via keyboard 2026-06-19 00:14:06 -04:00
Dan Stillman
e37a7d8f06 Advanced search: Don't run the search on Shift-Enter 2026-06-19 00:14:06 -04:00
Dan Stillman
2e79bbe402 Advanced search: Focus condition drop-down when adding row via keyboard 2026-06-19 00:14:05 -04:00
Dan Stillman
e38f8f78b0 Advanced search: Add find-as-you-type for all conditions
Closes #5953
2026-06-19 00:14:05 -04:00
Dan Stillman
90aa5d33a0 Fix first-column (Title) header label alignment after item tree refactor
The refactor dropped the 8px (COLUMN_PADDING / 2) offset that makes up
for the inline-start padding the first cell omits, so the header label
sat 8px too far left of the item titles.
2026-06-18 23:32:47 -04:00
Dan Stillman
f4adb452ee Keep the sticky items-list section header clear of the scrollbar
Pin the header inside the scrolling body as a zero-height position:
sticky element so its width tracks the body's content box and it lines
up with the rows without any JS geometry. Reserve a scrollbar gutter so
the macOS overlay scrollbar doesn't float over the content (the opaque
header must paint above the rows to occlude them, and so above the
scrollbar, so it can't be put under it). Drop the body's top padding for
the item tree so rows clip exactly where the header pins, and keep the
gap below the column header as a margin outside the scroll area.

Fixes #5958
2026-06-18 23:25:22 -04:00
Dan Stillman
08d875c7f7 Handle multiple-library item selections dropped on a collection or library
Route each item by its own library -- items already in the target
library are added directly (or skipped, for a library root), while
items from other libraries are copied in -- instead of attempting an
invalid cross-library insert. Disallow a move of such a selection
rather than silently copying.

Fixes #5961
2026-06-18 16:13:16 -04:00
Dan Stillman
6d1cd85211 Skip retraction banner during initial sync on a new computer 2026-06-18 14:51:44 -04:00
Dan Stillman
39954dc437 Remove dead collection assignment for window-opened notes (#5954)
The collections passed through Notes.open into the note window were never
read -- the note editor has no collections setter and EditorInstance.collection
is never assigned on this path -- so they had no effect. New notes are still
added to the selected collection(s) directly in newNote().
2026-06-18 14:10:25 -04:00
Dan Stillman
20fd5fe3e2 Move tag selector view settings popup handler out of XHTML (#5954) 2026-06-18 14:09:49 -04:00
Dan Stillman
618fcfa49d Use one combined tag query for a multiple-row selection (#5954) 2026-06-18 14:09:44 -04:00
Dan Stillman
1c76195a7c Show library-aware section headers in the item tree for multi-row selections (#5954)
When multiple collection-list rows are selected, group the combined
items by library under sticky headers (e.g., "My Library", "Group X (2
collections selected)"), or show a single summary header for a multi-row
selection within one library, with blank spacer rows separating
libraries.
2026-06-18 14:09:36 -04:00
Dan Stillman
94c78759ab Add support for sticky section headers to VirtualizedTable (#5954)
When stickySectionHeaders is enabled, the header of the section at the
top of the view is pinned in an overlay that the rows scroll under,
pushed up as the next section's header arrives. Consumers identify
header rows via isSectionHeader. Row striping restarts at each section
header so every section's first row is the same shade.
stickySectionHeaders defaults to false, so existing tables are
unaffected.
2026-06-18 14:09:31 -04:00
Dan Stillman
b80fa996f5 Group the items list by library (#5954)
When the items list contains items from more than one library, group
them by library -- in collections-list order, independent of the active
sort -- with a section heading above each library's items.

Grouping is triggered automatically by an items list spanning more than
one library, not the kind of selection behind it, so any future source
of multi-library items would be separated the same way. Today the
cross-library collection selection is the only such source.
2026-06-18 14:09:17 -04:00
Dan Stillman
15c2c95470 Support multiple-collection selection (#5954)
Allow selecting multiple collections, saved searches, or library roots in the
collection tree -- within a library or across libraries -- and show the union
of their items. The selection is threaded through the pane as an array
(getCollectionTreeRows(), changeCollectionTreeRows(), etc.); the item pane, tag
selector, reports, and export operate on all selected rows.

Adding items (new items and notes, drag-and-drop, the attachment dialog,
import, Add by Identifier) targets every selected collection. Only rows that can
share an items view may be combined: collections, saved searches, and library
roots mix freely, and multiple Recently Read rows can be combined across
libraries, but other special views (Trash, Duplicates, etc.) and rows from
different visibility groups can't be shown together, so a selection mixing them
keeps only the focused row. In-window advanced search runs across all selected
collections.

Advanced search value autocomplete is now scoped to the searched library, fixing
a long-standing TODO where suggestions were drawn from all libraries regardless
of the search scope; for a cross-library selection it spans the selected
libraries.

For a cross-library selection, the tag selector shows the union of tags
(colored tags only when a single library is in scope, since colors are
per-library), and deleting a tag spans all selected libraries while
rename/color/split are disabled. If advanced search is open, the
collection and saved-search conditions are omitted, since each is scoped
to a single library.
2026-06-18 14:05:55 -04:00
Dan Stillman
2ddb4e991b Show sort indicator on default-sorted column and fix first-click reverse
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
On a fresh profile, the default sort column had no explicit sortDirection,
so the first header click set the default direction instead of reversing
the sort, and no sort indicator was shown.
2026-06-17 15:33:37 -04:00
Abe Jellinek
e23a453e5c Recently Read: Show matching children as regular rows
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
In other words, don't show the children that make the parent "recently
read" as context rows.

Children that don't match the Recently Read condition (such as notes,
as well as other child attachments that weren't read since the cutoff)
are still shown as context rows.

And add tests for the new behavior, and clean up an unused local var.
2026-06-16 13:52:14 -04:00
Dan Stillman
4d38970ef0 Center the Advanced Search filter button's focus ring on the field edge
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-06-15 20:38:37 -04:00
Bogdan Abaev
e6f82dabfd fix keyboard nav through advanced search btns+deck (#5955)
- Include the Advanced Search button in the quick-search tab sequence
- Tab: quick-search textbox -> Advanced Search button -> item tree
- Shift-Tab: Advanced Search button -> quick-search textbox
- Shift-Tab: item tree -> Advanced Search button (when the deck is
  closed)
- Shift-Tab: Advanced Search collapse button -> New Item button
- Fix Shift-Tab from the item tree doing nothing when the Advanced
  Search deck is open; now moves focus to the deck's last focusable
  element
- add tooltiptext to expand/collapse and close btns
2026-06-15 20:38:27 -04:00
Dan Stillman
cf876033f6 Fix join-mode menulist clipping at top of Advanced Search 2026-06-15 15:22:16 -04:00
pi-dal
f03004bb9e
fix(attachments): add PMCID fallback for PubMed resolver lookup (#5906)
PubMed saves can include a PMCID even when DOI-based full-text lookup
fails to produce a PDF. In that case the connector asks the desktop
attachment resolver flow for fallback sources, but getFileResolvers()
only considered DOI and URL data, so Zotero never tried the matching
PMC article URL.

Treat PMCID as a valid signal for file lookup eligibility and add a
PubMed Central OA resolver before DOI-based OA lookup. Cover the new
fallback path in attachment resolver tests and the connector
hasAttachmentResolvers endpoint.
2026-06-15 14:57:49 -04:00
Abe Jellinek
80ca898a74 Preferences: Fix search not filtering and not highlighting results
1. 862573eab added references to fileRenaming.ftl strings but didn't
   add a <link> to it, so document.l10n.formatMessages() returned
   nothing. It fell back to Zotero.getString(), which threw an error
   due to a missing string in en-US. We didn't catch that, so the entire
   search process aborted every time.
2. message.attributes is an optional array of { name, value } objects,
   not an object itself, so we were never successfully adding FTL .title
   and .label attributes to the parsed strings.

This fixes the 862573eab regression, and makes search match FTL
attributes as it was always intended to.

Fixes #5947
2026-06-15 13:55:06 -04:00
Dan Stillman
7eef2fb3f0 Don't let a failed database backup block a schema upgrade or startup
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
During a schema upgrade, a failed backup aborted the upgrade and blocked
startup on every launch. Automatic backups are best-effort, so just log
the error and skip the backup instead.

https://forums.zotero.org/discussion/132201/errorunknownerror-could-not-stat-ns-error-generate-failure-44-4350
2026-06-15 11:53:01 -04:00
Dan Stillman
22055d92b7 Fix opening the DB on network filesystems (e.g., SMB) on macOS/Linux
(Confirmed on macOS, but probably Linux too)

Pass openNotExclusive to avoid acquiring an exclusive lock at the OS level
at open time, which fails with an I/O error on network shares. We still set
locking_mode=EXCLUSIVE, so the connection holds an exclusive SQLite lock for
its lifetime and keeps the WAL index in heap memory (no -shm file).

Regression in Zotero 7 (Fx115)

Fixes #4860
2026-06-15 11:16:54 -04:00
Dan Stillman
39d7835472 Don't show post-upgrade banner for source and dev builds 2026-06-15 09:57:45 -04:00
Dan Stillman
e41c788c4c Don't show standalone attachments open in tabs in the citation dialog 2026-06-14 23:24:22 -04:00
Dan Stillman
580e423d4c Fix failing saved-search test
The new Save Search name prompt blocked deck.pane.save().
2026-06-14 16:18:05 -04:00
Dan Stillman
27560d9476 Prompt for a name when saving a new advanced search
The temporary advanced search saved silently under an auto-generated
"Untitled" name. Prompt for a name instead (defaulting to that name),
and label its button "Save Search…" to signal the dialog. The saved-search
editor's button, which saves changes directly, is now just "Save".

https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
2026-06-14 15:17:25 -04:00
Dan Stillman
81b3c13b13 Close the saved-search editor when the edited search is deleted
Deleting the saved search being edited triggered onCollectionSelected's
save-changes prompt, which doesn't make sense for a search that's gone.
Detect the deleted search and close the editor without prompting.
2026-06-14 15:02:40 -04:00
Dan Stillman
a3180d87c5 Show focus ring on the advanced-search condition menu when opened
The menu was focused programmatically (in response to the filter-button
click), so :focus-visible wasn't set and no focus ring showed. Pass
focusVisible to force the ring.
2026-06-14 14:53:11 -04:00
Dan Stillman
8ed617ae53 Allow removing the last advanced-search condition to reset it
The remove button on the sole remaining condition is now enabled once a
value is entered, and clicking it replaces the condition with the default
empty one shown when the pane is first opened, instead of staying
disabled.
2026-06-13 19:33:31 -04:00
Dan Stillman
97aa4f93ea Restore saving advanced searches in editable group libraries
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Regression in #5658
2026-06-13 19:21:35 -04:00
Dan Stillman
3d334dc244 Align Advanced Search close button with the filter button's right edge
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-06-13 11:03:58 -04:00
Dan Stillman
18d332ec5e Patch out native control margins in Advanced Search
macOS-normalize-controls zeros margins on inputs and checkboxes but not
menulists or buttons, so their native platform margins threw off the
spacing. Zero them and restore spacing via the containers' gaps.

https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
2026-06-13 09:47:37 -04:00
Dan Stillman
702094f703 Shift Advanced Search filter button to center its icon
https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
2026-06-13 09:47:25 -04:00
Dan Stillman
40851e1f58 Add bottom border to Advanced Search area
https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
2026-06-13 09:47:20 -04:00
Dan Stillman
2689997c50 Fix Advanced Search area height calculation
The inner <deck> stacks both panes in one grid cell, so the area was
always sized to the taller (saved-search) pane and never shrank back.
The non-selected pane is now removed from layout entirely.

https://github.com/zotero/zotero/pull/5658#issuecomment-4696398750
2026-06-13 09:47:11 -04:00
Abe Jellinek
00527332c4 Move Advanced Search and saved search editing to the main window (#5658)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-06-12 15:21:59 -04:00
Bogdan Abaev
d2f1c56250 keep search conditionIDs in arithmetic sequence
When conditions are removed, shift conditionIDs so that
conditionIDs always go in increments of 1 (0, 1, 2 ...).
It prevents conditionIDs from conflicting with each other
when conditions are rearranged.

Fixes: #3434
2026-06-12 15:16:46 -04:00
Dan Stillman
cd29a818b0 Don't let hover background override selected item background 2026-06-12 15:07:17 -04:00
Bogdan Abaev
0f2d3e103d Citation dialog: show placeholder after a new bubble is added (#5932)
After the first bubble is added, the focused input gets a placeholder
indicating that typing a number will add it as a page to the just-added
bubble. The placeholder is truncated if it's too close to the edge in
multi-item citations.

Also add a tip to the item details popup explaining that locators can
be typed into the main input field, with a link to the documentation.
The tip stops appearing once a typed locator has been used.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-06-12 15:07:17 -04:00
Bogdan Abaev
40f949ba94 Citation dialog: allow accepting while details popup is open
- allow to accept the dialog via cmd/ctrl+Enter from
inside of a panel
- enable click-through on item details panel, so
one can click on the "Accept" button without having
to close the popup first

These two measures allow one to add a bubble, modify
the citation via item details popup, and accept it
with one less step (without having to close the popup first)
2026-06-12 15:02:14 -04:00
Dan Stillman
c72d80f22b Update Word for Windows submodule 2026-06-12 12:58:00 -04:00
Dan Stillman
cd39445b94 Automatically repair unusable login manager
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Zotero never sets a primary password, so if one is set on the NSS key
database, it was either corrupted or copied in from a Firefox profile,
and stored logins can never be decrypted, since there's no
primary-password prompt. This previously made it impossible to save
credentials without manually deleting cert9.db, key4.db, and
logins.json from the profile directory.

If reading or saving credentials fails and a primary password is set,
clear stored logins and reset the key database so that credentials can
be saved again. Saving the API key or WebDAV password is retried
automatically, so logging in completes without a manual fix, and if
previously stored credentials are lost to a reset, show a one-time
alert prompting the user to log in again.
2026-06-12 11:17:00 -04:00
Dan Stillman
632f11db74 Fix corrupted-login-manager warning never appearing
The once-per-minute rate limit from e40fff6a7d (5.0.91) was inverted,
and the last-error time was never initialized, so the alert hasn't
appeared since 2020.
2026-06-12 11:17:00 -04:00
Dan Stillman
55434d14a1
Encrypt API key and WebDAV password using OS keychain (#5897)
Wraps the values stored in nsILoginManager with OSKeyStore, which derives
its master key from Keychain on macOS, DPAPI on Windows, and libsecret on
Linux. A copy of the profile alone is no longer enough to extract these
credentials.

Existing plaintext entries are mirrored once per session to a new
"(encrypted)" realm but preserved in the original realm so a user can
still downgrade to a release that doesn't know about encryption. Active
credential changes (sign in, sign out, password change) write to the
encrypted realm only and remove the legacy entry. A future version can
clear any remaining legacy entries on startup.

Patches MOZ_APP_BASENAME in the bundled runtime so the keychain master
key is labeled "Zotero Encrypted Storage" rather than "Firefox Encrypted
Storage", with a check_line guard so a future Mozilla change to the
OSKeyStore label format fails the build instead of silently rebranding
the entry. Also fixes check_line to take an explicit file argument.
2026-06-12 11:16:22 -04:00
Martynas Bagdonas
98d82d2909 Add SDT support 2026-06-12 13:47:08 +03:00
Abe Jellinek
982c00aaf6
Add by Identifier: Stay open with input, don't clear unless submitted (#5949) 2026-06-11 11:54:42 -04:00
Dan Stillman
06e16c297c Fix file-change detection in all libraries after the first one synced
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
The local-file-change watcher added in f21e1b2d32 accumulated changed
item keys globally but was drained separately by each library's storage
engine, with the drained keys filtered to that library. The first
library to file-sync (normally My Library) consumed all pending events,
and keys belonging to other libraries were silently discarded, so files
modified on disk in group libraries were never marked for upload. The
initial and periodic full-scan fallbacks on Windows and Linux were
likewise global, so only the first library ever received them, and
changes made in other libraries while Zotero was closed were never
detected at all.

The sync runner now drains the watcher once per sync session and
immediately runs the modification check on the changed items across all
libraries, recording any changes in the database, and the per-library
storage engines skip the check entirely unless the watcher reports that
the library needs a full scan:

- On all platforms, a library that has never been scanned gets one full
  scan, which also gives libraries one recovery scan for changes dropped
  by affected releases.
- On Windows/Linux, where the watchers only capture events while Zotero
  is running, each library gets a full scan on its first file sync of
  the session, on every manual sync, and daily during background syncs
  (instead of the previous 3-hour interval, which dated from when scans
  were the primary detection mechanism).
- On macOS, libraries scanned since the last FSEvents journal
  discontinuity are tracked in a pref, since the journal -- and
  therefore the validity of previous scans -- survives restarts.

Also:

- Check FSEvents event flags and fall back to full scans when events
  were dropped or coalesced (MustScanSubDirs/UserDropped/KernelDropped/
  EventIdsWrapped), and skip HistoryDone sentinel events
- Disable the watcher for the session and fall back to legacy scanning
  on backend errors, including when the inotify watch limit is reached,
  instead of continuing with silently incomplete coverage
- Prune scan records for deleted libraries, since SQLite can reuse a
  deleted library's libraryID

https://forums.zotero.org/discussion/132120/
2026-06-10 22:26:13 -04:00
Dan Stillman
de1bf1cec1 Update Mac Word plugin install flow for macOS 27 Golden Gate
On macOS 27 and later, the installer gets access to the Word startup
folder via a folder-selection dialog rather than an OS permission
prompt, so support an adjusted banner message and add strings for the
folder dialog.
2026-06-10 15:06:01 -04:00
Dan Stillman
4e3baf09a0 Add okButtonLabel support to FilePicker
Maps to nsIFilePicker.okButtonLabel, which customizes the label of the
button used to accept the dialog, where supported by the platform.
2026-06-10 15:06:01 -04:00
Dan Stillman
fde4086e08 Show file-access error when storage directory can't be cleared on download
createDirectoryForItem() wipes and recreates the item's storage
directory before moving in a downloaded file. If removeDir() failed
(e.g., a locked file on Windows), the error bubbled up to zfs.js and
became the generic sync error. Route it through checkFileAccessError()
instead, so the user gets the actionable locked-file message and a
Show Parent Directory button.

https://forums.zotero.org/discussion/132097/
2026-06-10 08:07:32 -04:00
Adomas Venčkauskas
3e7030a642 Update Word for Windows submodule
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-06-10 12:31:08 +03:00
abaevbog
fa3e0f683f
Citation dialog: display preview of the citation (#5916)
In Add/Edit Citation mode, display a preview of the citation
in the bottom section. The section can be hidden/displayed
via the toggle in the right corner.

Remove io.preview from editor instance, so that citation
dialog knows not to show the preview even if the preference
is set.

A minor refactor to have resizeWindow() resolve
when the animation is fully over, and clear minHeight
on window in list mode before resizing, restoring
it when resizing animation is done, same as in library mode.
It allows us to fully expand the window in list mode before
showing the preview.

Fixes: zotero#5910
2026-06-09 12:35:30 -04:00
Abe Jellinek
dcf5010415
Info box: Optimize rendering of many creator rows (#5939)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-06-08 15:43:32 -04:00
Dan Stillman
31d9e89165 Fix item tree focus checks broken by view-specific tree IDs
The item tree's DOM id carries a view-specific suffix (e.g.
"item-tree-main-default", "item-tree-main-recentlyRead"), but three
call sites compared against a single hardcoded "item-tree-main":

- Collection highlighting on Ctrl/Option (zoteroPane.js) -- match on
  the "item-tree-main" prefix to cover all views. This restores
  highlighting in Recently Read, where it silently failed.
- Focusing the items list after Add Item by Identifier (lookup.js) --
  use the current view's tree id instead of a literal that resolved to
  null and threw.
- Shift-Tab from the item tree to the toolbar (zoteroPane.js) -- key the
  actionsMap on the current view's tree id.

Add a test confirming focus lands on the items list after a lookup.

https://forums.zotero.org/discussion/130968/collection-of-selected-papers-is-not-highlighted-in-recently-read-panel
2026-06-08 13:21:15 -04:00
Dan Stillman
d0507e02c8
Restore -default suffix on item tree column-prefs keys (#5941)
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
The earlier fix (ef7896ab5f) migrated 9.0.x's "<id>-default" prefs
forward to the refactor's new "<id>" key, but that left 9.0.x and 10
beta keying differently in treePrefs.json, so switching between versions
always reset 9.0.x to defaults.

Put the `-default` suffix back so Z9 and Z10 read and write the same
key, and migrate any unsuffixed `<id>` data written by an earlier beta
back to `<id>-default` on first load. The migration runs once per
process and can be removed in a future release once beta users have all
updated.

https://forums.zotero.org/discussion/131653/zotero-10-beta-5-reinitializes-the-columns-in-main-pane
2026-06-05 13:10:01 -04:00
Christopher Holmes
31b3ead557
Abbreviate Atmospheric as Atmos. (#5938)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
example: 
"Atmospheric Chemistry and Physics" is currently incorrectly abbreviated as "Atmospheric Chem. Phys." should be abbreviated "Atmos. Chem. Phys."
2026-06-04 15:38:42 -04:00
Adomas Venčkauskas
615ba3c178
Improve Add/Edit Citation button behaviour when cursor is in a non-citation Zotero field (#5935)
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
When in bibliography, show an appropriate message. When in any other
non-citation Zotero field - just open an empty edit citation dialog.
2026-06-03 03:51:26 -04:00
Abe Jellinek
e029354a35
Fix US-formatted Last Read dates in en-* alias locales (#5934) 2026-06-03 02:35:15 -04:00
Dan Stillman
87901c1c21 Fix error when removing a tag stored in a non-normalized form
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
Zotero.Tags.init() keyed the id cache by the raw tag name from the
database, but getID() and Items._loadTags() both normalize names via
cleanData() (trim + NFC). A tag stored in a non-normalized form (e.g.,
non-NFC or with surrounding whitespace) could therefore never be matched
by getID(), which returned false. When such a tag was removed from an
item, that false was bound as the tagID parameter in the DELETE FROM
itemTags query, throwing "Invalid boolean parameter 1 'false'" and
aborting the save. Since this fired while applying changes during sync,
it persistently blocked sync.

To fix, normalize names in init() to match getID()/_loadTags().

https://forums.zotero.org/discussion/131901/error-report-seems-to-be-preventing-sync
2026-05-31 17:07:13 +02:00
Adomas Venčkauskas
8cf5c9b3d1 Fix ctrl-arrow navigation in item tree with no selection causing a select
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
Regression 4a003049
2026-05-28 15:17:45 +03:00
Adomas Venčkauskas
c838e9b29e Fix double-indent on collection tree (regression 5ca1fbb1)
Closes #5915
2026-05-28 15:17:45 +03:00
Adomas Venčkauskas
8277277948 Make Item Pane display the item count on Zotero startup 2026-05-28 15:17:45 +03:00
Adomas Venčkauskas
786960079b Fix item pane lagging 'items in this view' count by one view
Closes #5913
2026-05-28 15:17:45 +03:00
Dan Stillman
70c4cadab1 Fix full-text indexing failing silently after periodic vacuum or backup
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
vacuum() and the APFS-cloning offline backup path both close and
reopen the SQLite connection, which drops all ATTACHed databases --
including the in-memory "indexing" alias used for the fulltextWords
scratch table set up in Fulltext.init(). Once the connection is
reopened, indexing queries fail with "no such table:
indexing.fulltextWords", and indexItems()'s ignoreErrors path
routes the error to logError(), so indexing silently stops working
for the rest of the session.

Add an onConnect() hook on Zotero.DBConnection for per-connection
state that doesn't persist across reopens, and use it from
Fulltext.init() to re-attach the indexing DB on each reconnect.

Regression sources:

- 67288047f3 ("Use APFS cloning for file copies on macOS") flips
  online idle backups to offline-with-clone on APFS, so every idle
  backup interval (24h default) closes and reopens the connection.
  Affects Mac users on APFS.
- b27c4cb023 ("Enable SQLite WAL mode and add periodic VACUUM
  INTO") adds the vacuum path, which closes and reopens on the
  first idle period in a session that passes the freelist/time
  gates. Affects all users with sufficient DB churn, roughly once
  per 14 days.

https://forums.zotero.org/discussion/131576/debug-id-d848621212-indexing-of-pdfs-fail-zotero-9-0-3
https://forums.zotero.org/discussion/131718/possible-bug-regression-report-search-unusable-on-macos-zotero-9-0-3-with-large-library
2026-05-22 10:59:43 -04:00
Abe Jellinek
bf8c6f2027 Revert "Read Aloud: Fix 402 handling"
This reverts commit a692ca3493.

Not necessary on main.
2026-05-22 10:27:40 -04:00
Dan Stillman
cfd5596814 Disable locale selector when parent style specifies default-locale
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
For a dependent CSL style with no default-locale of its own, citeproc-js
parses the parent's XML and silently uses the parent's default-locale
over any user-selected locale, making the locale dropdown a no-op. Add
a Zotero.Style.effectiveLocale getter that falls back to the parent's
locale, and use it in updateLocaleList and the locale-selector custom
element to disable the dropdown in that case.

https://forums.zotero.org/discussion/comment/512891/#Comment_512891
2026-05-21 22:32:23 -04:00
Abe Jellinek
a692ca3493 Read Aloud: Fix 402 handling 2026-05-21 15:10:32 -04:00
Dan Stillman
9ca79a2cb1 Update Mac and Linux builds to Firefox 140.11.0esr 2026-05-21 10:43:53 -04:00
Dan Stillman
d2631cc614 Don't mark group attachment as unsynced when opened
For group items, attachmentLastRead is stored in a synced setting in My
Library rather than on the item itself, but since we didn't pass
skipSyncedUpdate, the item was being marked as needing sync, resulting
in a permission-denied error in read-only libraries.

https://forums.zotero.org/discussion/131717/opening-pdf-in-read-only-group-library-triggers-permission-denied-on-sync
2026-05-21 09:58:34 -04:00
Dan Stillman
ad4caa8688 Let sync API client handle its own 429/Retry-After throttling
The general HTTP layer's automatic 429/Retry-After retry only retries
the one failed request, but the sync layer wants to pause its entire
batch of concurrent requests via concurrentCaller.pause(). Add a
noRetryOnThrottle option to Zotero.HTTP.request() that throws on 429 or
503 with Retry-After so the caller can apply its own throttling, and
have syncAPIClient set it. Extend sync's catch block to also honor
Retry-After on 503 (previously only invoked via _check429).
2026-05-21 09:15:04 -04:00
Dan Stillman
37bf9d0965 Trigger full sync when server library version goes backward
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
When a user's account is deleted and then undeleted on the server, the
dataserver creates a fresh shardLibraries row at a low version, while the
client still has a much higher local library version. Subsequent syncs
then throw "_libraryVersion cannot decrease" and the user is stuck.

Tag the cannot-decrease errors from the library version setters with a
named error, catch it in Engine.start(), reset libraryVersion and
storageVersion to -1, and restart. The retry hits the existing
libraryVersion == -1 branch and runs _fullSync(), which re-uploads the
local library against the recreated server library.
2026-05-20 23:11:35 -04:00
Dan Stillman
f81e7cb5da Handle 429 and Retry-After in general HTTP layer
Previously only 503 + Retry-After was retried automatically; 429 was
handled only inside the sync API client. Extend _retryOnServerError()
to also retry on 429, honoring Retry-After on both 429 and 503 and
falling back to the existing exponential backoff otherwise.
2026-05-20 15:08:52 -04:00
abaevbog
8f44172342
citation dlg: sort libraries by cited count (#5925)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
In list mode, sort libraries by the count of their items
cited in the current document, falling back to alphabetic sorting
when counts are equal, with "My Library" always getting
priority over other groups.

Fixes: #5924
2026-05-19 15:40:55 -04:00
Abe Jellinek
b7437faf55 Convert ItemPane{SectionElement,Container}Base to ES modules
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-05-18 15:48:31 -04:00
Abe Jellinek
c2a9ea2687 Add Abstract section to merge pane with value chooser 2026-05-18 15:48:31 -04:00
abaevbog
5c232d65c0
Citation dialog: display first run guidance panel when the first item is added vs on initial open (#5917)
Display the first run guidance panel when the first
item is added to the citation vs on initial open.
2026-05-18 14:28:32 -04:00
Dan Stillman
596882980d Update reader submodule 2026-05-18 14:19:53 -04:00
Abe Jellinek
714d4416f8 Suppress Crash Reports and RemoteSettings console spam
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
2026-05-13 14:40:51 -04:00
Abe Jellinek
ba7e036582 Devtools: Fix profiler (Performance tab)
Fixes #5111
2026-05-13 14:01:55 -04:00
Adomas Venčkauskas
431e1dabde Fix first-column sizing after tree refactor. Closes #5908
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-05-12 10:35:05 +03:00
Martynas Bagdonas
789c83249c Avoid selecting tabs queued for close
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
When closing multiple tabs, close() could choose another tab from the same close set as the replacement selection. If that tab was an unloaded note, it could load during shutdown or bulk close and open separately. Choose only from tabs that will remain open.

Fixes https://forums.zotero.org/discussion/131203/closing-zotero-with-a-note-opened-as-a-tab-opens-the-note-in-a-separate-window
2026-05-11 19:54:11 +03:00
Dan Stillman
504447de41 Close advanced search window in collectionViewItemTree notify() test
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
The test opened an advanced search window but never closed it, which
likely caused the intermittent failures in the ZoteroPane focus() test
"should shift-tab across the zotero pane".
2026-05-07 14:25:58 -04:00
Dan Stillman
ddf2419e78 Update Windows builds with Word template compatibility fix from #5904
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-05-06 10:51:13 -04:00
Adomas Venčkauskas
d66682b04e
Undo Mozilla changes that allow non-UTF-16 command line args (#5904)
To allow old Word Zotero.dotm to continue to work
2026-05-06 10:27:13 -04:00
windingwind
fda72a3434
Fix beta build plugin max version compatibility check (#5903)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Ignore persisted compatibility flag from previous app version in non-stable releases to fix disabled for wrongly using old flag
Ignore max version compatibility for plugin update check on non-stable releases
Relevant: https://forums.zotero.org/discussion/131096/

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-05-05 14:26:50 -04:00
Wuy
575dc5351c Fix reader cleanup on tab close and event listener removal 2026-05-05 14:30:26 +03:00
Mi Ramon
4b2c0518f1
Add File menu options to note tabs (#5876)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Show in Library and Close

And standardize `showInLibrary()` as a ZoteroPane function
2026-05-04 15:37:32 -04:00
Dan Stillman
99228a6c76 Disallow changing item type to/from attachment, note, or annotation
The UI doesn't allow this, but plugins could do it, leading to sync
errors. (Can't do much for direct DB access.)

https://forums.zotero.org/discussion/131286/67734299-error-happens-in-my-zotero-plz-help-me
2026-05-04 15:30:27 -04:00
Mynacol
ede90ac04a
Make icons directory during build (#5889) 2026-05-04 15:27:03 -04:00
Abe Jellinek
fc37e524fe
Fix Find Full Text captcha loop on ScienceDirect (#5902) 2026-05-04 15:23:43 -04:00
Dan Stillman
8d59331d43 Fix quick search not expanding annotation rows after item tree refactor
https://forums.zotero.org/discussion/131294/quick-search-does-not-expand-annotations-10-0-beta4
2026-05-04 13:52:37 -04:00
abaevbog
3c53118f2c
Citation dialog: resizable width of collectionTree (#5892)
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
2026-04-30 14:43:54 -04:00
Dan Stillman
ef7896ab5f Fix lost item tree column choices after item tree refactor
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Before the refactor, main library column prefs were keyed under
"<id>-default", because the visibilityGroup getter returns 'default'
(truthy) for the main library. The refactor changed the suffix logic to
only append non-default groups, leaving existing prefs orphaned at
"<id>-default" while the new code reads/writes "<id>".

Fall back to the legacy key on load when the new key is missing or an
empty object (which can be written out by an unmodified post-refactor
build that flushed prefs on a visibility-group switch, like switching to
feeds), and drop the legacy key on the next write.

https://forums.zotero.org/discussion/131202/lost-columns-displayed-choice-in-the-items-tree-after-update-to-10-beta-4
2026-04-29 15:25:46 -04:00
Dan Stillman
178f162449 Add test for collection highlighting on Ctrl/Option
Covers the regression fixed by a7d001fc1d. The prior test for
_setHighlightedRowsCallback() called the callback directly, bypassing
the focus check and the keydown handler.
2026-04-29 15:21:29 -04:00
Dan Stillman
a7d001fc1d Fix stale references to old item tree DOM id
After the item tree refactor, the items tree id became "item-tree-main"
instead of "item-tree-main-default". Update the remaining call sites,
restoring collection highlighting on Ctrl/Option, focusing the item tree
after Add Item by Identifier, and Shift-Tab focus movement from the item
tree to the toolbar (which only still worked due to the native tab
order).
2026-04-29 14:57:02 -04:00
Dan Stillman
36e25d171a Update English strings for Transifex 2026-04-29 14:14:58 -04:00
Dan Stillman
a57cb6276f Prefs: Localize "Data Directory:" label in Advanced pane
Hard-coded as "Data directory:" in the original commit. Move to Fluent
and fix the dara-dir-label typo on the id (and the matching
aria-describedby) while we're here.

https://forums.zotero.org/discussion/130868/untranslated-string-data-directory
https://forums.zotero.org/discussion/131212/translation-not-reflected
2026-04-29 14:14:32 -04:00
Dan Stillman
373accce3b Update locales from Transifex 2026-04-29 12:19:13 -04:00
Dan Stillman
66dc97de33 Update English strings for Transifex 2026-04-29 09:44:57 -04:00
Abe Jellinek
03d94060b4 Replace chai-as-promised assertions in server_connector tests
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-04-28 13:33:58 -04:00
Abe Jellinek
13cb8d99b6 Server: Remove /connector/request endpoint
Never used.
2026-04-28 13:05:31 -04:00
Abe Jellinek
82b31574a8 Server: Test new browser detection behavior 2026-04-28 13:05:31 -04:00
Abe Jellinek
bc2ede0459 Server: Close request without response when browser is detected 2026-04-28 13:05:30 -04:00
Abe Jellinek
2e19017d0e Server: Improve browser detection 2026-04-28 13:05:30 -04:00
Abe Jellinek
2f38926da5 CasePreservingHeaders: Fix some Headers instances not being recognized
I guess the Headers class accessible from test code is different from
the Headers class in http.js (for the purposes of instanceof, at least).
2026-04-28 13:05:30 -04:00
Dan Stillman
5dc817db44 Validate Host header in connector server
Restores a check that was originally added in 10ad4c33 but lost in the
June 2024 server rewrite (#5148)
2026-04-28 13:00:40 -04:00
Dan Stillman
fd812070b6 Fix WebDAV download auth for usernames with special characters
HTTP.download() was rewritten in 0fe31b0f04 to use fetch() and build the
Basic auth header itself via btoa(username + ':' + password), but the
username and password come from nsIURI.username/password, which are
percent-encoded. As a result, a username like "user@example.com" was
sent as "user%40example.com", causing a 401 on every WebDAV download for
any user with @, :, space, etc. in their username. Other request types
were unaffected because they go through xmlhttp.open(method, url, true,
username, password), which decodes internally.

Decode username and password in _parseURI() so the values returned can
be used directly for Basic auth (and as a side effect, fix the other
display/use sites that were getting the percent-encoded form).

https://forums.zotero.org/discussion/131174/zotero-10-betas-1-2-3-cant-download-from-webdav-http-401
2026-04-28 13:00:40 -04:00
windingwind
e13e85b2e5
Fix plugin FTL registration (#5896)
Use one registry to avoid competing of language detect between plugins,
as resources declared as optional is identical to resource missing and thus fails the check and returns null for the string (https://searchfox.org/mozilla-esr140/source/intl/l10n/rust/l10nregistry-rs/src/registry/asynchronous.rs#140)
Register the plugin FTL for all languages Zotero supports with proper fallback logic so that even resources are declared as required, the check doesn't fail when plugin doesn't provide the resource.
Fix Zotero.File.getResourceAsync to use NetUtil channel to handle jar: url with `@`.
2026-04-28 12:00:04 -04:00
Dan Stillman
7ca8242a3e Update reader submodule
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-04-27 16:05:48 -04:00
Dan Stillman
a43d6a5d37 Fix erroneous data dir reuse in new profile on Windows
In a newly created profile, we check whether another profile is using
the default data directory, and if so, we create a new data directory
named after the profile (e.g., "Zotero Work"). But on Windows, paths in
prefs.js are stored with escaped backslaches (C:\\Users\\foo\\Zotero),
so searching for the JS string with literal single backslashes
(C:\Users\foo\Zotero) always failed, we would conclude that the default
dir was unused, and we would reuse the existing database.

To fix, escape backslashes in dataDir before the substring check.
Mac/Linux paths have no backslashes so this is a no-op there.
2026-04-27 16:04:52 -04:00
Dan Stillman
1ff2f640eb Remove Zotero for Firefox data directory migration code
The new-install branch in DataDirectory.init() read prefs.js from the
default Firefox profile to detect a pre-2017 dataDir setting, and the
read was unwrapped, making prefs.js access errors fatal at startup [1].
We could add a try/catch, but after nine years, it's probably safe to
just remove the migration. If anyone is returning from >9 years in the
wilderness and they still want old unsynced Zotero data, they can
manually move their data to the default location.

[1] https://forums.zotero.org/discussion/131176/installation-error-accessing-mozillas-pref-js-see-msg-pls
2026-04-27 16:04:52 -04:00
Mi Ramon
462357e49e
"Updating add-ons" → "Updating plugins" (#5893) 2026-04-27 14:50:51 -04:00
Adomas Venčkauskas
777d01b5c2 Item Tree: Column visibility logic improvements
Columns now have properties: `enabledIn`, `disabledIn` and `defaultIn`,
corresponding to column picker availability and default visibility. The
properties now filter based on attached collection view type instead of
visibilityGroup.

Visibility groups are for views where we want distinct column sets to
persist, like the feeds view.

Collection type properties are used to specify which columns are
available for a given type, regardless of whether it's in a different
visibility group or not.
2026-04-27 14:44:39 -04:00
Adomas Venčkauskas
5ca1fbb167 Item tree refactor megacommit
Split ItemTree megaclass into:
- ItemTree - concerned with drawing the virtualized table container and
  column interaction
- ItemTreeRowProvider - provides rows and issues notifications for
  render updates
- ItemTreeRow and subclasses - contains row-specific data and rendering
  logic
- CollectionViewItemTree and its accompanying classes - a version of
  ItemTree that renders items attached to a given Collection or
  CollectionView (CollectionTreeRow).

Various improvements in logic and rendering, separation of concerns.
2026-04-27 14:44:39 -04:00
Dan Stillman
6b42f9f9c0 Update translate submodule
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
2026-04-23 15:34:28 -04:00
Abe Jellinek
d7920e320b cookieContextId -> userContextId 2026-04-23 15:34:28 -04:00
Abe Jellinek
f0474fa250 Add support for clearing challenge in browser during translation 2026-04-23 15:34:28 -04:00
Dan Stillman
df6037ce0a Update Windows build to Firefox 140.10.0esr 2026-04-23 13:43:12 -04:00
Dan Stillman
24e16c0aad extract_xul_dll: Process whichever arch ZIPs are present 2026-04-23 13:38:26 -04:00
Dan Stillman
2d14f33de2 Windows build: Copy custom Firefox ZIP back to app/win/ 2026-04-23 13:38:26 -04:00
Dan Stillman
de69835419 Update Mac and Linux builds to Firefox 140.10.0esr 2026-04-23 11:40:55 -04:00
Dan Stillman
0b15b794e6 Offer to reset data directory on SQLite I/O error at startup
When a custom data directory is on a network share or in a cloud storage
folder, SQLite can fail to open with NS_ERROR_STORAGE_IOERR and the user
previously had to manually edit prefs.js to recover. Show a "Use Default
Location" button in the startup error dialog that resets the data
directory pref and restarts.

Addresses #4860
2026-04-23 11:38:29 -04:00
Adomas Venčkauskas
e7ed0b2881 Update Word for Windows submodule 2026-04-23 09:09:06 +03:00
Dan Stillman
a487dd9eef Rename pdf-worker submodule to document-worker
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
Repo moved to zotero/document-worker on GitHub

After pulling, run:

    git submodule sync
    git submodule update --init document-worker

If an old `pdf-worker/` directory is left behind, it can be removed manually.
2026-04-22 15:50:02 -04:00
Adomas Venčkauskas
9abd13fd22 Fix ZoteroPane tests failing intermittently 2026-04-22 10:16:05 +03:00
Dan Stillman
e1cd83214f CI: Deploy from version branches (9.0, 10.0, etc.)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
And drop reference to fx128 branch
2026-04-21 14:41:21 -04:00
windingwind
fa9a54b773
Fix plugin prefs.js change not taking effect after update (#5888)
Do not use cache for loading the prefs.js
2026-04-21 10:44:44 -04:00
Abe Jellinek
a835136d3b Update reader submodule
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-04-20 12:00:05 -04:00
Abe Jellinek
89ee4c21c0 Read Aloud: Fix network error string key 2026-04-20 11:08:19 -04:00
Adomas Venčkauskas
26816b03e2 Fix bad utilities submodule commit 2026-04-20 09:35:46 +03:00
Adomas Venčkauskas
07906931c1 Update utilities submodule 2026-04-20 09:31:23 +03:00
Dan Stillman
c52a6151dc Fix ZoteroPane.viewAttachment() tests after HTTP.download() rewrite
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
2026-04-17 15:36:21 -04:00
Dan Stillman
ce424c6b67 Fix attachment tests after HTTP.download() rewrite
Add a spy on Zotero.HTTP.download and update call-count assertions to
split between request() and download() calls. Previously download()
delegated to request(), so a single requestStub tracked everything.
2026-04-17 15:21:25 -04:00
Abe Jellinek
cc76207a23
Remove CookieSandbox, add new utility for cookie isolation (#5853) 2026-04-17 14:23:04 -04:00
Tom Najdek
32e43cb8f1
Fix tag paste to include existing field text at cursor position (#5873) 2026-04-17 14:20:52 -04:00
Dan Stillman
00ece3db68 Update reader submodule 2026-04-17 14:18:21 -04:00
Dan Stillman
0fe31b0f04 Rewrite HTTP.download() to stream via fetch() + ReadableStream
- Replace XHR-based download with fetch() + response.body streaming,
  writing chunks to disk via IOUtils instead of buffering the entire
  response in memory
- Separate out the retry and URL-parsing logic so it can be reused
  between request() and download()
- Split the ZFS download code into a request() with `followRedirects:
  false` to get the metadata headers and a separate download() to
  download the file

Fixes #5476, Downloading of large files is broken
2026-04-17 14:17:40 -04:00
Dan Stillman
45596e70e9 Work around Fluent issue leaving "{$version}" in post-upgrade banner
Some users see the literal "{$version}" placeholder in the banner,
likely due to some Fluent quirk triggered by plugin localizations.
(Missing localizations? Missing strings?) After translation, check the
version span and substitute manually if needed.

https://forums.zotero.org/discussion/129171/version-variable-is-displayed-in-zotero-8-upgrade-banner/p1
https://forums.zotero.org/discussion/130864/just-upgraded-to-zotero-9-on-debian-through-check-for-updates-and/p1
2026-04-17 11:32:22 -04:00
Dan Stillman
11c75ae6e5 Update reader submodule 2026-04-17 11:14:39 -04:00
Dan Stillman
c2c66795d2 Add some extra asserts to debug test flakiness in CI 2026-04-17 10:47:04 -04:00
windingwind
55672ba933
Fix removal logic for menu added by MenuManager (#5886)
Add the registered main key to menu class list so that they can be found when plugin is removed.
Add test for the fix.
2026-04-17 10:13:36 -04:00
Abe Jellinek
488fd29978 Read Aloud: Handle failure when trying to pause other tab
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
https://forums.zotero.org/discussion/130920/tts-breaks-after-automatic-theme-changes
2026-04-16 16:26:45 -04:00
Dan Stillman
d2d4451165 Fix WebDAV tests for Account pane rename
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-04-14 23:56:23 -04:00
Dan Stillman
a9fe8e3b62 Fix Account preferences tests
And update suite name
2026-04-14 22:46:37 -04:00
Dan Stillman
acfb7c7915 Rename zotero-prefpane-sync ID to zotero-prefpane-account
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-04-14 16:10:50 -04:00
Dan Stillman
7334080693 Rename sync preference files to account 2026-04-14 16:10:50 -04:00
Dan Stillman
c89abbc5be Rename Sync preferences pane to Account
- Restructure the pane layout: account info (username, email,
  login/logout) at the top, followed by a separate "Sync" section with
  an intro description and "Learn more" link, data sync options, and
  file syncing
- Migrate hardcoded "Zotero storage" strings to FTL and fix
  capitalization
- Rename the "Zotero" storage dropdown to "Zotero Storage".
2026-04-14 16:10:50 -04:00
Mi Ramon
676d609d0d
Add a icon for "Relate Items" (#5884) 2026-04-14 15:46:54 -04:00
Tom Najdek
ad4bb17db9
Fix CSS specificity causing tag swatches to render in reverse order 2026-04-14 11:38:15 +02:00
abaevbog
683ecf1620
fix related item sometimes not selected on click (#5885)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
https://forums.zotero.org/discussion/130887/clicking-related-fails-zotero-report-id-1418809492
2026-04-13 13:09:53 -04:00
Dan Stillman
56ef61e45b Store vacuum.lastTime as seconds, not milliseconds
Also drop the default pref, since this is just internal state
2026-04-13 12:39:51 -04:00
Dan Stillman
f20eb5b566 Fix crash when annotation item is missing itemAnnotations row
Don't throw when updateDisplayTitle() retry fails after loading
annotation data for a corrupt item. Also add an integrity check to
delete annotation items without itemAnnotations rows.

Fixes https://forums.zotero.org/discussion/130872
2026-04-13 12:29:57 -04:00
Dan Stillman
62815bdd00 Update version to 10.0
Some checks failed
CI / Build, Upload, Test (push) Has been cancelled
2026-04-10 13:41:18 -04:00
Dan Stillman
1ed1fab55b Switch to 9.0 branch for 9.0 releases 2026-04-10 13:40:13 -04:00
Dan Stillman
16b63a97dd Remove concurrent backup tests
These tested racing an offline backup against an in-progress online
backup, which wouldn't happen in practice (schema backups run at startup
before the idle observer, and someone is very unlikely to perform a DB
integrity check immediately after returning from an idle that triggered
a backup). The tests relied on fragile timing and a shared tmp file,
causing failures on Linux after the WAL checkpoint change. The tests
were already skipped on macOS after switching to APFS clones.
2026-04-10 13:35:57 -04:00
Dan Stillman
b27c4cb023 Enable SQLite WAL mode and add periodic VACUUM INTO
- Switch journal mode from DELETE to WAL for better write performance.
  With EXCLUSIVE locking mode, SQLite uses heap memory for the WAL
  index, avoiding an -shm file. Set synchronous=NORMAL (matching what
  Mozilla uses for Places). Checkpoint WAL on database close so the
  .sqlite file has all data (for copies or backups).
- Add periodic database compaction on idle (after DB backup) using
  VACUUM INTO and do an atomic file swap back to zotero.sqlite if no
  writes occurred during the operation. Check if vacuuming is needed
  based on time interval (default 14 days) and freelist ratio (default
  10% threshold).
- Disable auto_vacuum, which causes fragmentation and is unnecessary
  with periodic VACUUM
- Remove the VACUUM call from the integrity check, which was always just
  an awkward hack to let people trigger a VACUUM without having an
  explicit button

Closes #652
2026-04-10 13:19:32 -04:00
Dan Stillman
e22e2cd9dd Fix hardcoded version in post-upgrade banner 2026-04-10 11:21:20 -04:00
Abe Jellinek
3e04f5e3e7 Enable Read Aloud in stable builds
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-04-09 15:35:02 -04:00
Dan Stillman
a0198e2571 Support pinned incrementals from deploy server
Fetch pinned-incrementals-{platform} from the deploy server and merge
with the normal last-N incrementals list. This ensures incremental MARs
are always built from key versions (e.g., last release of each major
version) regardless of how many newer versions have been released.
2026-04-09 14:35:18 -04:00
Dan Stillman
e96d65f0ed Fix manage_incrementals on Cygwin
Remove Windows path conversion that was needed for aws s3 cp but
breaks scp (which interprets the drive letter colon as a hostname).
2026-04-09 12:32:03 -04:00
Dan Stillman
1a89239a13 Separate build and deploy for release scripts
Split build_and_deploy into build_for_deploy (builds, uploads to S3,
rsyncs manifests) and a deploy script in client-downloads (updates
updates JSON, updates incrementals, runs deploy command).

Release scripts support -b flag for build-only (deploy later manually).
Beta/dev/test scripts build and deploy automatically via SSH.

Move incrementals files from S3 to deploy server. Update DEPLOY_PATH to
root of client-downloads so manifests and deploy script paths are
derived from it.
2026-04-09 11:37:05 -04:00
Dan Stillman
a1baa68006 Update reader submodule 2026-04-09 11:30:51 -04:00
Dan Stillman
f82efc766d Fix Added/Modified By visibility when switching between library types
The `_getColumns()` column cache key didn't account for library type, so
switching between personal and group libraries reused cached columns
from the previous library type. This caused `groupLibrariesOnly` columns
(Added By, Modified By) to be missing in group libraries or incorrectly
shown in personal libraries.

Additionally, `VirtualizedTable.componentDidUpdate()` only recreated its
internal `Columns` object when the id prop changed, not when the columns
prop changed, so even with correct column rebuilding in `ItemTree`, the
table wouldn't pick up the new column list.

https://forums.zotero.org/discussion/130521/added-by-column-missing
2026-04-09 11:29:02 -04:00
Dan Stillman
ea230faa26 Add deployment script for 9.0
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
2026-04-08 14:58:25 -04:00
Dan Stillman
c3d8058794 Update locales from Transifex 2026-04-08 14:49:52 -04:00
Dan Stillman
117fa1dddb Update citeproc.js to include Juris-M/citeproc-js#275 2026-04-08 11:19:01 -04:00
Abe Jellinek
4b14ca9af4
Set Recently Read threshold relative to most recent item (#5878)
Some checks are pending
CI / Build, Upload, Test (push) Waiting to run
And persist cutoff within session
2026-04-07 14:10:18 -04:00
Dan Stillman
91e82566ff Update locales from Transifex 2026-04-07 11:00:22 -04:00
Dan Stillman
70047b6fd6 Update English strings for Transifex 2026-04-07 11:00:22 -04:00
Mi Ramon
5671a724e8
Fix broken Find option under Edit menu in reader (#5877) 2026-04-07 10:38:50 -04:00
Dan Stillman
a3aa2bb5c7 Add general-more-information FTL string 2026-04-07 10:33:11 -04:00
Dan Stillman
3f0de027a2 Auto-resolve lastRead sync conflicts by picking the most recent value 2026-04-07 10:29:25 -04:00
Dan Stillman
4398a2bcee Update reader submodule 2026-04-03 15:49:23 -04:00
Dan Stillman
67288047f3 Use APFS cloning for file copies on macOS
Add Zotero.File.copyFile(), which uses clonefile() on APFS with a
fallback to IOUtils.copy(), and use it for all significant file copies.
APFS is detected and cached for each parent folder via
Zotero.File.isAPFS(), which uses statfs().

On APFS, all database backups now use the offline (close/clone/reopen)
path instead of the SQLite online backup API. Cloning is nearly
instant, and backup files share disk blocks via copy-on-write, saving
potentially gigabytes of space.

Closes #5330
2026-04-03 14:33:21 -04:00
Dan Stillman
b11d776b36 Update locales from Transifex 2026-04-03 13:19:02 -04:00
Dan Stillman
92c876e47d Fix switching-accounts error on loginCancelled from stream server
Only resolve for known events, matching the polling behavior.
2026-04-03 12:17:03 -04:00
Dan Stillman
d5ee350081 Disable snapshot saving in feed item translation itest
To try to fix intermittent CI errors
2026-04-03 09:58:47 -04:00
Dan Stillman
5a347f4be5 Update style test for NLM/Vancouver style changes 2026-04-02 16:11:47 -04:00
Dan Stillman
a602d21258 Remove erroneous license header 2026-04-02 15:57:19 -04:00
Dan Stillman
7f9fdc0786 Update translators, styles, CSL locales, and renamed-styles.json 2026-04-02 15:51:43 -04:00
Dan Stillman
2d88a3f181 Update English strings for Transifex 2026-04-02 15:48:14 -04:00
Dan Stillman
0ff3ec6a1b Move file renaming strings to fileRenaming.ftl
Move file-renaming-* and rename-files-preview-* strings from zotero.ftl
to a dedicated fileRenaming.ftl. Rename rename-files-preview-* keys to
file-renaming-preview-window-* for consistency.
2026-04-02 15:48:14 -04:00
Dan Stillman
c2ce0af87a Update Windows build to Firefox 140.9.0esr 2026-04-02 15:20:52 -04:00
Dan Stillman
92db62df0a Update reader submodule 2026-04-02 15:16:23 -04:00
Tom Najdek
862573eabc
Per-group file renaming settings (#5862)
Also:

- Add `isAdmin` property to libraries
 
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-04-02 15:15:06 -04:00
Abe Jellinek
41b5442952 Add string for Read Aloud from Here 2026-04-02 14:42:42 -04:00
abaevbog
c6a95fd5b7
citation dlg: fix CI test breakage (#5870)
- prevent the focus handler from resetting the cached
items in search handler
- wait for search to finish after changing dialog type
in "should not display empty note child rows" test
2026-04-02 11:27:43 -04:00
Dan Stillman
842115ff7e Update Mac and Linux builds to Firefox 140.9.0esr 2026-04-02 11:20:52 -04:00
Dan Stillman
8effdacf65 Update English strings for Transifex 2026-04-01 14:57:52 -04:00
Dan Stillman
d2a01d02f8 Move zotero-text-link margin reset to base _textLink.scss
Not sure why this was only in preferences.scss
2026-04-01 14:53:46 -04:00
abaevbog
6f5f221db0
citation dlg: no irrelevant child rows in itemTree (#5863)
Pass filterChildItems to itemTree from citationDialog
to filter out unwanted child rows before adding them
in toggleOpenState.

In annotations mode, do not add child notes and
attachments without annotations
In add-note mode, only keep child notes that have content
2026-04-01 14:05:40 -04:00
Bogdan Abaev
df18ac4e1f citation dlg: updated icons
- updated annotations dialog type icons for dark and light mode
- use checkmark instead of right arrow for "accept" button
- fix incorrect sizing of "accept" and "cancel" icons
2026-03-31 14:53:11 -04:00
Bogdan Abaev
0e7ed13a43 citation dlg: variable min-width for list/library
- Allow different min-width for list vs library layouts.
In library mode, we need at least 1000px to have a functional
itemTree, especially when sidepane is present. But for list
mode, 1000px is very wide, so min-width should be 800px.
Increased min-height of itemTree from 200px to 400px
- When the dialog closes, store the width/height of
the current dialog's layout. Then, when the dialog opens
next time in that layout, set window's width/height based on last saved
params. That way, one can open a citation dialog in list
mode at 800px, then open annotations dialog (always library mode)
at 1000px, and then have the citation dialog in list mode open
again at its intended 800px.
- If one switches from library to list mode, add a smooth
window resizing animation to shrink diagonally from current width
to 800px. If one switches from list to library mode,
window smoothly resizes to 1000px min-width. This is
not applied to linux where win resizing behavior is a bit jumpy.
2026-03-31 14:53:11 -04:00
Tom Najdek
f3ecf285b3
Fix flakiness in "should shift-tab across the zotero pane" test 2026-03-31 01:38:28 +02:00
Mi Ramon
293cd25fb6
fix bg color for highlighted and selected rows in collection tree (#5855)
regression from a365465
2026-03-30 16:50:43 -04:00
Mi Ramon
5f942684b6
fix: indentation for some settings items is not taking effect on Windows (#5867) 2026-03-30 16:49:18 -04:00
Tom Najdek
2ad4618efb
Exclude ©, ®, and ™ from emoji detection regex 2026-03-29 17:51:27 +02:00
Abe Jellinek
fb8389d654
Read Aloud: Persist last-read position in synced setting (#5860) 2026-03-27 15:22:02 -04:00
Dan Stillman
391e8497a6 Handle per-object 403 on settings upload and reset to remote version
If the server returns a write report with 403 for admin-only settings,
mark successful settings as synced and reset rejected settings to
their remote values by re-downloading from the server.

Needed for #5862
2026-03-26 15:54:28 -04:00
Dan Stillman
1267440187 Update reader submodule 2026-03-26 14:23:26 -04:00
Dan Stillman
16380e4a19 Pass includeEmails=1 in /keys request when opening Sync settings 2026-03-26 13:11:26 -04:00
Dan Stillman
6140088b0c Swap order of annotation and note buttons in citation dialog 2026-03-25 13:14:12 -04:00
abaevbog
1d437d88f0
citation dialog: insert annotations followup (#5858)
- fix breakage when initially setting annotation dialog
   mode to library
- on click of a non-annotation item in suggested items,
   select the top-level library if the item could not be selected
   in the initial state of the itemTree
- increase min-width of the dialog to 1000px to ensure
   there's always space for the sidebar.
2026-03-24 21:07:19 -04:00
Dan Stillman
8a0a798672 Update word processor submodules for Add Annotation 2026-03-24 15:38:33 -04:00
Dan Stillman
0e8b195c5a Add tests for image annotation placeholder in notes 2026-03-24 12:09:15 -04:00
Dan Stillman
8167c903f3 Fix WebSocket 4409 error on sync logout
Properly remove the sync streaming subscription before clearing the
API key on logout.
2026-03-24 12:09:15 -04:00
Dan Stillman
322b764091 Show email addresses in sync settings
Fetch emails from /keys response and login session result, persist them
in the local database, and display them in the sync settings for both
logged-in and logged-out-but-linked states.

Also unify the separate logged-out-linked and logged-in containers into
a single layout that toggles elements based on login state.

Requires dataserver and stream-server changes
2026-03-24 12:09:08 -04:00
Dan Stillman
5ac56ae717 Fix removing from Recently Read in read-only group libraries 2026-03-24 11:51:37 -04:00
Dan Stillman
2522be0c17 Add test for removing group item from Recently Read on settings sync 2026-03-24 11:36:43 -04:00
Dan Stillman
a3909af5ab Clear lastRead in Item#fromJSON() when absent from API response
When the server returns an attachment without lastRead (because it was
cleared), fromJSON was not clearing the local value since it only
processes fields present in the JSON. Add explicit check after field
processing to clear lastRead when absent.
2026-03-24 11:36:43 -04:00
Martynas Bagdonas
6998235a31 Allow note creation when image annotation rendering fails
Fixes #5857
2026-03-24 11:56:53 +00:00
abaevbog
81591eb890
ItemTree: + expands all rows one level at a time (#5797)
On + keypress, expand the rows down one level, instead of
expanding all rows at once. If some rows are expanded
and some are collapsed, expand to the deepest level
currently visible.
E.g. if some regular items are already expanded,
expand only remaining collapsed regular items. If there
is a row with expanded attachments, expand all rows.

On - keypress, collapse the rows up one level to be consistent.

https://forums.zotero.org/discussion/129596/optional-annotations-in-item-list
https://forums.zotero.org/discussion/130365/expand-all-shift-in-zoteros-behavior-changed
2026-03-23 15:25:30 -04:00
Bogdan Abaev
6bec3c817e Citation dialog: allow to insert annotations (#5356) 2026-03-23 15:00:22 -04:00
Dan Stillman
587d5cda7e Show placeholder for image annotations without cache image in notes 2026-03-23 14:31:41 -04:00
Dan Stillman
351860273f Fix error creating note from image annotations when PDF file is missing 2026-03-23 14:31:41 -04:00
abaevbog
5823247df9
itembox: fix lost focus on tab from invalid DOI (#5856)
Fix lost focus on tab from DOI field after it is updated
from a valid to an invalid value.

https://github.com/zotero/zotero/pull/5850#discussion_r2976238955
2026-03-23 13:43:23 -04:00
Dan Stillman
d21a5d9cad Use Fluent strings for annotation type names
This also fixes title casing for "Image Annotation"/"Ink Annotation" in
the items list.

https://github.com/zotero/zotero/pull/5356#issuecomment-4112317776
2026-03-23 13:28:45 -04:00
Abe Jellinek
763330d1f4 Prevent attachment preview from bumping lastRead
And also use new _isTransient() to control lastPageIndex update, instead
of overriding _setState() as a no-op in ReaderPreview.
2026-03-23 12:54:00 -04:00
Mi Ramon
6fa5c4b3e3
Add "View Online" for PMID/PMCID (#5850)
resolves: #5845
2026-03-23 12:41:57 -04:00
Dan Stillman
fd7cc84f6b Fix quicksearch in Recently Read to match parent items
Add includeParents to the Recently Read search so parent items are
included in the search scope alongside their child attachments.
2026-03-23 11:22:01 -04:00
Dan Stillman
5bceea7b7b Fix Added By backfill skipping items when batch count doesn't change
Use itemID offset to advance through batches instead of comparing
result counts, which would bail out when the number of remaining items
equaled the previous batch size.
2026-03-23 10:25:34 -04:00
Dan Stillman
46ba21fe75 Add More Information link to Switch Accounts confirmation dialog 2026-03-23 10:25:33 -04:00
Abe Jellinek
8af91b0147
Bump lastRead when reader is closed (#5852) 2026-03-21 13:41:54 -04:00
Dan Stillman
e629a3c456 Update global schema to version 42 to enable lastRead fetching 2026-03-21 13:31:11 -04:00
Dan Stillman
6e5738de0f Try to fix missing Added By for some items
Backfill groupItems rows that have lastModifiedByUserID but null
createdByUserID (not totally sure how), and track completion with a
per-library flag in the settings table so the backfill only runs once
(since createdByUserID could potentially be permanently unavailable).
2026-03-21 12:27:27 -04:00
Dan Stillman
518056f9e7 Notify item tree after backfilling group item users
Fire a modify notifier event after _updateGroupItemUsers() so the
item tree refreshes to show the newly backfilled Added By / Modified By
values. Also change error handling from return to break so partial
progress still triggers the refresh.
2026-03-21 12:21:17 -04:00
Dan Stillman
ba3c3a506f Add test for preserving synced on lastRead setting download 2026-03-20 17:03:23 -04:00
Dan Stillman
e28c97dc56 Fix synced group item marked as unsynced when lastRead setting is applied
When a lastRead synced setting for a group item was downloaded and
applied via AttachmentReadObserver.notify(), the group attachment item
was saved without skipSyncedUpdate, causing it to be marked as synced=0.
On the next sync, this could trigger an upload attempt that failed with
403 for read-only group libraries.
2026-03-20 16:55:02 -04:00
Dan Stillman
7b17187a1a Update reader submodule 2026-03-20 15:46:42 -04:00
Dan Stillman
eb3a136ccd Use new batch setting deletion API endpoint
Use DELETE /settings?settingKey=key1,key2 instead of individual DELETE
per key, with batching matching uploadDeletionBatchSize.
2026-03-20 15:43:03 -04:00
Abe Jellinek
0ade1b9ed5 Show Attachments column by default in Recently Read 2026-03-20 15:19:03 -04:00
Dan Stillman
98d911f6b1 Add "Switch Accounts" flow for logged-out-but-linked state
When a user logs out without removing local data, the sync pane now
shows a third UI state with the linked username, a "Log In" button
(constrained to the same account), and a "Switch Accounts…" button
that shows a hard confirmation dialog requiring the user to type
"remove local data" before proceeding. On confirmation, local data
is reset and the app restarts, reopening the account preferences.
2026-03-20 13:11:52 -04:00
Dan Stillman
883747e372 Improve hard confirmation dialog textbox
- Show confirmation text as placeholder
- Auto-focus the textbox
- Fix border styling
2026-03-20 13:11:38 -04:00
Dan Stillman
35713fefd0 Upload setting deletions during sync
Deleted settings were written to syncDeleteLog but never uploaded. Add
dedicated setting deletion upload in _startUpload() using individual
DELETE requests per setting, since the API doesn't support batch setting
deletion.
2026-03-20 11:25:27 -04:00
Dan Stillman
3ebd26acc0 Fix saving lastRead in read-only library from synced setting
The same skipEditCheck fix from 5dc9705294 but for the synced setting
handler, which updates lastRead when a lastRead setting arrives via
sync for a group attachment.
2026-03-20 11:25:27 -04:00
Dan Stillman
5dc9705294 Fix saving lastRead in read-only library 2026-03-20 08:48:03 -04:00
Dan Stillman
cf7fcd751f Move lastRead column string from .properties to .ftl
Follow-up to 34039991f8
2026-03-19 16:19:52 -04:00
Dan Stillman
1d31ed2508 Fix sync runner test for updated login error button text 2026-03-19 15:48:16 -04:00
Dan Stillman
1502214b5d Web-based login flow
Replaces the login form with a "Log In" button that creates a dataserver
login session and opens the website for logging in. The client polls for
session completion every few seconds and also watches for a notification
from the streaming server.
2026-03-19 15:07:53 -04:00
Abe Jellinek
1f702fc5dc Read Aloud: Add sentence-granularity skip strings 2026-03-19 13:46:44 -04:00
Dan Stillman
c9f893a5a2 Update reader submodule 2026-03-19 12:46:37 -04:00
Martynas Bagdonas
376fba55b6 Update note-editor submodule 2026-03-19 15:57:30 +00:00
Dan Stillman
8401b83cb6 Update dateModified and lastModifiedByUserID when trashing 2026-03-19 11:35:15 -04:00
Dan Stillman
1fad46f983 Disable OA PDF lookups in feed item translation test
Avoid external network requests during test that may have been a source
of intermittent CI failures.
2026-03-19 11:04:22 -04:00
Dan Stillman
5b5b70b499 Show file size in MB instead of KB in ZFS quota warning 2026-03-19 10:38:57 -04:00
Dan Stillman
489a46564f
Add "Remove from Recently Read" (#5847)
- Non-force delete clears `lastRead` on all child attachments (with
  prompt), force delete moves to trash (matching collection behavior)
- Re-run search on item modify in Recently Read view so items disappear
  when lastRead is cleared (also handles sync changes)
2026-03-19 10:30:18 -04:00
Dan Stillman
f0af0bec9d Fix lastModifiedByUserID update overwriting createdByUserID
Use ON CONFLICT to update only lastModifiedByUserID when
createdByUserID isn't being set, rather than REPLACE INTO, which
replaces the entire row.
2026-03-19 00:10:02 -04:00
Dan Stillman
07d99d891f Fix breakage from aaed7ddfc0 in non-standard collectionTreeRow objects
Guard against collectionTreeRow missing isWithinGroup() (e.g., in
Advanced Search)
2026-03-18 23:28:29 -04:00
Dan Stillman
aaed7ddfc0 Add Added By and Modified By columns for group libraries
- Add columns as options in item tree, visible only in group libraries
- Fall back to createdByUserID for Modified By when lastModifiedByUserID
  is not set
- Update lastModifiedByUserID on local saves when dateModified changes
- Fix backfill in _updateGroupItemUsers() to process all batches
- Update formatColumnName() to support Fluent string keys

Closes #233
2026-03-18 23:19:11 -04:00
abaevbog
288fa8e401
hide recently-read pseudo-collection during filter (#5849)
Same way as unfiled, duplicates and trash pseudo-collections
are hidden
2026-03-18 12:05:16 -04:00
Tom Najdek
085a6fa5e0
Move search condition localization strings to Fluent (#5848) 2026-03-18 11:25:58 -04:00
Dan Stillman
d20cac39c9 Fix unhandled promise rejections in ConcurrentCaller when clearing queue
When stop() or stopOnError rejected queued task promises, the rejection
handlers (via Promise.allSettled) weren't attached yet, causing Mozilla
to report each one as "uncaught exception: Object". Add no-op catch
handlers before rejecting so the runtime knows they'll be handled
downstream. Also fix stopOnError path to reject with CanceledException
instead of undefined, and remove legacy Bluebird handledRejection flag.
2026-03-17 13:59:52 -04:00
Abe Jellinek
34039991f8
Track attachment last-read time, add Recently Read virtual collection (#2854)
Track when attachments are last opened or read, storing a `lastRead` Unix timestamp on the attachment. For user library items, `lastRead` syncs as an attachment property in item JSON. For group library items, it syncs via a per-user synced setting (like `lastPageIndex`).

- Add `lastRead` column to `itemAttachments`
- Add `AttachmentReadObserver` to update `lastRead` on file open and page change (throttled to 5 min for page changes)
- Add "Recently Read" virtual collection (items read in last 14 days, sorted by `lastRead` descending)
- Add `lastRead` search condition with date operators
- Add `lastRead` item tree column with new `dependsOnChildren` property for parent item aggregation
- Add `getItemLastRead()` to return max `lastRead` across child attachments

Also:

- Generalize collection tree SCSS to support universal (context-fill) icons alongside themed icons

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-03-17 13:52:00 -04:00
Dan Stillman
0ce5cd5bac Clear Read Aloud voices pref when logging out 2026-03-17 11:44:09 -04:00
Dan Stillman
08c75006f3 Update English strings for Transifex 2026-03-16 15:26:28 -04:00
Dan Stillman
290d44480e Move new "Log In"/"Log Out…" strings to Fluent 2026-03-16 15:25:34 -04:00
Dan Stillman
43f422a989 Update English strings for Transifex 2026-03-16 15:16:10 -04:00
Dan Stillman
a9342cc242 "Log in with your zotero.org account" → "[…] Zotero account" 2026-03-16 15:15:07 -04:00
Dan Stillman
426d681b65 Remove Citation Explorer string
Final string should be committed with #3468 (and in Fluent)
2026-03-16 15:12:42 -04:00
abaevbog
fb4ac78291 citation dlg: fix details popup buttons overflow (#5838)
For locales with long button labels.
https://forums.zotero.org/discussion/130208/spacing-is-off-in-citation-detail-pop-up
2026-03-12 14:46:45 -04:00
Tom Najdek
fa476245fe
Fix "Rename from Parent" button sometimes not appearing (#5835)
After editing the parent item, switching back to the attachment item before the parent item's save completes creates a race condition between the save and the attachment render.

Moved item pane tests related to file renaming to a separate describe() block.
2026-03-11 14:39:37 -04:00
Tom Najdek
518e7d22a0 Fix a flaky test on CI 2026-03-11 14:35:39 -04:00
Tom Najdek
3328557d00 Revert "Temporarily disable attachment-pane tests due to test failures"
This reverts commit 810626e458.
2026-03-11 14:35:39 -04:00
Tom Najdek
259abb15f5 Revert "Temporarily disable attachments-pane tests due to test failures"
This reverts commit 3d09df1419.
2026-03-11 14:35:39 -04:00
Tom Najdek
d3509ae259 Add support for test retries and enable three retries on CI 2026-03-11 14:35:39 -04:00
Dan Stillman
be1373dd3a Update reader submodule 2026-03-10 11:35:46 -04:00
Tom Najdek
fcce3bb21d
Fix autocomplete popup always showing scrollbar
Previous styling caused the autocomplete popup to render a scrollbar even when all items were visible.
2026-03-09 23:32:41 +01:00
windingwind
f130e3b3e5
Fix menu API icon inheritance (#5832)
Also:

- Change menu API l10nArgs from object to string to be consistent with other APIs and function calls
2026-03-09 15:42:42 -04:00
abaevbog
25a96c35ca
citation dlg: fix itemTree dblclick stealing focus (#5833)
Make sure bubble-input is refocused after double-click
on itemTree, same as as after adding an item to citation
via Enter or + click.

Fixes: #5830
2026-03-09 14:56:47 -04:00
Dan Stillman
d2d66d322c Fix UNC paths test for updated Zotero RDF translator
zotero/translators@9906b24d changed the file path predicate from
rdf:resource to z:path.
2026-03-09 14:55:25 -04:00
Dan Stillman
db70d92b4c CLAUDE.md: Clarify that Fluent should be used for all new strings 2026-03-09 14:45:43 -04:00
Dan Stillman
2c234c4c0a Update reader submodule 2026-03-09 10:28:32 -04:00
Dan Stillman
2f6825027a Add Director fallback to firstCreator for Video Recording items
The firstCreator/sortCreator SQL now falls back through primary →
editor → director → contributor. This ensures Video Recording items
with only a Director creator show that creator in the items list.

Also refactors the repetitive SQL-building in _getFirstCreatorSQL()
and _getSortCreatorSQL() into helper functions.

Closes #5720
2026-03-09 10:28:06 -04:00
Dan Stillman
f9a10e1874 Fix Title column not recoverable when hidden by a plugin
If a plugin (or corrupt treePrefs.json) sets the Title column to
hidden and no other primary column is visible, force-show it.

Closes #5747
2026-03-09 10:28:06 -04:00
Dan Stillman
f6811bd1a8 Add Citation Key column
https://forums.zotero.org/discussion/130194/feature-request-citation-key-column-in-library-window
2026-03-08 14:46:39 -04:00
Dan Stillman
e60bf91e31 Fix PDF magic number sniffing matching anywhere in first 200 bytes
The %PDF- sniffer entry had no offset constraint, so it matched anywhere
in the file sample. This could cause ZIP files containing PDFs to be
misidentified as application/pdf when opened, since %PDF- could appear
within the first 200 bytes of the archive. Pin the match to offset 0,
where it must appear in a valid PDF.

Fixes https://forums.zotero.org/discussion/130161/zip-files-and-indexing
2026-03-08 14:46:39 -04:00
Abe Jellinek
81f80a62e3 Update RDF tests 2026-03-06 16:51:25 -05:00
Abe Jellinek
12942ec717 Update translators 2026-03-06 16:51:25 -05:00
Adomas Venčkauskas
ce11d5295c Update LibreOffice submodule 2026-03-06 13:57:53 +02:00
abaevbog
b80bcea184
add citationKey to quicksearch-titleCreatorYear (#5814)
So quicksearch and citation dialog in list mode search
by the citation key, in addition to title,creator,etc.

https://forums.zotero.org/discussion/130090/zotero-picker-i-cant-search-item-by-citation-key
2026-03-05 20:08:52 -05:00
Abe Jellinek
0abf2b455f
Add Read Aloud first-run guidance panel (#5818)
Plus guidance-panel changes:

- Fix description not updating when multiple panels exist
  in the document
- Fix nonfunctional noautohide attribute
- Show "Got It" button for noautohide with no navigation

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-03-05 20:07:06 -05:00
Dan Stillman
20735c425f
Add resetData() as lightweight alternative to resetDB() for sync tests (#5822)
resetData() clears user data and sync state using application APIs
instead of doing a full Zotero shutdown/reinit cycle, making it much
faster for tests that just need clean state between runs.

Also make Tags.init(), Creators.init(), Users.init(), and
SyncedSettings.loadAll() properly clear their caches before reloading,
so they are safe to call more than once.
2026-03-05 15:54:10 -05:00
windingwind
76c7a613a9 Fix attachment preview 2026-03-05 12:24:26 -05:00
Mi Ramon
c0c9437da8
use different notes icons in itempane and tab sidepane (#5799) 2026-03-04 23:46:17 -05:00
Abe Jellinek
3a11ac146d Item merging: Don't merge trashed attachments of any type 2026-03-04 23:14:53 -05:00
Abe Jellinek
c9d108ba5e Extract merge logic to new module
Without changes. Most of this code was never called outside items.js,
so there was no reason to expose it.
2026-03-04 23:14:53 -05:00
Dan Stillman
810626e458 Temporarily disable attachment-pane tests due to test failures
https://github.com/zotero/zotero/issues/5810
2026-03-04 23:08:25 -05:00
windingwind
c4df6b2151
Fix should register other main window menus test (#5812) 2026-03-04 22:56:18 -05:00
Dan Stillman
3d09df1419 Temporarily disable attachments-pane tests due to test failures
https://github.com/zotero/zotero/issues/5810
2026-03-04 22:52:41 -05:00
Dan Stillman
9d5f46587e Disable Better BibTeX <9.0
https://forums.zotero.org/discussion/comment/508650/#Comment_508650
2026-03-04 22:43:24 -05:00
Dan Stillman
7c61d282ee Use our own blocked-plugin message that doesn't mention Mozilla 2026-03-04 22:43:11 -05:00
Abe Jellinek
a6a919234a Update reader submodule 2026-03-03 17:37:06 -05:00
Abe Jellinek
f07d7077b5 Read Aloud: Move caching to RemoteInterface 2026-03-03 17:36:51 -05:00
Abe Jellinek
4e60a9fbca Read Aloud: Don't log FTL fetching errors
appLocalesAsBCP47 contains locales that we don't have FTL files for, so
ignore those errors.
2026-03-03 16:07:47 -05:00
Abe Jellinek
db124860ed Read Aloud: Use POST for /speak 2026-03-03 15:40:35 -05:00
Dan Stillman
f5dd72dfb5 Run CI tests against zip-round-tripped build output
Instead of testing against the build/ directory directly, zip it up first
and then unzip it back into build/ before running tests. This ensures
tests run against the same artifact that gets uploaded to S3 and used by
the deployment scripts, catching issues that only manifest after a zip
round-trip (e.g., resolved symlinks, missing files).

Also makes Ruby setup conditional on deployment branches
2026-03-03 09:53:07 -05:00
Adomas Venčkauskas
1cd12791f6 Update LibreOffice submodule 2026-03-03 11:45:32 +02:00
Abe Jellinek
520a12ebe0 Update reader submodule 2026-03-02 17:51:17 -05:00
Abe Jellinek
b6ffa16893 Add failsafe to ensure pdf.js build worked 2026-03-02 17:46:05 -05:00
Abe Jellinek
a7c76e3202 CI: Trigger new build 2026-03-02 17:33:38 -05:00
Abe Jellinek
041fa48664 Read Aloud: Annotate Last Sentence → Annotate Sentence 2026-03-02 15:32:35 -05:00
Abe Jellinek
13f1606dc7 Read Aloud: Implement onPurchaseReadAloudCredits() 2026-03-02 14:47:14 -05:00
Abe Jellinek
3f90e60cd0 Remove AudioPlayback actor
Not necessary, and it caused some error spam.
2026-03-02 14:13:39 -05:00
Abe Jellinek
e63ed25b3f Read Aloud: Don't localize keys in Annotate Last Sentence 2026-03-02 13:32:28 -05:00
Abe Jellinek
8adee749e0 Read Aloud: Add key hint to Annotate Last Sentence string 2026-03-02 13:19:56 -05:00
Abe Jellinek
b0b2378866
Add support code for Read Aloud (#5355) 2026-03-02 13:00:23 -05:00
abaevbog
5905638907
Clear tag selector when itemTree selection changes, if needed (#5602)
Similar to how quickSearch is cleared already.

To avoid rerunning itemTree refresh twice (once for
clearing quickSearch and once for clearing tag filter),
clear tags and search text directly on collectionTreeRow.
And then call itemTree.refreshAndMaintainSelection to
refresh everything. Without it, tag selector sometimes
would find itself out of sync with current search
results and display tags as if quickSearch was not
cleared.

Fixes #5601
2026-02-28 15:41:32 -05:00
abaevbog
578361913a
Citation dialog: scroll to 1st cited row in library mode (#5782)
Scroll to the first cited item in this citation in current collection

Fixes: #5780
2026-02-28 15:33:36 -05:00
abaevbog
da4978354b
avoid reloading linked item each time it's fetched (#5565)
item.loadAllData() completely reloads all data and
may clear unsaved changes. Instead, Zotero.Items.loadDataTypes
will only load data once if it hasn't been loaded yet.

Followup to fefad8a9e3
per discussion in https://github.com/zotero/zotero/pull/5547#issuecomment-3373597427
2026-02-28 15:24:08 -05:00
Adomas Venčkauskas
003bdab3ff Auto-recover ALL-CAPS macOS corrupted word field codes.
Closes zotero/zotero-word-for-mac-integration#38
2026-02-26 16:22:28 +02:00
Tom Najdek
f0d3347b26
Fix tag splitting for strings containing Unicode characters. Fix #5807 2026-02-25 11:30:55 +01:00
Dan Stillman
f21e1b2d32 Add platform-native file change watcher for storage sync
Use platform-native file change APIs to track which stored-file
attachments have been modified, avoiding expensive full scans of all
attachment files during sync.

Backends:
- macOS: FSEvents (persistent event journal, survives restarts)
- Windows: ReadDirectoryChangesW (live recursive directory watch
  via overlapped I/O polling)
- Linux: inotify (live per-directory watches)

On unsupported platforms or on error, falls back to existing scan logic.
2026-02-24 14:15:15 -05:00
Dan Stillman
2739b29709 Avoid excessive /file requests during file sync
Don't mark attachments for download when server has no file (mtime is
null), and clear TO_DOWNLOAD state on 404 responses in both ZFS and
WebDAV so items aren't re-checked on every subsequent sync.
2026-02-22 23:45:55 -05:00
Adomas Venčkauskas
3386490a03 Fix browser toolbox switching windows breaking script debugging 2026-02-20 12:11:25 +02:00
Dan Stillman
2aaf0c213f DataObject: Set type-derived properties on prototype instead of per-instance
Move _ObjectType, _objectTypePlural, _ObjectTypePlural, and
_ObjectsClass from per-instance properties to prototype properties,
computed once per subclass on first instantiation. This avoids a huge
number of duplicate string allocations and reduce memory usage a bit
(e.g., ~35 MB in a 19K-item test library).
2026-02-17 12:09:59 -05:00
Dan Stillman
0d16d81a3c Share a single cached CSL.Engine instance across output formats
setOutputFormat() just sets this.opt.mode and this.fun.decorate, so
there shouldn't be a need to cache separate engines per format. The
format is excluded from the cache key, and setOutputFormat() is called
on cache hit instead.

Also simplify _preloadFormat() in quickCopy.js to only create one
engine instead of two (one per format).

Saves ~7 MB of resident memory (~140 MB before the citeproc-js
sort-macro caching optimization).
2026-02-17 12:09:59 -05:00
Dan Stillman
fe74fce120 citeproc-js: Cache sort-mode macro expansions to reduce memory usage
Previously, macros referenced by sort keys were expanded inline into
the sort token array every time they were encountered. Non-sort macros
were already cached in state.macros[name] and referenced via closures,
but sort-mode macros were duplicated across sort keys.

With a complex style like chicago-shortened-notes-bibliography (155
macros, 6 macro-based sort keys), this produced 179,605 tokens and
355,652 closures in bibliography_sort alone -- 99.9% of all tokens.

This commit caches sort-mode macro expansions in state.sort_macros[name]
using the same closure-reference approach as non-sort macros.

In addition to the memory savings, avoiding the redundant macro
expansion makes engine initialization dramatically faster with complex
styles.

Results with chicago-shortened-notes-bibliography:
- Engine initialization: 464 ms -> 57 ms (8x faster)
- Per-engine heap (Node.js): 123 MB -> 6.7 MB (94.6% reduction)
- Resident per engine (Firefox): ~140 MB -> ~7 MB
- Total tokens: 187,893 -> 11,669 (93.8% reduction)
- Total closures: 370,982 -> 21,823 (94.1% reduction)
- All 1,502 citeproc-js tests pass (at f88a47e6)
- Same 1501/1505 pass on master (same 4 pre-existing failures)
2026-02-17 12:09:59 -05:00
Dan Stillman
7fe4e50c4c HTTP.request(): Don't try to output responseText if not text response 2026-02-14 16:10:30 -05:00
Dan Stillman
c9ab1ec62c WebDAV: Compute fresh Digest auth headers for each request
The previous approach of capturing and replaying the Authorization header
from an initial request worked for Basic auth but broke with Digest auth,
where the header includes the HTTP method and URI in the hash. This caused
each replayed request to be rejected with a 401, which Firefox wouldn't
retry because the request already included Authorization.

Instead, split _channelAuthorization into _basicAuthHeader and
_digestParams. For Basic auth, the header is still replayed as-is. For
Digest, the challenge parameters (realm, nonce, qop, etc.) are cached
and a fresh Authorization header is computed per request.

_getAuthorizationHeaders() now takes method and URI parameters so it can
compute the correct Digest response hash for each request.
2026-02-14 16:10:30 -05:00
Abe Jellinek
ae397ca37c Fix item type becoming 'undefined' when menu is dismissed with Escape
By adding a missing getter override. If a setter is overridden, the
corresponding getter also needs to be overridden, or it will implicitly
return undefined.

Fixes #5793
2026-02-10 14:34:00 -05:00
Dan Stillman
c377c4c7ec CLAUDE.md: Additional test guidance 2026-02-09 22:38:33 -05:00
Dan Stillman
427fb1d4d9 Update version 2026-02-09 22:38:33 -05:00
windingwind
94dbdd5981
Skip plugin strict_max_version check on non-stable releases (#5781)
Unless `extensions.strictCompatibility` is set to true

resolve: #5775
2026-02-09 22:30:09 -05:00
Dan Stillman
fcd887a89d Fix cross-suite ZoteroPane test failures
Failed when running CollectionTree and ZoteroPane tests together

- Make `deleteSelectedItems()` async and await `deleteSelection()` so
  the full Notifier commit chain (including items list refresh)
  completes before the function resolves. Update tests to call
  `deleteSelectedItems()` directly instead of calling fire-and-forget
  keyboard events, and add a stubbed test for the Delete key binding.

- Fix "Delete Item…" menu test to explicitly re-select after `saveTx()`
  to guarantee selection state
2026-02-09 22:18:50 -05:00
abaevbog
9640875fcf
Space on an option of menulist dropdown will select it (#5783)
Fixes: #5779
2026-02-09 16:43:23 -05:00
Dan Stillman
c35d7f21e9 Upload build-{os}.json manifests to deployment server
Needed by zotero/client-downloads@4b2060361, which reads build
metadata from these files instead of hardcoding it.
2026-02-09 15:24:07 -05:00
Dan Stillman
8a1bdacf7d Fix WebDAV verification failure on OPTIONS/PROPFIND auth type mismatch
`checkServer()` was capturing the Authorization header from OPTIONS and
explicitly setting it on PROPFIND via `setRequestHeader()`. If the auth
type didn't match what the server required for PROPFIND (e.g., Basic vs.
Digest, though maybe other things too), the explicit header could
prevent Firefox from negotiating the correct auth scheme on the 401
challenge.

To fix, move `onAuthorizationHeader()` from OPTIONS to PROPFIND and
don't pass captured auth to PROPFIND, letting Firefox handle the auth
transparently.

https://forums.zotero.org/discussion/129665/webdav-error-for-zotero-8-0-3
2026-02-09 14:21:58 -05:00
Dan Stillman
5cb83c69c6 Fix incorrect bold text in file-renaming banner 2026-02-09 14:21:58 -05:00
Yago Iglesias
fe3021701e
Use grep -E instead of egrep (#5789) 2026-02-09 09:54:19 -05:00
Dan Stillman
06794ce278 Update note-editor submodule 2026-02-06 15:57:43 -05:00
Dan Stillman
fa30baf523 Add CLAUDE.md with project guidance for Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:54:09 -05:00
Dan Stillman
c7014b97c7 Update locales from Transifex 2026-02-06 15:47:31 -05:00
Dan Stillman
752b0f7008 Convert preferences_sync.xhtml from spaces to tabs 2026-02-06 15:15:30 -05:00
Dan Stillman
d966eee6a7 Update English strings for Transifex 2026-02-06 00:55:29 -05:00
Dan Stillman
2f7055e15d Update CSL locales 2026-02-06 00:51:33 -05:00
abaevbog
bd8d56e384
Preference to hide context annotation rows (#5315)
hideContextAnnotationRows pref, accessible via View > Hide Non-Matching Annotations,
will hide non-matching context annotation rows.
Enabled by default.

Fixes zotero#5264
2026-02-06 00:51:01 -05:00
Tom Najdek
bbb39719f0
Tweak hover color for banner buttons (#5776) 2026-02-04 12:10:24 +01:00
Dan Stillman
eecdd66098 Use PROPFIND instead of OPTIONS to cache WebDAV credentials
Some WebDAV servers allow unauthenticated OPTIONS requests, so the
Authorization header capturing added in 089701eca8 wouldn't work.

PROPFIND with Depth: 0 reliably requires authentication while only
returning properties of the directory itself.

https://forums.zotero.org/discussion/comment/506993/#Comment_506993
2026-02-03 23:42:45 -05:00
Abe Jellinek
5e82c888b6 Scaffold: Fix repeated reload prompts 2026-02-03 16:14:25 -05:00
Tom Najdek
0f565ecfef
Replace × close links with buttons in banners (#5735) 2026-02-03 15:51:24 -05:00
abaevbog
7b0f5d8f32
update menulist keyboard nav and appearance (#5773)
- on all platforms, Space will open the menulist popup
- on Windows, ArrowUp/ArrowDown will change selection without triggering command event.
This is needed to avoid alerts that are meant to fire after a confirmed
selection when the user is just trying to navigate the menu (e.g. item type menu)
- on Windows, fix odd appearance of checked menulist options in advanced search
condition dropdowns when "More" option is selected
- on Linux, fix menulist focus ring not appearing in Advanced Search
or Preferences (https://forums.zotero.org/discussion/129498/advanced-search-drop-down-menus-are-not-highlighted-during-keyboard-navigation)

Fixes: #5772
2026-02-03 15:22:30 -05:00
Dan Stillman
2f064a5604 Rename deployment scripts 2026-02-03 14:34:37 -05:00
Tom Najdek
818ce564bb
Improve RTF scan. Add test coverage. (#5672) 2026-02-02 15:03:57 -05:00
Dan Stillman
331a83d2e8 Update reader submodule 2026-02-02 11:13:03 -05:00
Dan Stillman
56cd4713f1 Clear cached WebDAV credentials if we get a 401 2026-02-01 16:42:55 -05:00
Dan Stillman
089701eca8 Fix WebDAV authentication handling in fx140
Firefox no longer seems to send a previously used Authorization header
with subsequent requests. This results in extra requests, since every
WebDAV request triggers a 401, and also results in errors, because a PUT
is sent without Authorization, causing some WebDAV servers to
immediately send a 401 and close the connection, which the HTTP layer
interprets as a connection failure (status 0). (It's also not good to
try to send a large file just to get a 401.)

There might be some way to share context between requests, but instead,
just get the used Authorization header and include that explicitly in
future requests.

To test this properly, we have to switch to using httpd.js for all
WebDAV requests, since the mocked XHR doesn't trigger the 401 retry.

https://forums.zotero.org/discussion/129194/webdav-uploads-fail-on-zotero-8-put-sent-without-authorization-server-closes-connection
2026-02-01 16:42:55 -05:00
Dan Stillman
e7d1772470 Don't show Authorization header value in HTTP.request() debug mode 2026-02-01 16:42:55 -05:00
windingwind
1347023b39
Fix note context tabNotesDeck.selectedPanel null error (#5771)
fix: #5770
2026-02-01 11:06:10 -05:00
Dan Stillman
62894d56e5 Update version 2026-01-29 15:35:21 -05:00
Dan Stillman
a0758e016a Update locales from Transifex 2026-01-29 15:35:21 -05:00
Dan Stillman
0c97b6f74c Include collections in trash when generating item JSON
Any modification to an item belonging to collections in the trash would
cause those collections to be removed from the item when the item was
uploaded.

Fixes #5766
2026-01-29 15:14:02 -05:00
Dan Stillman
f3c0e4fdc2 Fix error resetting group library with unsynced embedded images
https://forums.zotero.org/discussion/129373/zotero-7-8-warning-when-syncing-group-library
2026-01-29 13:22:14 -05:00
Dan Stillman
2c2b6fe171 Update package-lock.json 2026-01-29 13:22:13 -05:00
abaevbog
22f62138a3
Fix quicksearch not finding annotations of standalone attachments (#5756)
Fix search not finding annotations of standalone attachments
when searching within a scope.

Fixes: #5751
2026-01-28 13:30:36 -05:00
windingwind
1969bde82c
Fix attachment preview discard logic (#5757)
Use keepAlive() to prevent deferred discard when the preview moves out of view but later enters the view again before the discard timeout.
2026-01-28 11:28:06 -05:00
Abe Jellinek
db9598f01e Update reader submodule 2026-01-28 10:28:23 -05:00
Abe Jellinek
c31386dc3e Update reader submodule 2026-01-28 10:01:15 -05:00
windingwind
aef4beee89 Fix note editor iframe docShell undefined error
Regression after 37ce3c0
2026-01-28 10:49:55 +01:00
Abe Jellinek
4880674668 Remove DOI when creating bookSection from book
And book from bookSection.

Closes #5732
2026-01-27 16:20:14 -05:00
Dan Stillman
85bff00a3c Update translators 2026-01-27 13:46:49 -05:00
Dan Stillman
0e78eb6709 Update locales from Transifex 2026-01-27 13:45:15 -05:00
Dan Stillman
2b262c5442 CI: Fix disabling of certain tests
Wrong property name
2026-01-27 13:34:25 -05:00
Tom Najdek
aabac2576c
Ensure automatic file renaming is enabled for group libraries (#5750) 2026-01-27 13:25:12 -05:00
abaevbog
56de1abb79
Citation dialog: localized CSL page locator in guidance panel text (#5746)
* citation dlg: local. CSL locator in guidance panel

Use localized short CSL page locator in guidance panel
text example ("p10")

Fixes: #5745

Also, fix dialog mode switch buttons not properly sizing
in locales where list/library string is longer.
2026-01-27 12:26:32 -05:00
Dan Stillman
7d79ec01a5 CI: Disable "should add an item to My Publications" test due to errors
https://github.com/zotero/zotero/issues/5749
2026-01-27 12:14:01 -05:00
Abe Jellinek
8b446b7fc5
Set auto attachment title when not renaming (#5748) 2026-01-27 11:44:50 -05:00
Tom Najdek
cc53bc1699
Fix clipped inputs in RTF Scan and related UI issues
* Remove `overflow: visible` in RTF Scan to prevent richlistbox from expanding the width and causing clipping
* Introduce small margins as an alternative to prevent focus rings from being clipped in the `wizard`
* Fix "Display as" alignment on Windows
2026-01-27 13:06:04 +01:00
Dan Stillman
98b0a3a741 Update utilities submodule
Fixes #5744
2026-01-27 00:39:40 -05:00
abaevbog
1f9d9d2967
citation dlg: updates to locator shortcuts (#5529)
- after a new bubble is added to the citation, it is recorded
as a just-added bubble. The next locator typed without a search query
will go to that item instead of going to the item before where the locator was typed.
Same logic applies when multiple bubbles are added at once.
- the record of just-added bubbles is cleared on focusout
or keypress of an arrow key. That way, it's discarded if the user
is almost certainly not intending to immediately type a locator.
- added a special case to recognize a numeric value as a page locator
if it is typed when just-added bubble is recorded. That special locator
will be added to the just-added bubble as one is typing without
pressing Enter after debounce. Enter will immediately add the locator
without waiting for debounce.
- if a just-added bubble is recorded, cmd-z will clear
whatever numeric locator may have been typed and place
it back into the input, in case one meant to type an
actual search query

- added a special case to recognize ":<number>" as a page locator
in the same circumstances that "page <number>" is currently recognized
- do not use year extraction (SearchHandler._cleanYear)
when parsing input. It strips the first number from
a range of numbers and conflicts with the new locator
logic.
- ensure a bubble with a very long locator does not overflow
2026-01-26 20:12:44 -05:00
abaevbog
83e1a6030a
fix windowIcon logged error in titlebar.js on win (#5743)
Instead of adding listeners to the window icon unless
no-titlebar-icon is set on the window, add listeners
to the icon whenever it exists. That way, one doesn't have
to remember to add the no-titlebar-icon attribute to
every window that doesn't have an icon.

Fixes: #5733
2026-01-26 20:11:18 -05:00
Dan Stillman
c9f34754a9 CI: Skip attachment pane preview test for now
Too many spurious failures

See #5624
2026-01-26 16:50:50 -05:00
abaevbog
ea1ef4492d
Citation dialog: restore first run guidance panel (#5740)
- restore guidance panel with instructions appearing on the first run
- clear firstRunGuidanceShown.quickFormat pref
- set slightly higher arrow padding on win/linux for the guidance
  panel. If the arrow is too close to the edge, it appears
  partly disconnected from the panel.

Fixes: #5738
2026-01-26 16:34:53 -05:00
Abe Jellinek
3c6ad491b5
Fix citations accumulating due to CSL.Engine caching (#5742)
* makeFormattedBibliographyOrCitationList(): Don't modify CSL.Engine state

Use previewCitationCluster(), which leaves the processor state alone,
instead of calling appendCitationCluster(), which has side effects and
isn't used anywhere else in Zotero.

* Make CSL.Engine caching opt-in

* Remove unnecessary CSL.Engine reinitialization

* Code style

* Add/update tests
2026-01-26 16:12:36 -05:00
Dan Stillman
c42f3b8174 Fix citing in Serbian locale
Fixes #5741
2026-01-26 15:14:19 -05:00
Dan Stillman
e0ff103afd Update reader submodule 2026-01-26 12:29:05 -05:00
Tom Najdek
f58ca4b399
Fix missing ellipsis for styles in the style selector (#5736) 2026-01-26 11:29:11 -05:00
Adomas Ven
6697db1dba
Delete duplicate renamed styles on init (#5734)
After citation-style-language/styles#7928 renamed Vancouver styles to
NLM terminology, Zotero installations end up with both vancouver.csl
and nlm-citation-sequence.csl. To fix, on init, delete any installed style
whose ID appears in the renamed-styles mapping if the target style
also exists.

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2026-01-26 10:35:43 -05:00
Martynas Bagdonas
69002c122d Add string for "Black" reader theme 2026-01-26 17:06:48 +02:00
Martynas Bagdonas
6c4fde54be Add string for image color inversion option in reader themes 2026-01-26 16:44:41 +02:00
Dan Stillman
a34b845154 Update style test to use NLM citation-sequence instead of Vancouver 2026-01-26 00:43:06 -05:00
Dan Stillman
e3e122b69d Add script for pulling Fluent translations 2026-01-25 22:15:29 -05:00
Dan Stillman
67c10a9ff7 Update bundled styles and renamed styles 2026-01-25 22:12:52 -05:00
Dan Stillman
9891b6dc51 Update version 2026-01-25 14:48:55 -05:00
Dan Stillman
2ffd6a893b Don't load annotations or tags for items individually at startup
Load tags and annotations before itemData, since `_loadItemData()`
calls `updateDisplayTitle()`, which needs creators, tags, and
annotations to be loaded for some item types.

This should fix slow startup times in Zotero 8 in libraries with many
(e.g., tens of thousands) of annotations.

The slowdown here was introduced by #3416 loading annotations in the
`catch`, but it was already incorrect for tags to be loaded that way at
startup. Creators were loaded before itemData, but tags weren't.

The `catch` might not be necessary at all now that the loading order for
`loadAllData()` is fixed, but we'll need to test further to confirm
that.

Fixes #5724
2026-01-25 11:15:30 -05:00
Mark
b1b0d45353
Update Russian translation for reader.ftl (#5728) 2026-01-24 09:34:37 -05:00
Mark
3424ab511b
Update Russian translation for note-editor.ftl (#5727) 2026-01-24 09:34:33 -05:00
Mark
3c6c90a2a1
Update Russian translation for integration.ftl (#5726) 2026-01-24 09:34:27 -05:00
Mark
c21d8983de
Update Russian translation for zotero.ftl (#5725) 2026-01-24 09:34:21 -05:00
Dan Stillman
fef2178450 Don't load unnecessary item types for updateDisplayTitle() fallback
We don't need to try to load annotations for regular items or creators
or tags for annotations.

Every annotation item's annotation is still loaded independently, but we
can try to fix that separately. For now, just don't try to load data
that will never exist.

Addresses #5724
2026-01-23 13:07:57 -05:00
Dan Stillman
d53d760cb6 Remove obsolete call to updatePostUpgradeBanner() 2026-01-23 12:53:02 -05:00
Dan Stillman
aab4a08267 Revert disabled="disabled" to disabled="true" after #5719 2026-01-23 11:25:28 -05:00
Northword
dfca2f5862
Fix color of OK button in MS Word DocPrefs (#5719)
https://forums.zotero.org/discussion/129173/zotero-8-accent-color-of-the-ok-button-in-the-document-preferences-pane-of-ms-word-addins
2026-01-23 11:19:33 -05:00
Gemini
404667f6a9 Fix double-encoding of special characters in DOIs in word processor integrations
Disable wrap_url_and_doi for citeproc instances used in word processor
integrations to prevent brackets and other special characters in DOIs
from being double-encoded.

Fixes #5557
2026-01-23 12:58:17 +02:00
Dan Stillman
dcad02c1d4 Update Windows build to Firefox 140.7.0esr 2026-01-22 23:22:35 +00:00
Abe Jellinek
bd89c00d37
About window: Always pass centerscreen, and pass dialog=yes on macOS (#5718) 2026-01-22 15:52:39 -05:00
Dan Stillman
5dcb1a4bba Update Mac and Linux builds to Firefox 140.7.0esr 2026-01-22 15:25:13 -05:00
Abe Jellinek
3e248c703e
showAttachmentFilenames: Fall back on invalid attachment path (#5717) 2026-01-22 14:55:23 -05:00
windingwind
14a26038d5
Fix v-table stylesheet keys (#5714)
Fix custom column keys parsing issue in v-table
Use index of css suffix instead of dash for key parsing
Ensure style map key is escaped
2026-01-22 14:42:33 -05:00
Abe Jellinek
0d864ae83e
Fix contextPaneOpen race condition causing duplicate button (#5716) 2026-01-22 14:42:13 -05:00
Dan Stillman
620d1b96b9 Don't try to create incremental updates from Z7 for ARM Linux 2026-01-21 22:42:18 +00:00
Dan Stillman
4706601213 Update note-editor submodule 2026-01-21 11:45:16 -05:00
abaevbog
2c0bebdaf0
citation dlg: List mode tab always before Library (#5712)
In the segmented control switch, List mode tab always
appears before the Library. Tabs are no longer
reordered based on the default mode preference.
2026-01-20 16:29:25 -05:00
windingwind
da82e8aa28
Fix v-table shift-dblclick not trigger activateNode (#5711) 2026-01-20 16:10:04 -05:00
Dan Stillman
d681caba1c Update note-editor submodule for #5710 2026-01-20 16:04:59 -05:00
windingwind
6a4f36ad82
Add note editor content view mode (#5710) 2026-01-20 16:03:37 -05:00
Abe Jellinek
686ebb5a5c Update reader submodule 2026-01-20 14:51:05 -05:00
Abe Jellinek
4399329b83 renderItemTitle(): Only split on known tags
Fixes #5708
2026-01-20 12:58:44 -05:00
Dan Stillman
cc95b93741 Update translators 2026-01-20 12:06:55 -05:00
Dan Stillman
e9bcf6f10a Update global schema
Locale updates
2026-01-20 12:06:54 -05:00
Dan Stillman
42a4807d59 Update locales from Transifex 2026-01-20 12:04:18 -05:00
windingwind
86c4bc23c7
Fix Libraries and Collections box editable field is undefined in note editor (#5702)
fix: #5698
2026-01-20 11:56:04 -05:00
Tom Najdek
d6fc06cd7c
File renaming: Allow local time conversion for accessDate (#5707) 2026-01-20 11:00:52 -05:00
windingwind
7a61614450
Fix tag editor focus ring cut off in note editor (#5703)
fix: #5699
2026-01-20 10:58:49 -05:00
windingwind
97d0bdbaea
Fix note tab reopen logic (#5706)
fix: #5704
Distinguish note editor CE and instance in var name
Add note editor CE _initPromise
Await CE init in various places
Add tests for note tab
2026-01-20 10:56:04 -05:00
Tom Najdek
eb62aad999
Extend emoji matching to include symbols
These were previously matched, before d1d4065d
2026-01-16 13:31:02 +01:00
Dan Stillman
1b0442e773 Restore "Manage Styles…" link in doc prefs
https://forums.zotero.org/discussion/128985/manage-styles-option-missing-when-clicking-add-edit-citation-in-word
2026-01-14 11:16:31 -05:00
windingwind
37ce3c0ed7 Fix note editor toolbar window dragging 2026-01-14 23:53:50 +08:00
Dan Stillman
145fbfa194 Update note-editor submodule 2026-01-13 13:46:37 -05:00
Dan Stillman
0af7c56b7e Update sample data for tests, again
There's some bug where this has to be generated multiple times to be
correct.
2026-01-13 13:14:00 -05:00
Dan Stillman
564d3f90c7 Update utilities submodule 2026-01-13 13:05:23 -05:00
Dan Stillman
395aadff4e Update sample data for tests 2026-01-13 13:05:23 -05:00
Dan Stillman
2bb8ba60ad Fix test after adding eventPlace to conferencePaper 2026-01-13 12:50:00 -05:00
Dan Stillman
72321ead34 Update bundled styles and renamed-styles.json 2026-01-13 12:05:38 -05:00
Dan Stillman
42b559dda2 Update global schema 2026-01-13 12:05:38 -05:00
Dan Stillman
913a60fa0f Update locales from Transifex 2026-01-13 12:05:38 -05:00
Tom Najdek
d1d4065dae
Add support for displaying country flag emojis in the Items List (#5700) 2026-01-13 11:50:47 -05:00
Dan Stillman
bed1d1ff35 Update bundled styles and CSL locales 2026-01-13 11:37:02 -05:00
Dan Stillman
ddb3488680 Update reader submodule 2026-01-13 11:35:51 -05:00
Dan Stillman
e478c89ca0 Fix base-mapped fields not being migrated from Extra
https://forums.zotero.org/discussion/comment/505322/#Comment_505322
2026-01-13 11:35:51 -05:00
Tom Najdek
c37a79e066
Fix library tab fields not refreshing (#5697)
Previously, fields and the abstract in the library tab did not refresh after updates were made in the Reader’s context pane.
2026-01-13 11:35:32 -05:00
abaevbog
e899012e5e
fix wrong focus on shift-tab from note-editor (#5695)
Fixes: #5690
2026-01-12 16:07:32 -05:00
Dan Stillman
faf8f2510e Batch Extra field migrations in transactions 2026-01-07 11:07:16 -05:00
Dan Stillman
29bf6a7809 Add timing for Extra field migration 2026-01-07 11:07:16 -05:00
Dan Stillman
a67ad90b9a Remove unnecessary comment about testing Extra field migration
Just need to disable auto-sync to trigger migration
2026-01-06 11:20:27 -05:00
Dan Stillman
8c8da79116 Update note-editor submodule 2026-01-06 10:56:41 -05:00
Abe Jellinek
d04843e41d Update reader submodule 2026-01-05 10:46:29 -08:00
Abe Jellinek
21f0ba5a03 Reader: Support blob argument to onSaveImageAs() 2026-01-05 10:43:06 -08:00
Abe Jellinek
59f4dacc55 Add missing string for encrypted EPUB 2026-01-05 09:55:52 -08:00
windingwind
bb17ac2c74
Fix tab focus control (#5689)
fix: #5683
fix: #5684
fix: #5688

Rename tab hook focus -> focusFirst
Replace tab-focus custom event with tab hook refocus
Move context pane _handleTabReady to Zotero_Tabs
Focus note editor after opening tab
Use setTimeout to ensure the focus does not stay in tab element
2026-01-05 10:15:21 -05:00
windingwind
47e6a0f7ab Fix open Note in New Window doesn't focus existing window
fix: #5687
2026-01-03 11:37:27 +08:00
windingwind
46385b06fa Fix Libraries and Collections render not triggered in note windows
fix: #5685
2026-01-03 09:55:12 +08:00
Dan Stillman
58baa44ad4 Always use Zotero.CreatorTypes.getLocalizedString()
Since some strings are now stored in schema.ftl

https://forums.zotero.org/discussion/128757/z8-beta-films-dont-have-any-creator-types
2025-12-30 14:30:24 -05:00
Dan Stillman
c6ef23a650 Update locales from Transifex 2025-12-30 10:30:47 -05:00
Dan Stillman
6c48cd54ce Fix migrateExtraFields() when onProgress isn't passed 2025-12-29 17:59:25 -05:00
Dan Stillman
05e1d39217 Clarify comment for Extra field migration 2025-12-29 16:12:19 -05:00
Dan Stillman
7316aeb92c Fix Zotero.Feeds.updateFeeds() resolving too soon
Regression from 4d7a099c32
2025-12-29 16:04:21 -05:00
Dan Stillman
fb20db9a58 Delay Extra field migration until after auto-sync and show progress
This avoids redundant field migration on other devices if it has already
run on one. Conflict resolution should automatically ignore identical
changes, so it's not the end of the world if it runs in multiple places,
but we might as well avoid it if we can.
2025-12-29 15:56:20 -05:00
Dan Stillman
a5843d8e2d Add Zotero.startupSyncPromise
Resolves after initial auto-sync completes or after the initial
auto-sync would run if disabled
2025-12-29 15:56:06 -05:00
Dan Stillman
4d7a099c32 Add pause mechanism for feed updating
```
var handler = await Zotero.Feeds.pause();
[...]
handler.resume();
```
2025-12-29 15:56:00 -05:00
Dan Stillman
ef2f7049c7 Allow wider text for items in progress popup 2025-12-29 15:55:50 -05:00
Dan Stillman
b30c9bea11 Don't try to rename files for feed items 2025-12-29 15:55:50 -05:00
Dan Stillman
87bb7d17b2 Remove unnecessary test in file-renaming code
Regular items are by definition top-level items
2025-12-29 15:55:50 -05:00
Dan Stillman
689e59f161 Update global schema
Remove "Creator" from "Audio Recording"
2025-12-26 23:45:24 -05:00
Dan Stillman
0320a45d46 Show more granular error for generateItemJSONData() test 2025-12-26 23:36:09 -05:00
Dan Stillman
3256273015 Update global schema
"Uploader" → "Creator" on Video Recording
2025-12-26 23:35:43 -05:00
Dan Stillman
22d4bdcef8 Revert treating "arXiv ID" as a field in Mendeley import 2025-12-26 15:55:45 -05:00
Dan Stillman
f0ff173ad2 Update global schema to remove arXivID field 2025-12-26 15:36:13 -05:00
Dan Stillman
7ac22dbef2 Fix creator extraction from Extra
Creators shouldn't be extracted if creators of the same type already
exist on the item, to follow citeproc-js behavior, but the code to do
that was broken.
2025-12-26 11:08:19 -05:00
Dan Stillman
d1fd7c998b Fix Mendeley import test after new identifier fields 2025-12-25 22:37:14 -05:00
Dan Stillman
f7764ca9a4 Recognize "arXiv" for arXiv ID in Extra 2025-12-25 22:37:14 -05:00
Dan Stillman
7487595e6a Fix test error after adding type to document 2025-12-25 22:13:34 -05:00
Dan Stillman
7bf3084e65 Update utilities submodule
Fixes the CSL-JSON import problem mentioned in #5679
2025-12-25 22:04:41 -05:00
windingwind
b3ef63859d Destroy editor instance in CE properly 2025-12-25 23:15:33 +08:00
Dan Stillman
b94dac5586 Update sample data for new item fields 2025-12-25 00:20:34 -05:00
Dan Stillman
f0bbb9b370 Update global schema 2025-12-25 00:20:34 -05:00
Dan Stillman
611acd0703 Update global schema 2025-12-24 22:00:52 -05:00
Dan Stillman
0692985c80 Don't upload tags on item conflict if only tag order has changed 2025-12-24 22:00:07 -05:00
Dan Stillman
8bd3bf976a Update locales from Transifex 2025-12-24 13:24:38 -05:00
Dan Stillman
5fa92f87fe Update package-lock.json 2025-12-24 13:24:38 -05:00
Dan Stillman
e4ffcfc822 Remove "arXiv ID", "PMID", and "PMCID" from schema.ftl
Now hard-coded in update-schema in global schema repo

See zotero/zotero-schema@5d87c21fec
2025-12-24 13:24:38 -05:00
Tom Najdek
891a9809d0
Improve bibliography modal initialization (#5675) 2025-12-24 09:47:03 -05:00
windingwind
7d57c0c103
Fix note tab font reset menuitem (#5678) 2025-12-24 09:43:19 -05:00
windingwind
f149b8985b
Fix tab sidebar layout (#5677) 2025-12-24 09:43:03 -05:00
Dan Stillman
1564af89f2 Update reader submodule 2025-12-20 19:02:09 -05:00
Dan Stillman
7acb28099d Rename "Rights" field to "License"
Closes zotero/zotero-bits#63
2025-12-19 22:21:59 -05:00
Dan Stillman
d6fc071139 Load attachment data when handling file renaming on item metadata change
File renaming has a Notifier handler for item modifications that needs
best-attachment titles, but attachments and their item data might not be
loaded even when parent items are. (This came up when migrating fields
from Extra after a global schema update before the main window had
loaded.)
2025-12-19 22:03:19 -05:00
Dan Stillman
bf9e5d3a83 Don't try to hide/show Retracted Items if ZoteroPane is still loading 2025-12-19 22:02:36 -05:00
Dan Stillman
7422538fb3 Update English strings for Transifex 2025-12-19 22:02:16 -05:00
Dan Stillman
9e23214c77 Add English strings for new item fields and creator types 2025-12-19 21:57:44 -05:00
Tom Najdek
6bf4c371fc
Upgrade ftl-tx. Fix #5671. 2025-12-20 02:22:23 +01:00
windingwind
ca6dd437a5 Update JSDoc for menu manager API 2025-12-19 00:03:06 -05:00
windingwind
834d3b3bb3 Fix JSDoc for reader.js 2025-12-19 00:03:06 -05:00
windingwind
cf8dd78a4a Fix JSDoc for preferencePanes.js 2025-12-19 00:03:06 -05:00
windingwind
8a76aac83f Update JSDoc for itemPaneManager and itemTreeManager 2025-12-19 00:03:06 -05:00
Tom Najdek
51dae17314
Fix Bibliography modal race condition (#5638)
- Replace polling with a `ready` promise for initialization
- Use fixed height for the rich list and re-order initialization to avoid layout shifting
- Ensure the "accept" button is disabled until initialization completes
2025-12-18 23:59:28 -05:00
windingwind
ccfa109fcf
Fix note editor hard refresh after saving note (#5640)
Fix note editor instances' editing state lost after saving
2025-12-18 23:57:12 -05:00
windingwind
c60134e8f1
Add macOS Tahoe menu icons (#5603)
resolve: #5549
2025-12-18 23:51:36 -05:00
windingwind
e522f6185e
Add note as tab (#5528) 2025-12-18 23:49:42 -05:00
abaevbog
f1c7b7939c
Citation dialog: allow to switch between "Add Note" and "Add/Edit Citation" (#5586)
Allow one to switch between "Add/Edit Citation" and "Add Note" modes
in the citation dialog via segmented control in the bottom
left corner.

One cannot switch modes when editing an existing citation or
when inserting a citation in the note editor.

Use the same segmented control UI to switch between
list and library modes for clarity. Replace icons with
"List" and "Library" strings.

Collect some of the state variables into DIALOG_STATE object
for better organization.

Fixes: #5582

Also, remove earlier logic of not resizing window on refocus
on Linux if focus was lost <100ms ago. That workaround
no longer works, and now that we retain the window sizing
after search reruns, it is less of an issue.
Addresses: #5590
2025-12-18 23:18:35 -05:00
Dan Stillman
0cb8116bed Add default pref for showAttachmentFilenames
Missing from #4601
2025-12-18 23:06:33 -05:00
Abe Jellinek
06b2475b7a Fix ebook hyphenation pref not applying when reader opens
https://forums.zotero.org/discussion/128612/enable-automatic-hyphenation-not-working-after-closing-and-reopening-epubs
2025-12-18 15:01:32 -08:00
Adomas Venčkauskas
1233fd5a75 Fix an error occuring when attempting to insert an empty note
Addresses #5665
2025-12-18 09:32:43 +02:00
Bogdan Abaev
be8c64b2bf citation dlg: fix hang when main window is closed
When fetching open tabs for the citation dialog, fall back
to the last saved session state if the main window is closed.
Session state was already being updated when zotero pane
is closed.

Fixes: #5661
2025-12-18 09:21:03 +02:00
Dan Stillman
ead0924a79 Fix deletion of .dll in extract_xul_dll 2025-12-18 05:36:07 +00:00
Dan Stillman
d31fd8aac0 Update custom Firefox component hashes for Windows build 2025-12-18 05:35:41 +00:00
Dan Stillman
86823e45ef Update to Firefox 140.6.0esr 2025-12-17 15:37:41 -05:00
Abe Jellinek
aec284b0b8 Add "Normalize Attachment Titles..." menu item (#4601)
---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2025-12-17 12:56:04 -05:00
Abe Jellinek
2d79f1f959 Add option to show filenames in the item tree (#4601)
And move item tree pref observer into the item tree itself.
2025-12-17 12:55:56 -05:00
Dan Stillman
c6a6832444
Remove 1px solid transparent border on links (#5663)
And remove redundant CSS rules for `.zotero-text-link` in zotero.css,
now specified in _textLink.scss
2025-12-16 23:06:33 -05:00
Northword
b20084be43
Fix(Plugins): Prevent user preferences from being lost on plugin disable (#5646)
When a Zotero plugin is disabled, user-modified preferences can be inadvertently reset to their default values. This occurs under specific conditions where a parent preference has not been user-modified, but a child preference within that branch *has* been modified.

The root cause lies in the `Zotero.Plugins.clearDefaultPrefs` function. For preferences that do not have a user-set value (`!branch.prefHasUserValue(pref)`), it incorrectly calls `Services.prefs.getDefaultBranch('').deleteBranch(pref)`. While intended to clear unmodified defaults, `deleteBranch(pref)` operates on the *entire preference branch* starting with `pref`, inadvertently removing any user-modified sub-preferences as well.

This commit changes the problematic line from `branch.deleteBranch(pref)` to `branch.clearUserPref(pref)`. `clearUserPref(pref)` correctly removes only the user-set value for the *specific* preference `pref`, leaving default values and any user-modified sub-preferences intact. This ensures that only truly unmodified default preferences are cleared, preserving user data for related sub-preferences.

This fix prevents unintended loss of user settings when plugins are disabled.
2025-12-16 23:06:18 -05:00
Dan Stillman
60ece63df4 Bold only "Zotero 8" in the post-upgrade banner 2025-12-16 14:16:45 -05:00
Dan Stillman
4ca9bbdae5 Automatically show post-upgrade banner for each new major version
And remove the density switcher
2025-12-16 13:50:27 -05:00
Dan Stillman
7859016ec1 Update locales from Transifex 2025-12-16 12:17:54 -05:00
Dan Stillman
67fe951572 Remove obsolete "Focus Mode" string 2025-12-16 12:17:53 -05:00
Dan Stillman
34508e145c Update translators, styles, and CSL locales 2025-12-16 10:42:48 -05:00
Dan Stillman
6e44fc1c62 Update note-editor and reader submodules 2025-12-16 10:42:48 -05:00
Adomas Venčkauskas
3dec4217aa Fix remaped styles not working in Document Preferences dialog
If you're writing a document and a citation style is renamed
Zotero-side, then the citation dialog won't load with the style
preselected without this
2025-12-16 16:33:45 +02:00
abaevbog
ae9330b513
Fix no onActivate handling on double click of collectionTree rows (#5656)
After 015769a removed pointer-events: none from table cells,
clicking on the collectionTree would always trigger
a focusout event, ZoteroPane.handleBlur would call
collectionTree.setHighlightedRows, which would always
redraw the collectionTree. This complete tree redraw on
every click made it impossible to register double-clicks.

With this change, setHighlightedRows won't have
any effect if called with the same rows to highlight
as before (including no rows).
Without constant tree redraws, double-clicks fire as expected.

Fixes: #5655
2025-12-15 23:11:53 -05:00
abaevbog
e6068833ae
Properly display annotations as rows of relatedBox (#5585)
Fixes: zotero#5583
2025-12-12 14:55:27 -05:00
Abe Jellinek
8b9ca9dbce Update reader submodule 2025-12-12 11:27:11 -05:00
Abe Jellinek
a2c3e80f0f Add strings for Reading Mode 2025-12-12 11:27:06 -05:00
Martynas Bagdonas
6e723d3262 Update pdf-worker submodule 2025-12-11 13:07:04 +00:00
abaevbog
11223451d2
set libraryID on annotations in tests (#5644)
Fix tests breakage post 84764570f4
2025-12-04 08:27:57 -05:00
Adomas Venčkauskas
cc1b72ebf2 Update Word for Mac submodule 2025-12-04 10:25:58 +02:00
Abe Jellinek
84764570f4
Enforce annotationSortIndex format by type (#5641) 2025-11-25 12:59:09 -05:00
Tom Najdek
49325e27d9
Fix attachment matching filename isn't correctly retitled (#5637) 2025-11-17 10:40:38 -05:00
windingwind
2f2dfc9038
Fix should discard attachment pane preview after becoming invisible (#5630)
fix: #5624
2025-11-17 10:39:32 -05:00
Tom Najdek
b9ef88d4fc Fix alignment issues and missing icons 2025-11-14 19:45:58 +01:00
Tom Najdek
8a57f6c45b
Fix the "Edit → Select All" menu option not selecting items (#5626) 2025-11-13 16:11:48 -05:00
Tom Najdek
3f8a0b5b86
Fix misaligned text in the attachment box (#5632) 2025-11-13 10:33:08 -05:00
Tom Najdek
aec7b6a1ea
Persist the state of the Reader sidebar #5489 (#5593) 2025-11-13 10:32:34 -05:00
Abe Jellinek
6c70ccc66c Fix Edit menu items not working in some field context menus
Regression in c45ed42af.

Fixes #5629, should fix #5376 (though I couldn't reproduce that)
2025-11-12 13:18:12 -05:00
Dan Stillman
4bd829ebdb Fix potential sync error if unloaded editable library becomes read-only
https://forums.zotero.org/discussion/128001/app-not-syncing-to-newly-created-group
(error was in an existing group, not the new one)

Fixes #5625
2025-11-11 13:43:01 -05:00
windingwind
721306879a
Unify info section onContextMenu handling (#5622)
Fix menu API with date and custom fields
2025-11-11 11:26:22 -05:00
Dan Stillman
0748b09753 Update reader submodule 2025-11-11 10:28:17 -05:00
windingwind
01d764ef2f
Fix attachments pane test being unstable (#5610) 2025-11-10 14:18:09 -05:00
abaevbog
c45ed42af7
Fix broken new collection dialog opened from context menu of a group (#5592)
Fix new collection dialog appearing broken when
opened via "New Collection" option of context menu
on "My Library" or a group in collection tree.

On macOS, the popup of collections would never leave
and on windows, subcollection would never appear.

This is a followup to zotero#5409
that fixes this issue for all collections. The reason
why it didn't work for groups is that the command event
would fire not on a menuitem but on the <command> node itself,
which is not what the workaround expects.

Now, if we get such an event, we'll try to use the
original 'command' event dispatched on the <menuitem>
from event.sourceEvent to locate the <menupopup>
and as a blueprint for redispatching the event.
2025-11-10 14:14:47 -05:00
abaevbog
544e9d11a3
Fix itemTree quicksearch cleared when image pasted into a note (#5613)
Fixes: #5612
2025-11-10 14:06:30 -05:00
abaevbog
a7cc94c04c
fix createParent dialog stuck during quicksearch (#5596)
Fix createParentDialog hanging when opened during
quicksearch or in saved search. After a parent item
is added, itemTree is being refreshed. If _refreshPromise
is resolved after a timeout, it will not complete until the
modal dialog is closed. Instead, resolve _refreshPromise
after Zotero.Promise.delay, which uses the XPCOM global
timer unaffected by modals.

Fixes: #3026
2025-11-10 13:59:24 -05:00
Abe Jellinek
7877c63600
fx140: Fix Ctrl-Enter check (#5608) 2025-11-10 13:50:34 -05:00
Tom Najdek
e09e993081
Align buttons with the top line of the field (#5604) 2025-11-10 13:46:58 -05:00
abaevbog
260ae891f5
new fix to failing 'should refresh on item update' (#5605)
Another potential fix to the test failure. Earlier fix
from 30784dd241 seems
to not have worked.

A new explanation is that the test before it does not properly
wait for the trash to refresh before trying to select the library, in
which case collectionTree select event will be suppressed
and library selection will not happen.

Fixes: #5584
2025-11-10 13:45:08 -05:00
Tom Najdek
85d1f723fb
Restore the arrow in the guidance panel (#5611) 2025-11-10 13:44:05 -05:00
Tom Najdek
ef1fc4fc66
Fix missing labels for options under “Display citations as” (#5618) 2025-11-10 13:43:47 -05:00
Tom Najdek
b1941fbff4
Add logic to limit auto-rename banner to 7 days (#5621) 2025-11-10 13:42:43 -05:00
abaevbog
a2a6df529a
Scaffold UX tweaks (#5615) 2025-11-07 13:21:53 -05:00
Martynas Bagdonas
06afaf3316 Update pdf-worker submodule 2025-11-07 11:20:22 +00:00
Adomas Venčkauskas
49130c71d8 Fix connector save timing out with Advanced Search
Report: https://forums.zotero.org/discussion/127760/connector-conflict-with-advanced-search

Also restore Advanced Search tests that depend on the item tree. They
were disabled due to React timing issues. Hopefully with these changes
the initialization of the tree is more robust.
2025-11-03 09:24:35 +02:00
Abe Jellinek
2d6deda04a
Support zooming in basicViewer (#5599) 2025-10-28 12:28:15 -04:00
Tom Najdek
7aefa6d9ee
Use current collection when adding items (#5598) 2025-10-27 11:54:03 +01:00
abaevbog
59a789c750
Fix misaligned sync reset options radio button labels (#5587)
Fixes: #5580
2025-10-24 14:09:14 -04:00
abaevbog
30784dd241
fix 'should refresh on item update' failing (#5595)
This test would sometimes fail, most likely due to
the library sometimes not getting re-selected in the previous test
'should update custom header for items in the trash'.

A likely explanation is that the selection event in
collectionTree would still be suppressed when selectLibrary
is called, so make sure to wait for item deletion to
go through before trying to re-select the library.

Fixes: #5584
2025-10-24 14:08:15 -04:00
Abe Jellinek
5b82a38383 Don't cache CSL.Engine for integration 2025-10-22 09:13:15 +03:00
Adomas Venčkauskas
128f2229e6 Update Word for Mac submodule 2025-10-21 16:37:07 +03:00
Dan Stillman
d1f478fc4e Update translators, styles, and CSL locales 2025-10-14 12:03:25 -04:00
Dan Stillman
82f08cc4b8 Update note-editor and reader submodules 2025-10-14 12:01:23 -04:00
Abe Jellinek
892a4c48ce Style configurator: Log error and discard invalid locale values 2025-10-14 11:52:47 -04:00
Abe Jellinek
f621de41c7 Style configurator: Don't use string interpolation to build XUL 2025-10-14 11:52:47 -04:00
windingwind
9d91ff8df9
Improve attachments section test (#5576)
Cancel pending discard to avoid race condition
Cancel pending render/discard after tests
2025-10-13 13:18:51 -04:00
Abe Jellinek
9509c62918 Fix search test descriptions, remove outdated tests
Some descriptions weren't changed to match test behavior changes in
48fd23c, and the doesn't-match tests are no longer relevant after those
changes.
2025-10-10 12:39:26 -04:00
Abe Jellinek
e921a4bc7f Local API: Improve search debugging
Use our own method in order to debug scopes, with a new getter for
that property.
2025-10-10 12:39:26 -04:00
Abe Jellinek
7da9c0669d Local API: Match annotations (#4337)
Except in collection items responses.

And use scope searches for itemType/tag search syntax instead of
filtering in JS.
2025-10-10 12:39:26 -04:00
abaevbog
90972577af
ensure tag selector is initialized before using (#5574)
If the tag selector is not initialized in setScope,
initialize it before proceeding. This fixes
the tag selector being stuck in loading state
after expanding.

Fixes: #5573
2025-10-10 10:25:59 -04:00
Tom Najdek
18a34b938f
Remember last import settings (#5465)
Closes #5303

Also:

* Ensure the file chooser dialog opens only once
2025-10-10 10:22:57 -04:00
Dan Stillman
9d22c5406f Add special handling for general-sentence-separator string 2025-10-09 15:32:04 -04:00
Dan Stillman
6359d69ef4 Update locales from Transifex 2025-10-09 15:32:04 -04:00
Abe Jellinek
52f5ab035f Document Preferences: Add missing FTL to fix variable in title
Fixes #5570
2025-10-09 10:37:12 -04:00
Dym
05e4982245
Local API: Resolve 500 error on single item export (#5560) 2025-10-08 19:48:17 -04:00
abaevbog
2e3deb0a21
tagsbox: fix deleted tags reappearing (#5533)
https://forums.zotero.org/discussion/110655/z7-beta-tags-reappear-after-quickly-deleting-them

If a library has many tags and one quickly clicks on "-" button
to deleted multiple tags from an item, the database updates
may not keep up with UI. To address this, save item only
once after a debounce.

Fixes: #5410
2025-10-08 15:37:17 -04:00
windingwind
cc4c8cd191
Keep tabs menu focus when using up/down (#5346) 2025-10-08 15:33:45 -04:00
Abe Jellinek
46f879dcdb
Scaffold: UI tweaks and testing overhaul improvements (#5441)
- Remember diffs from test runs, show in sidebar when test is selected
- Add button to immediately update test, without a prompt
- Indicate when test has a custom defer delay set (although no tests currently do)
- Use a persistent test store as source of truth, instead of data attributes on the listbox rows
- A bit of code cleanup

Closes #5420, closes #5419
2025-10-08 15:16:49 -04:00
Abe Jellinek
e2fc3d35b8
Style editor: Simplify exposed methods, add shortcuts for refresh (#5545) 2025-10-08 14:51:42 -04:00
Abe Jellinek
da0578fc39 Remove special handling for URLs dragged from Firefox (#4649)
And the temporaryPDFHack that we used for them.
2025-10-08 14:30:21 -04:00
Abe Jellinek
dbce2b33b3 Use extension-less filenames when adding multiple attachments at once (#4649)
And use a new notifier queue in the ZP routine (like we already did in
the item tree) to prevent notifier events from firing in the middle.
2025-10-08 14:30:21 -04:00
Dan Stillman
ee9ea5d5a1 Update locales from Transifex 2025-10-08 13:48:47 -04:00
Tom Najdek
b75ebd6f41
Fix auto-rename not triggering when creators change (#5567) 2025-10-08 18:19:21 +02:00
Abe Jellinek
77420760b0 Quick Copy: Don't leak pref observers when uninitialized 2025-10-08 12:04:21 -04:00
Abe Jellinek
6131da92c4 Quick Copy: Fix init race condition
QuickCopy.init() calls Style#getCiteProc(), and a side effect of
citeproc-js locale initialization could cause a recursive call back to
QuickCopy.init(), which would again call Style#getCiteProc() (after it
had checked its cache but before it had added the new engine to it),
leading to duplicate CSL.Engine initialization. Now we correctly check
whether we've already been initialized, eliminating the race condition.

Separately, remove async keyword from init(). It never actually was
async - even before Bluebird removal, it didn't await anything or return
a promise. And we can't make it await its pre-caching work because
schemaUpdatePromise won't resolve until init() resolves/returns.
2025-10-08 12:04:21 -04:00
Abe Jellinek
e9dd050229 Item pane header: Only pre-cache CSL.Engine if set to bibEntry mode 2025-10-08 12:04:21 -04:00
Tom Najdek
5cb537274e
Fix a couple of issues with file renaming (#5562) (#5566)
* Fixed incorrect new name generation for attachments without a file extension and not present on disk
* Fixed invalid preview in certain edge cases
* Added tests covering file renaming in general and the above issues
2025-10-08 00:39:54 -04:00
abaevbog
1f04c55765
Relate converted embedded note to its attachment (#5559)
When an embedded attachment note is converted to
a standalone note, relate the standalone note to
the attachment and place it in the same collections
as the attachment.
2025-10-07 13:54:09 -04:00
abaevbog
e3926c657a
fix "Next" button not appearing in merge dialog (#5563)
On windows and linux.
Fixes: #5561
2025-10-07 12:48:22 -04:00
abaevbog
59742ccc8f
no find-as-you-type on 0 keypress in itemTree (#5564) 2025-10-06 22:32:13 -04:00
abaevbog
fefad8a9e3
Fix item sometimes not added to collection on cross-library drag-drop (#5547)
When drag-dropping items into a collection in another library,
perform the addition to collection in the same transaction
as creating a new item in the target library.

When the librariesCollectionsBox refreshes on the `modify` event
when a newly created group item is linked to the selected item,
it re-loads the data of the linked item via item.loadAllData().
This could happen after the item is added to the collection
but before this change is saved. In that case, item._changed.collections
would be cleared, and when the item is saved, there would be
no changes to collections to save.

Fixes: #5539

Also, cleanup leftover unused logic of restoring linked item
from trash on drop that was removed in 2dd16b44d6
2025-10-06 15:30:01 -04:00
Martynas Bagdonas
5a96e7c90f Fix test for Citavi annotation import 2025-10-02 17:17:37 +01:00
Dan Stillman
3089a4a534 Update locales from Transifex 2025-10-02 10:42:58 -04:00
Martynas Bagdonas
b48aed2fed Update reader, note-editor, and pdf-worker submodules 2025-10-01 17:37:48 +01:00
abaevbog
ac36b6d361
Display libraries and collections box for standalone notes and attachments (#5554)
Display Libraries and Collections box in the itemPane
for standalone notes and attachments.

Fixes: #5553
2025-09-29 14:44:18 -04:00
Dan Stillman
dd19cf58cc Hide file renaming banner if "Learn More" is clicked 2025-09-29 14:43:25 -04:00
abaevbog
b21e442c4f
maintain the size of tabs as they are being closed (#5548)
When a tab is closed via clicks on "X" button, record
and lock all tabs' widths. That way, the "X" button
of the tab that follows the one that was just deleted
will land under the mouse cursor, allowing one to
delete many tabs. In addition, preserve the scroll position
of the tab bar. If it's impossible because too many tabs
were deleted, add a spacer after the last tab.

Fixes: #5523
2025-09-29 14:28:30 -04:00
Tom Najdek
42b47c5453
Add missing Fluent strings for the reader 2025-09-29 19:59:37 +02:00
Dan Stillman
e27b58176d Update locales from Transifex and merge new English strings 2025-09-18 11:28:16 +02:00
Tom Najdek
516d230647
Fix "Rename from Parent" not visible after the parent item changes Close #5542 2025-09-15 22:56:52 +02:00
Northword
91a74ed063
Fix JSDoc return types of PluginAPIBase._unregisterByPluginID 2025-09-15 14:02:46 +02:00
windingwind
913d15632b Fix menu API async unregister and event listener 2025-09-15 10:07:46 +02:00
Dan Stillman
9ab9ccf79d Update English strings for Transifex 2025-09-13 18:27:45 -04:00
Dan Stillman
0d072f2945 Add "[one]" to localized string to make Transifex happy
Even though we would never use it, Transifex still wants both 'one' and
'other' for English
2025-09-13 18:27:45 -04:00
Dan Stillman
0125de3d06 Don't include "Settings…" link in file-renaming announcement banner 2025-09-13 02:31:41 -04:00
Dan Stillman
e3ab296a72 Updated Liquid Glass icon that fixes glitch in dark/tinted/clear modes 2025-09-13 02:29:17 -04:00
Dan Stillman
ceb32b3d8a Fix max-width on hardConfirmationDialog and reduce input font size 2025-09-13 01:31:12 -04:00
Tom Najdek
e3fc1e62a0 Display the number of items that would be deleted when replacing the online library (#5359)
And require checkbox or text challenge depending on the severity of the
action

---------

Co-authored-by: Dan Stillman <dstillman@zotero.org>
2025-09-13 01:31:10 -04:00
abaevbog
562fed6e7e
fx140: a11y fix voiceover not reading out menulist labels (#5540)
Post-fx140, menulist labels in chrome windows are not
announced by VoiceOver. Setting the menulist's role to 'combobox'
addresses this and gets VoiceOver to announce the labels again.
This is interesting because 'combobox' is supposedly
menulist's implicit role, so it's not clear why this helps.

It does slightly change the way VoiceOver announces
menulists: it announces "combobox" instead of "popup button",
but semantically combobox is still appropriate per
https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only

Fixes: #5424
2025-09-13 00:57:51 -04:00
Tom Najdek
e896fd0137
Add continuous-renaming functionality for attachment files (#3860)
Resolves #1685
2025-09-11 04:54:03 -04:00
Abe Jellinek
17bcb1e84c
Move New Group and New Feed directly under File menu (#5531) 2025-09-11 04:46:40 -04:00
Dan Stillman
c67e35c5ae Add Liquid Glass icon for macOS Tahoe
And preserve icon for previous macOS versions using
https://github.com/vslavik/TahoeIconsTests
2025-09-11 04:42:01 -04:00
Dan Stillman
eaf3f0c5df Use temporary download URL for Firefox 140.3.0esr candidate build
So we can address #5424 before the ESR is released on 9/16
2025-09-10 23:32:31 -04:00
Dan Stillman
cd8ae9308d Update Mac build to Firefox 140.3.0esr
Fixes #5424
2025-09-10 23:32:12 -04:00
abaevbog
8f5471d96b
fix zoteroSearch missing attachment conditions (#5538)
Fix regression after 4d803fe2cd
which caused the operator for condition to never get set.

Fixes: #5537
2025-09-09 22:19:28 -04:00
Bogdan Abaev
b3223dd94b Prefs: Small redesign of Cite → Style Manager table (#5518)
- Remove the delete button below the style table
- Make selected rows stand out less. They will only have an outline,
  like focused rows. This should remove the impression that this is the
  table where one selects the style for citations.
- Add minus icon buttons to each row to delete the style without having
  to select the row. The icon of the selected row is focusable, so it
  can be tabbed onto.
- improve labels and appearance of buttons to add a new style
- move "Reset Styles…" to "Cite" section and rename to "Restore Default
  Styles…"
2025-09-05 02:04:52 -04:00
Bogdan Abaev
5a7444f424 virt table: don't propagate pageUp/Down on macOS (#5518)
Same as on all other platforms. That way, if the parent
container is scrollable, it will not scroll.
2025-09-05 02:04:40 -04:00
Bogdan Abaev
015769a9f8 fx140: allow pointer events on cells in virt table (#5518)
Revert change from 8e2790e2d2.
Post fx140, dragstart fires fine on rows that are not
yet selected. This allows us to remove the workaround that
limited pointer events to the rows of virtualized table (vs its cells).
With it, we no longer need most of the manual handling
of the tooltip in virtualized table - just adding title attribute is enough.
We also don't need to add extra mouse events handling on
clickable button columns as in citationDialog - we can use
proper :hover and :activate effects, as well as attach
click handler directly to the button.
2025-09-05 02:04:15 -04:00
Abe Jellinek
41055ce0a1 Scaffold: Rename file when label changes 2025-09-04 10:09:50 -07:00
abaevbog
4ae1ea1daf
citation dlg: Don't remove bubbles on Backspace on Windows/Linux (#5526)
On non-macOS platforms, Delete (not Backspace) on a table row
will remove all of this item's bubbles from the citation.
This is to be consistent with similar delete operation
handling in other tables.

On macOS, this is still done via Backspace as before.

Per https://github.com/zotero/zotero/pull/5518#discussion_r2310293170
2025-09-04 03:50:50 -04:00
abaevbog
25a881255e
citation dlg: fix non-english locators wrong order (#5527)
Sort the locators alphabetically by their label
after constructing the popup.

https://forums.zotero.org/discussion/comment/497495/#Comment_497495
2025-09-04 03:48:47 -04:00
Abe Jellinek
afc02141ce
Replace CSL validator with reproducible WASM build (#5525) 2025-08-29 00:40:41 -04:00
Tom Najdek
cd82ab7c59
Enable support for Unicode features in file renaming by default (#5521) 2025-08-28 11:21:47 -04:00
Dan Stillman
86b9d28451 File sync: Use filtered filename when renaming from multi-file ZIPs
Follow-up to bab0237727
2025-08-28 03:50:08 -04:00
Dan Stillman
b4dc00eca1 Remove debug line in Zotero.File.rename() 2025-08-28 03:10:49 -04:00
Dan Stillman
b857283ed6 Fix skipItemUpdate logic in Item#relinkAttachmentFile()
We were skipping updating of `clientDateModified` in order to prevent an
attachment item upload, but that hasn't been used since Zotero 4. This
might mean that, in some rare situations (e.g., a filename that needed to
be shortened due to filesystem path length?), people in read-only or
non-files-editable groups may have ended up triggering access errors and
needing to reset local data after a file download.

Now, we properly skip marking the attachment item as unsynced.
2025-08-28 03:10:48 -04:00
Dan Stillman
f5010ce764 Don't update file mod time to force sync upload when renaming
We've always done this, but it's no longer necessary:

- If a file exists locally when a remote filename change comes in, we
  now rename the local file.
- If a file doesn't exist locally and it's a single file, we save the
  file with the new filename on the next file download.
- If a file doesn't exist locally and it's an old multi-file snapshot,
  we now try to find the primary HTML file and rename it.
2025-08-28 03:10:46 -04:00
Dan Stillman
bab0237727 File sync: Rename single HTML file within multi-file ZIP to known name
If a file was renamed remotely and a new copy wasn't uploaded for some
reason, the ZIP wouldn't contain the new filename. We already renamed a
single file within the ZIP to match the new filename, but now we also
rename a single HTML file in old multi-file snapshot ZIPs. If there are
multiple HTML files for some reason (old-style ZIP with iframes?), we
let the user fix it.

And then we can stop reuploading files after renames.
2025-08-28 03:06:52 -04:00
Dan Stillman
33bd1327a0 Remove unnecessary file stat in Item#attachmentModificationTime 2025-08-28 03:06:52 -04:00
Dan Stillman
11d17c5803 Rename file on remote filename change during sync
Previously, the local file wasn't renamed, so it would become unlinked.
Since we currently force reuploading/reregistering of files when they're
renamed, opening the attachment would then redownload the modified
remote file, but there's no need for the file to become unlinked in the
first place.
2025-08-28 03:06:52 -04:00
Abe Jellinek
7abc35bc02
Notes context pane: Create context menus locally (#5508) 2025-08-27 00:41:21 -04:00
Abe Jellinek
e4e6b4189c
Keep Zotero in the User-Agent, add Firefox (#5500) 2025-08-27 00:40:08 -04:00
abaevbog
31e2e95452
itembox: restore unsaved input value after refresh (#5517)
If itemBox refresh happens when an input is focused and
has some unsaved changes, restore the value of the input
after the refresh.

Potential solution for:
https://forums.zotero.org/discussion/126273/bug-data-that-is-input-is-not-saved-and-needs-to-be-entered-again
2025-08-26 23:43:02 -04:00
Abe Jellinek
23567b0395
ESMify feed processing code (#5520) 2025-08-26 23:42:00 -04:00
Abe Jellinek
5c95c35743 Style installation: Don't prompt on validation error in silent mode 2025-08-26 00:44:14 -04:00
Abe Jellinek
08e4d6b06b Stop using restoreProcessorState() (#5516)
restoreProcessorState() breaks locale-specific punctuation (and is
deprecated).

rebuildProcessorState() is enough if we're passing an empty citation
list; if we're reinitializing with a new non-empty list, call
updateItems() first.

Also:

- Add tests for this issue and for potential regressions in disambiguation handling
2025-08-26 00:44:14 -04:00
Abe Jellinek
1a3ea508fe Preferences: Add getScope() function 2025-08-25 14:22:29 -04:00
Adomas Venčkauskas
b1dbe35102 Fix note type picker appearing for Google Docs
Report https://forums.zotero.org/discussion/126026/endnotes-option-showing-in-google-docs
2025-08-25 12:12:07 +03:00
windingwind
4d803fe2cd customElements: Lint 2025-08-23 12:15:40 +02:00
windingwind
7f1c59fde8 Fix attachmentBox reindexButton undefined 2025-08-23 11:54:35 +02:00
Dan Stillman
5c6400e21b Skip unnecessary SQL query if no attachments in Item#getBestAttachments()
This probably shouldn't be SQL at all, but while it is, skip it if not
necessary.
2025-08-22 03:38:02 -04:00
Dan Stillman
2216729721 Clarify variable name in Item#_updateAttachmentStates() 2025-08-22 03:38:02 -04:00
Abe Jellinek
ea3f14f195 HiddenBrowser: Lint 2025-08-21 12:22:25 -04:00
Abe Jellinek
ee42f44254 HiddenBrowser: Fix proxy setting all properties on browser
Missing return.
2025-08-21 12:19:46 -04:00
Abe Jellinek
74c8226323 HiddenBrowser: Remove leak timer
Always logged a warning, even though it doesn't seem like memory is
actually being leaked here.
2025-08-21 12:19:29 -04:00
Abe Jellinek
ba4949e822 Fix nukeSandbox error when closing settings
Fixes #5509
2025-08-21 11:35:41 -04:00
Bogdan Abaev
098be27375 citation dlg: fix row cutoff on linux after resize
When resizing the window in list mode to match the height
of the content, account for the difference between
outerHeight and innerHeight of the window on Linux.
outerHeight > innerHeight (perhaps Linux includes chrome,
borders, etc. in its outerHeight), so we have to add their difference
to the desired height for resizing. Otherwise, the visible
dialog ends up receiving only some of the desired height and
the last row is cut off.

Also, ensure that in tests, we wait for searching triggered by
switching dialog mode to finish before running the next search.

Fixes: zotero#5502
2025-08-21 11:50:10 +03:00
abaevbog
2f262c943c
Additional fixes to form element appearance on Linux (#5503)
Followup to 06ef9ab

Fix <html> based form elements, in citation dialog item popover.
Set slightly smaller default border width of buttons.
2025-08-21 11:48:53 +03:00
Abe Jellinek
4f1589132c
Add Relate Items to context menu (#5499) 2025-08-20 02:15:06 -04:00
windingwind
7f9418fe98
Implement custom menu API (#5028) 2025-08-20 02:10:35 -04:00
Abe Jellinek
8eb803292e Clear pane.persist in tests (#5495) 2025-08-20 02:00:31 -04:00
Abe Jellinek
d854058be6 Don't reopen collection/item pane on restart (#5495) 2025-08-20 02:00:31 -04:00
Abe Jellinek
d35bdd333b
Fix errors when multi-selection contains collections in trash (#5497)
By using getSelectedItems() instead of getSelectedObjects(). The code
below all assumes that `items` contains items, and we handle general
(item or collection) options above, so this seems more correct.

And fix duplicate var.
2025-08-20 01:23:47 -04:00
Dan Stillman
bdf49b9b00 Fix slow purging of creators
Same as e532e435f but for creators
2025-08-19 03:29:05 -04:00
Adomas Venčkauskas
72cc386207 Update LibreOffice submodule 2025-08-19 10:07:53 +03:00
Abe Jellinek
3a61e782ad Scaffold: Fix modern JS APIs missing from code completion 2025-08-18 13:17:03 -04:00
Dan Stillman
dfc869bd2d Update LibreOffice submodule 2025-08-16 11:08:31 -04:00
Dan Stillman
8df8182f01 Update LibreOffice submodule 2025-08-15 02:59:05 -04:00
Dan Stillman
bf59a1e2f8 Revert "Fix relations to erased items reappearing after sync (#5486)"
I think this might cause problems and needs to be tested further.

This reverts commit 07066fd8c1.
2025-08-15 02:09:18 -04:00
abaevbog
07066fd8c1
Fix relations to erased items reappearing after sync (#5486)
Fix ghost relations to erased items reappearing after
sync. The dataserver ensures that related relations are
bidirectional. If a related item is erased, syncing
would first upload the update to the remaining related item
(where the relation is removed), followed by the deletions.
While handling update upload, the dataserver would return
the old, should-be-removed, "dc:relation" entry for that item
because it does not know that the other item is erased.

To handle this, swap the order of operations during sync:
upload deletion first and then upload updates after.
Then, when the dataserver handles the update of the non-erased item,
there is no related item to enforce bidirectional relations to.

Fixes: #5481
2025-08-15 02:00:43 -04:00
abaevbog
8b0f8d7c20
avoid relation to self after merging related items (#5488)
When copying over relations during merge of items
that are related to one another, avoid relating the
chosen main item to itself.

Fixes: #5487
2025-08-15 01:40:01 -04:00
windingwind
20f738ba0d
Remove timeline related code (#5484) 2025-08-15 01:30:42 -04:00
Abe Jellinek
86f9dd7400
Handle invalid path when launching/revealing on macOS (#5485) 2025-08-15 01:29:05 -04:00
Abe Jellinek
d69a1a65ee Fix intermittent context pane toggle test failure
Hopefully.
2025-08-14 17:52:28 -04:00
Abe Jellinek
a993ac74f1
Ensure read-only fields don't save on blur (#5468)
Fixes tabbing out of Scaffold's abstract box causing the test item to be
added to the user's library.

And use same format as itemPaneHeader.js in abstractBox.js.
2025-08-13 23:59:26 -04:00
abaevbog
f3227275b1
filter out trashed items in relatedBox (#5482)
If the item itself is trashed, all related items are displayed
(trashed or not).

Fixes: #1780
2025-08-13 23:56:40 -04:00
Martynas Bagdonas
71c2ffb3ee Update reader submodule 2025-08-12 14:17:25 +03:00
Matthias Kurz
9330db9ff8
Update Copyright year to 2025 (#5473) 2025-08-12 00:47:26 -04:00
Matthias Kurz
9f14f8424f
Set Gecko MinVersion to 140 (#5474) 2025-08-12 00:47:16 -04:00
Abe Jellinek
3e17928547 Fix hiddenDOMWindow comment 2025-08-08 09:54:07 -06:00
Dan Stillman
96dd7b16ac Better method of unsetting and restoring Mozilla environment variables
Hopefully a better fix for #4981, which wasn't working properly on at
least some Linux systems because the variables were getting restored
before the subprocess launched. This delays a (debounced) second, to
give the subprocess time to start, and then automatically restores the
variables.

An explicit restart via app code also immediately restores the
variables, since restarts on Linux inherent the environment and don't
use our startup script. (An upgrade restart could still happen when the
variables were cleared, but you'd have to be extremely unlucky --
launching URLs or files while also performing a manual restart the same
second.)
2025-08-08 02:57:47 -04:00
Adomas Ven
06ef9ab657
Fix linux form element styling (#5467)
Closes #5435
2025-08-07 02:16:16 -04:00
Dan Stillman
24a249353c "Caching" → "Cached" in CSL.Engine cache debug line to clarify timing 2025-08-07 01:11:01 -04:00
Dan Stillman
970675a2ac Update citeproc.js to include Juris-M/citeproc-js#256 2025-08-07 01:10:10 -04:00
Dan Stillman
c72a8a1424 Update citeproc.js to latest build
Includes Juris-M/citeproc-js#253 and Juris-M/citeproc-js#254
2025-08-07 00:59:48 -04:00
Abe Jellinek
d514786239 Clear _blockingObserver field when it was never initialized
Follow-up to cc3fc42b0a
2025-08-05 11:18:19 -04:00
Abe Jellinek
cc3fc42b0a Catch BlockingObserver error when reader is closed immediately
https://forums.zotero.org/discussion/125841/cannot-open-a-pdf-file-in-a-new-tab
2025-08-05 11:17:03 -04:00
Adomas Venčkauskas
2c7475a5e6 Fix Google Docs integration breaking when cancelling edit bibliograpy
I've run into this randomly. It only occurs if the first operation on a
Google Doc since Zotero restart is the edit bibliography dialog which
you cancel. Due to how the HTTP integration client is implemented, the
missing await causes it to try to send another response to the Connector
(which is no longer waiting on the /response endpoint), failing and
causing a forever pending promise, which makes subsequent attempts to
interact with the Google Docs plugin no-op.

We get occassional reports from users about Google Docs getting stuck
that is fixed by Zotero restart, so hopefully this will reduce those.
2025-08-05 09:55:09 +03:00
Martynas Bagdonas
e644df74fe Remove tab characters from FTL files to avoid parsing issues
Fixes #5447
2025-08-04 15:02:51 +03:00
Tom Najdek
8355a95407
Warn users running non-ARM version on ARM CPU on Linux (#5461) 2025-08-02 09:59:48 -04:00
Abe Jellinek
f8a1a2c6ed
Don't merge into a missing snapshot attachment (#5459) 2025-08-02 01:53:47 -04:00
abaevbog
487677958e
fix subcollection not being copied to library root (#5464)
Fixes: #5463
2025-08-02 01:50:54 -04:00
Abe Jellinek
68ffe9583c Clean up JSDoc comments and unused field (#5443) 2025-08-02 01:49:39 -04:00
Abe Jellinek
fd6c52ede4 Find Full Text: Simulate ProQuest redirect back to original page (#5443)
We don't follow JS redirects, so this is necessary in order for
us to get to a translatable page after authentication.
2025-08-02 01:49:39 -04:00
Abe Jellinek
91732419b6 Attach CookieSandbox to Find Full Text requests and translation (#5443)
With a 24-hour validity per site (eTLD+1).
2025-08-02 01:49:39 -04:00
abaevbog
2a60011414
fx140: fix cutoff progressWindow on linux (#5462)
Resize the window to fit content on load after the
header is rendered. If the window is resized on
DOMContentLoaded, nothing is rendered yet and the
window will look cutoff on Linux.

Addresses #5453
2025-08-02 01:41:39 -04:00
abaevbog
873beebc0f
close more leftover windows in tests (#5460)
- close hyphenationTest window.
- properly close zoteroPane window in preferences_syncTest.
after() hook inside of the test would not close the window until the
very end of the entire test run. Without this, during
subsequent tests that open their own zoteroPane window, there
are two instances of zoteroPane open at the same time.
- close open tabs in readerTest cleanup, since subsequent
tests expect library tab to be selected.

Followup to https://github.com/zotero/zotero/pull/5444#issuecomment-3141879892
2025-08-02 01:40:21 -04:00
Abe Jellinek
74926fcc2e Fix menubar menu not opening after submenu item is selected
On Windows and (I'm assuming) Linux. Regression in c3f8ba8.

Fixes #5456
2025-08-01 12:12:36 -04:00
windingwind
37ff8c9143
Improve linux popup appearance (#5452)
* Fix tabs menu panel padding

* Fix tabs menu panel background

* Fix panel style selector

resolve: #5434
2025-08-01 11:02:14 -04:00
windingwind
1dbfb1a998
Improve linux window appearance (#5451)
resolve: #5449
2025-08-01 10:59:38 -04:00
Dan Stillman
2bff6541f2 Update note-editor and reader submodules 2025-07-31 22:50:21 -04:00
abaevbog
b1e163b7f5
fx140: Use architecture-specific build folder for tests on Linux (#5445) 2025-07-31 22:48:21 -04:00
Dan Stillman
87d27f9c51
Close ProgressQueueDialog after connector server tests (#5446)
Since some connector operations trigger recognition

This will hopefully fix the tags box test failures.
2025-07-31 21:54:15 -04:00
windingwind
773ca6f724
Improve item pane test stability (#5436) 2025-07-31 21:51:40 -04:00
abaevbog
69fdf0b145
fx140: fix collection tree toolbar color on Linux (#5440)
Override default toolbar color set in https://searchfox.org/mozilla-esr140/source/toolkit/themes/linux/global/toolbar.css#12

Fixes #5439
2025-07-31 13:35:14 -04:00
Dan Stillman
19a848484a Don't split tags at em dashes on paste in item tags box
https://github.com/zotero/zotero/pull/5366#discussion_r2245628458
2025-07-31 11:25:57 -04:00
Dan Stillman
5063a8b396 isAbove → isAtLeast in sync engine tests for testing delays
Maybe related to Bluebird?
2025-07-31 10:45:19 -04:00
Tom Najdek
efadc21cdc
Update a call to a deprecated function in Mendeley import 2025-07-31 16:04:06 +02:00
Tom Najdek
a5cdd8d5aa
Fix context menu closes on every third right-click. Resolve #5427 2025-07-31 13:03:01 +02:00
Dan Stillman
682722bc29 Update LibreOffice and Word for Mac submodules 2025-07-30 22:31:12 -04:00
abaevbog
95e4a34476 fx140: fix separators on linux (#5432)
- fix transparent gap between tag selector and separator
- remove double border below separator of context pane in
stacked mode

Fixes: #5430
2025-07-30 22:31:12 -04:00
Dan Stillman
7f684ce130 Remove unused argument (previously to Zotero.Promise.map()) 2025-07-30 22:31:12 -04:00
Dan Stillman
87971248c5 Replace Bluebird's .spread() in integration code 2025-07-30 22:31:11 -04:00
Dan Stillman
18ff5c30f0 Update LibreOffice and Word for Mac submodules for fx140 2025-07-30 22:31:11 -04:00
Dan Stillman
922af23c63 fx140: Fix Mendeley Importer import 2025-07-30 22:31:11 -04:00
Dan Stillman
57fd2162d1 fx140: Fix NetUtil import 2025-07-30 22:31:11 -04:00
windingwind
08e2be9875 fx140: fix item pane test further
Should call updateLayout after changing layout
Should wait for notify after saving object
2025-07-30 22:31:11 -04:00
windingwind
0ce54e2b14 Fix item pane test failure
fix: #5421
2025-07-30 22:31:11 -04:00
abaevbog
747a3f7a9f fx140: remove sliding animation from panels (#5423) 2025-07-30 22:31:10 -04:00
Abe Jellinek
5af15f6496 fx140: Bring tabbox tabs back to the middle
And nudge them down a bit further.
2025-07-30 22:31:10 -04:00
Dan Stillman
4a5689e9b6 Restore rejection of last feed item when FeedReader terminates
Without this, the "should reject if the provided url is not a valid
feed" test fails

Follow-up to #5412
2025-07-30 22:31:10 -04:00
Abe Jellinek
c1270e635e Overhaul testing in Scaffold (#5301) 2025-07-30 22:31:10 -04:00
abaevbog
2e135d035f Fx140: do not use bluebird methods in citation dialog (#5416)
Instead, decide if the search should be skipped after
the delay based on the timestamps.

Fixes: #5414
2025-07-30 22:31:10 -04:00
Dan Stillman
0a33eeb0c0 Update translate submodule 2025-07-30 22:31:10 -04:00
Dan Stillman
4c51883b97 Restore Zotero.Promise.method() on ItemGetter#setAll()
Required for noWait translation
2025-07-30 22:31:09 -04:00
Dan Stillman
d797c7a4d8 BluebirdShimPromise: Add value() and fix isResolved() semantics
- value() is used in noWait export translation

- isResolved() technically means fulfilled or rejected -- unclear if we
  ever depended on that, but it might as well be correct
2025-07-30 22:31:09 -04:00
abaevbog
dd5667c09b fx140: update X icon for search cancel button (#5417)
searchfield-cancel.svg no longer exists, use close-12.svg
that is the same one used by search-textbox
2025-07-30 22:31:09 -04:00
windingwind
d1c6ee33be Fix plugin API test failure
Fix: #5411
2025-07-30 22:31:09 -04:00
Abe Jellinek
a53e474f9c Make filterStack() a no-op 2025-07-30 22:31:09 -04:00
Dan Stillman
e7805914f4 ConcurrentCaller: Increment numRunning in the correct place 2025-07-30 22:31:09 -04:00
Dan Stillman
0f560f4e4c ConcurrentCaller: Fix return value in comment after Bluebird removal 2025-07-30 22:31:09 -04:00
Dan Stillman
5c172e3be9 ConcurrentCaller: Reject queued promises when stopping queue
Previously, we simply cleared the queue, leaving any pending promises
unresolved, and we checked for isResolved()/isPending() in
storageEngine.js (and maybe other places). storageEngine.js was changed
to simply await those promises, but that results in a hang if the queue
is stopped, since the promises were never resolved. Instead, when
stopping the queue, reject queued promises with a CanceledException and
ignore those promises in storageEngine.js.
2025-07-30 22:31:08 -04:00
Abe Jellinek
0a62553746 Remove remaining chai-as-promised method calls 2025-07-30 22:31:08 -04:00
Abe Jellinek
9442b7ee6c Fix Zotero.Feed tests after chai-as-promised removal 2025-07-30 22:31:08 -04:00
Abe Jellinek
7e706354f9 Don't try to reject last feed item when FeedReader terminates
As the comment says, this hasn't been useful (and isn't used anywhere),
and I honestly don't understand how the test ever passed - process()
would've resolved all the feed item promises already.

Fixes #5412
2025-07-30 22:31:08 -04:00
Abe Jellinek
c8aff336b3 Migrate Bluebird methods in Zotero.Utilities.Internal.saveDocument()
Fixes #5415
2025-07-30 22:31:08 -04:00
Dan Stillman
26ffaf0ad3 Update translate module
Fixes test failure in fx140
2025-07-30 22:31:08 -04:00
Dan Stillman
f81763b173 Remove Chai as Promised
A test using `assert.eventually` was failing after the Bluebird removal
but worked with just `await`, and since there hasn't really been much
point to Chai as Promised since the introduction of `async`/`await` ages
ago, just remove the library instead of figuring out why.
2025-07-30 22:31:08 -04:00
Dan Stillman
26b6c70b96 Fix Libraries test after Bluebird removal 2025-07-30 22:31:07 -04:00
Dan Stillman
6809aa50ad Fix item tree tests after Bluebird removal
Remove uses of PromiseInspection.value()
2025-07-30 22:31:07 -04:00
Dan Stillman
dc80fb2d50 fx140: nsIStringInputStream#setData() → setByteStringData() 2025-07-30 22:31:07 -04:00
Dan Stillman
1500fb9fa3 ConcurrentCaller: Revert to Promise.delay()
We're shimming it, and we override it with a stub in a syncAPIClient
test.
2025-07-30 22:31:07 -04:00
Dan Stillman
0bf4fc54c4 Remove uses of Promise.isFulfilled() in connector server tests 2025-07-30 22:31:07 -04:00
Dan Stillman
c2b6a9cc12 fx140: Fix NetUtil import in server tests 2025-07-30 22:31:03 -04:00
Dan Stillman
c4cfee1807 fx140: Fix additional mendeleyImport.mjs imports 2025-07-30 22:30:58 -04:00
Dan Stillman
b7c2002b63 Fix another Bluebird removal bug
Use of `Promise.map()`
2025-07-30 22:30:58 -04:00
Dan Stillman
e07ac6243c Fix bug from Bluebird removal
Bluebird had a `Promise.filter` method

Fixes failing data directory tests
2025-07-30 22:30:58 -04:00
Abe Jellinek
9f038891ef Set name for Zotero.DBConnection.TimeoutError
Fixes failing timeout test in dbTest.js.
2025-07-30 22:30:57 -04:00
Abe Jellinek
97dbcc6870 Restore timeout in dataObjectTest.js, fix test
OK, the timeout removed in 05e8da wasn't actually useless, but this test
was.

41706a changed erase() so it no longer forwards skipNotifier when
deleting child items. That means that we *would* get a notify() call for
the deleted child, so the test should have been failing.

However! Because of a bug in Bluebird's filtered catch() implementation,
Promise.TimeoutError wasn't recognized as an Error subclass, and it was
instead used as a predicate function, which would always pass.

So all errors were caught as "timeouts" and suppressed, making this test
always pass. Now it no longer always passes (but does pass with the
current behavior).
2025-07-30 22:30:57 -04:00
Abe Jellinek
6203b9712d Fully asyncify feedTest.js 2025-07-30 22:30:57 -04:00
Abe Jellinek
b87bdeea41 Update asyncify script to handle more Mocha hooks 2025-07-30 22:30:57 -04:00
Abe Jellinek
20e75c3251 Remove Bluebird methods in concurrentCallerTest.js
This is really messy and not ideal, but these tests were written for
Bluebird Promise state inspection, and it's difficult to make them look
nice without that being available.
2025-07-30 22:30:57 -04:00
Abe Jellinek
2bc0afa830 Remove useless timeout() in dataObjectTest.js 2025-07-30 22:30:57 -04:00
Abe Jellinek
d9341bbf1a Use alternate method to test feed check delay 2025-07-30 22:30:56 -04:00
Abe Jellinek
2b7535cd83 Fix test runner
By restoring ZOTERO_TEST=1 in runtests.sh after 711b336.
2025-07-30 22:30:56 -04:00
Tom Najdek
7e1f4b8dbf fx140: ESMify errorReport.xhtml 2025-07-30 22:30:56 -04:00
Abe Jellinek
52b4fc0b9d Preferences: Fix sandbox destructor 2025-07-30 22:30:56 -04:00
windingwind
519c4cb518 fx140: Fix scaffold missing menubar 2025-07-30 22:30:56 -04:00
windingwind
db29ed6501 fx140: Fix Linux window control button position 2025-07-30 22:30:56 -04:00
windingwind
331ed31b1c fx140: Fix Linux titlebar appearance 2025-07-30 22:30:56 -04:00
windingwind
2562871424 fx140: Fix menu icon overlap
Fix after #5343
2025-07-30 22:30:55 -04:00
Tom Najdek
4eae5bc658 Fix incorrect MendeleyImporter import path after ESMify 2025-07-30 22:30:55 -04:00
Abe Jellinek
f4a5ee0c7e fx140: ESMify about.xhtml 2025-07-30 22:30:55 -04:00
Abe Jellinek
e4878052aa fx140: Add draft macOS native-like textarea style
Firefox doesn't use any native textareas anymore, so I guess they just
removed/broke native textarea rendering.
2025-07-30 22:30:55 -04:00
Dan Stillman
072b2e3285 Force Firefox refetch when switching to older branch 2025-07-30 22:30:55 -04:00
Dan Stillman
ac32c0fd8b Add Firefox build script for Linux 2025-07-30 22:30:55 -04:00
Dan Stillman
233cfb80ea Update Word for Windows submodule for fx140 2025-07-30 22:30:55 -04:00
Abe Jellinek
964b5a0e88 fx140: Fix remaining bare ChromeUtils.import() calls 2025-07-30 22:30:54 -04:00
Abe Jellinek
1dd60c948b Fix export translation
Restore Zotero.Promise.method() wrapper on getCodeForTranslator()
because the translation framework needs it to return a synchronously
resolved promise, unfortunately.
2025-07-30 22:30:54 -04:00
Dan Stillman
04e03478a9 Update version to 8.0 2025-07-30 22:30:54 -04:00
Dan Stillman
b50d87a6f4 Fix stderr output in build script 2025-07-30 22:30:54 -04:00
Dan Stillman
12354bd23b Include Linux ARM64 in build_and_deploy 2025-07-30 22:30:54 -04:00
Dan Stillman
69963dce2c Re-enable packaging of the updater on Linux 2025-07-30 22:30:54 -04:00
Dan Stillman
38cfc233cf Fix various bugs in the build scripts on Linux
After the arch-specific-builds overhaul
2025-07-30 22:30:54 -04:00
Dan Stillman
f31dde4a1b Take path to Firefox source on command line for Windows Firefox builds 2025-07-30 22:30:53 -04:00
Dan Stillman
93edf4e099 Use SHA256 for custom Firefox components on macOS
(Not currently necessary)
2025-07-30 22:30:53 -04:00
Dan Stillman
44bba85ce8 Use custom xul.dll on Windows
Necessary for non-Mozilla C:\ProgramData folder now that we can no
longer do a string replace on the official DLL

The custom component ZIPs are currently stored in S3 and referenced by
SHA-256 hashes, which get committed. This is basically the same as Git
LFS, but since we now support per-arch fetch_xulrunner and building,
this avoids the need to download large xul.dll files for other
architectures. (Git LFS files also can't ever be removed, so
particularly while we're testing this, we don't necessarily want to
permanently store every copy.)
2025-07-30 22:30:53 -04:00
Dan Stillman
3e0d2c06f1 Support arch-specific builds on Windows and Linux
`fetch_xulrunner`, `dir_build`, and `build.sh` now take an optional `-a`
argument on Windows and Linux to specify the architecture to
fetch/build. If `-a` is omitted for `dir_build` (as in `build_and_run`),
it defaults to the current architecture.

xulrunner hashes are now computed for each architecture. This changes
some filenames in xulrunner/, so you may want to clean out some old
folders/files.

This removes the custom handling for CI, since it will now default to
the architecture of the test runner.
2025-07-30 22:30:53 -04:00
Abe Jellinek
67d2e1cead fx140: Asyncify/ESMify tests
They seem to be succeeding when run individually, but failing when
run as a whole. Not sure why yet.
2025-07-30 22:30:53 -04:00
Abe Jellinek
d3419ed124 fx140: Update asyncify script to handle it() calls 2025-07-30 22:30:53 -04:00
Abe Jellinek
6fc1726da7 fx140: Fix folderImport import in fileInterface.js 2025-07-30 22:30:53 -04:00
Dan Stillman
ae83e01923 Update Mozilla patch for Firefox 140 for Windows
With ROOT_UPDATE_DIR_NAME change
2025-07-30 22:30:52 -04:00
Zotero
581157ca75 Set RUST_TARGET for Windows build 2025-07-30 22:30:52 -04:00
Dan Stillman
363383363d Disable some services for the Windows build
Unclear if these affect the launcher or xul.dll, but no need to build
them either way
2025-07-30 22:30:52 -04:00
Adomas Venčkauskas
19903271ce Use ESMified word processor plugin installers 2025-07-30 22:30:52 -04:00
Adomas Venčkauskas
601a1f2292 fx140: ESMify word-processor-plugin-installer 2025-07-30 22:30:52 -04:00
Adomas Venčkauskas
35919f640c fx140: Remove isFulfilled and isResolved uses in integration 2025-07-30 22:30:52 -04:00
Dan Stillman
94aa126107 Fix signing of Mac builds with fx140
There's now an UpdateSettings.framework in the updater that needs to be
signed before the updater itself can be signed.
2025-07-30 22:30:52 -04:00
Dan Stillman
4657156c91 Always sign macOS test builds
Since they don't start after fx140, for reasons I didn't investigate
2025-07-30 22:30:51 -04:00
Dan Stillman
e1a595d78d Update macOS SDK for fx140 build in mozconfig 2025-07-30 22:30:51 -04:00
Dan Stillman
21aaa1c33c Update Linux updater to Firefox 140.0esr
Including ARM64 version
2025-07-30 22:30:51 -04:00
Dan Stillman
63741ca9bc Add script to extract Linux updaters from custom Firefox builds 2025-07-30 22:30:51 -04:00
Dan Stillman
48e9bda610 Update Linux mozconfig for fx140 2025-07-30 22:30:51 -04:00
Abe Jellinek
6b2331b647 Remove special handling for Promise.coroutine() in server.js 2025-07-30 22:30:51 -04:00
Abe Jellinek
ae0413ce80 Asyncify standalone.js 2025-07-30 22:30:50 -04:00
Abe Jellinek
499ce2709c Asyncify errorReport.xhtml 2025-07-30 22:30:50 -04:00
Abe Jellinek
0937d47721 Asyncify feedSettings.js 2025-07-30 22:30:50 -04:00
Abe Jellinek
f9541b3281 Asyncify advancedSearch.js 2025-07-30 22:30:50 -04:00
Abe Jellinek
0970415c3a fx140: De-uglify tabbox on macOS
This is definitely not pixel-perfect to how it was before, but it's a
lot closer.
2025-07-30 22:30:50 -04:00
Abe Jellinek
d71a0db4a2 fx140: Preferences: Fix "Update now" label being appended, not replaced 2025-07-30 22:30:50 -04:00
Abe Jellinek
303b3a9a89 Preferences: Fix sandbox creation for subpanes 2025-07-30 22:30:50 -04:00
Abe Jellinek
e2bcf90856 Migrate Zotero.Promise#timeout() in db.js 2025-07-30 22:30:49 -04:00
Abe Jellinek
8fb3e6d1a9 Migrate Zotero.Promise methods in storageEngine.js 2025-07-30 22:30:49 -04:00
windingwind
8ff5d9e3e1 fx140: Fix tabs menu panel style 2025-07-30 22:30:49 -04:00
Abe Jellinek
6231b6dd5a migrate-fx140: Fix error message 2025-07-30 22:30:39 -04:00
windingwind
cb449989b8 fx140: Fix menupopup style 2025-07-30 22:30:38 -04:00
Abe Jellinek
1c4d2b84e0 Migrate Zotero.Promise#return() in libraries.js 2025-07-30 22:30:38 -04:00
Abe Jellinek
64a2237fb3 fx140: Don't rely on hiddenDOMWindow in plugins.js 2025-07-30 22:30:38 -04:00
Abe Jellinek
b9e560d33e fx140: Remove EventUtils
Unused, relied on hiddenDOMWindow.
2025-07-30 22:30:38 -04:00
Abe Jellinek
0f2690eb75 fx140: Migrate hiddenDOMWindow in style.js
XSLTProcessor is available in the global scope now.
2025-07-30 22:30:38 -04:00
Abe Jellinek
0c1e341f50 fx140: Remove unused method that used hiddenDOMWindow 2025-07-30 22:30:38 -04:00
Abe Jellinek
3e2f6a8783 fx140: Update feedReader.js hiddenDOMWindow comment 2025-07-30 22:30:37 -04:00
Abe Jellinek
e52b1993f4 fx140: Work around grid layout bug in item box
And don't use hiddenDOMWindow, since it's no longer available on all
platforms.
2025-07-30 22:30:37 -04:00
Abe Jellinek
4ecac12cc6 fx140: Update type definitions 2025-07-30 22:30:37 -04:00
Abe Jellinek
31edc728d4 fx140: Update type definition update script
Use ZIP export to more easily get all subdirectories.
2025-07-30 22:30:37 -04:00
Abe Jellinek
6cbb0bc103 Migrate Zotero.Promise#isPending() in feedReader.js 2025-07-30 22:30:37 -04:00
Abe Jellinek
ed20f8b4d1 Migrate Zotero.Promise#isPending() in syncRunner.js 2025-07-30 22:30:37 -04:00
Abe Jellinek
ed00016caa Migrate Zotero.Promise#isPending() in dataObjects.js 2025-07-30 22:30:37 -04:00
Abe Jellinek
c4a0a760b1 Migrate Zotero.Promise#isResolved() in citationDialog.js 2025-07-30 22:30:36 -04:00
Abe Jellinek
e81f5d96f1 fx140: Fix Mendeley import
'else' branch is untested.
2025-07-30 22:30:36 -04:00
Abe Jellinek
e6aba9b879 fx140: Fix folder import 2025-07-30 22:30:36 -04:00
Abe Jellinek
fbf0177215 fx140: Fix Dictionary Manager 2025-07-30 22:30:36 -04:00
Abe Jellinek
ee859e374a fx140: Fix -debugger flag 2025-07-30 22:30:36 -04:00
Abe Jellinek
be0cc2c602 fx140: Fix ChromeUtils.defineESModuleGetters(this, ...) in MJS files
`this` is undefined - use globalThis.
2025-07-30 22:30:36 -04:00
Abe Jellinek
7b7c153711 Migrate Zotero.Promise#cancel() in zoteroPane.js 2025-07-30 22:30:36 -04:00
Abe Jellinek
90e92e995f Migrate Zotero.Promise.each() in syncLocal.js 2025-07-30 22:30:35 -04:00
Abe Jellinek
9a44834289 Migrate Zotero.Promise.filter() in profile.js 2025-07-30 22:30:35 -04:00
Abe Jellinek
68255d075e Migrate Zotero.Promise.map() in syncEngine.js 2025-07-30 22:30:35 -04:00
Abe Jellinek
70f75fd3b0 Migrate Zotero.Promise#cancel() in feeds.js 2025-07-30 22:30:35 -04:00
Abe Jellinek
122b160639 Migrate Zotero.Promise.map() in quickCopy.js 2025-07-30 22:30:35 -04:00
Abe Jellinek
18500b19d0 Migrate Zotero.Promise.each() in quickCopy.js 2025-07-30 22:30:35 -04:00
Abe Jellinek
676be3fc00 Update JSDoc for forEachChunkAsync() 2025-07-30 22:30:35 -04:00
Abe Jellinek
da6efecaef Migrate Zotero.Promise.map() in syncFullTextEngine.js
Fixes syncing, I think.
2025-07-30 22:30:34 -04:00
Abe Jellinek
14afba4b62 Import ConcurrentCaller in storageEngine.js 2025-07-30 22:30:34 -04:00
Abe Jellinek
ffb68f2f6c Make Library#_initSave() async to match superclass
Fixes .catch() error
2025-07-30 22:30:34 -04:00
Abe Jellinek
ce0ee4b693 Migrate Promise#return() in file.js 2025-07-30 22:30:34 -04:00
Abe Jellinek
9cad92b817 fx140: Fix ZoteroProtocolHandler URI parsing
nsIURI#pathQueryRef no longer includes the authority component. A URI
that would've had pathQueryRef '//authority/path' before will now have
pathQueryRef '/path'.

Reader seems to be functional after this change.
2025-07-30 22:30:34 -04:00
Abe Jellinek
2a077ab131 Remove Zotero.spawn()
This method relied on Bluebird and can be replaced by just awaiting an
async function.
2025-07-30 22:30:34 -04:00
Abe Jellinek
76232fda21 Replace use of Zotero.spawn() in ZoteroProtocolHandler 2025-07-30 22:30:34 -04:00
Abe Jellinek
2b0255435e Replace use of Zotero.spawn() in http.js 2025-07-30 22:30:33 -04:00
windingwind
6222737d1a fx140: Fix titlebar and window control icons 2025-07-30 22:30:33 -04:00
Abe Jellinek
488bcc484f Replace uses of Zotero.Promise.defer() as a constructor 2025-07-30 22:30:33 -04:00
Abe Jellinek
bb8a8a4362 fx140: Use import statements in MJS files 2025-07-30 22:30:33 -04:00
Abe Jellinek
c434ec40dd fx140: Remove unused bare imports 2025-07-30 22:30:33 -04:00
Abe Jellinek
c8dd6095c8 fx140: Fix remaining bare importESModule() calls 2025-07-30 22:30:33 -04:00
Dan Stillman
08879d81d6 Update Windows updater to 140.0esr
This allows updates from Firefox builds past 115.23esr/128.10esr/138.0,
which change the command line arguments passed to the updater.

https://bugzilla.mozilla.org/show_bug.cgi?id=1923376
2025-07-30 22:30:33 -04:00
Dan Stillman
587e4227d7 Update Windows build to Firefox 140.0esr 2025-07-30 22:30:32 -04:00
Dan Stillman
f6f920350d Update Mac updater to 140.0esr
This allows updates from Firefox builds past 115.23esr/128.10esr/138.0,
which change the command line arguments passed to the updater.

https://bugzilla.mozilla.org/show_bug.cgi?id=1923376
2025-07-30 22:30:32 -04:00
Dan Stillman
cbefe513c2 Update Mac build to 140.0esr 2025-07-30 22:30:32 -04:00
Dan Stillman
001a6cc259 Update Rust version for fx140 builds 2025-07-30 22:30:32 -04:00
Dan Stillman
c634273fe1 Update build scripts with new default Firefox source folder
After the git transition
2025-07-30 22:30:32 -04:00
Dan Stillman
0a80484919 Fix win32 argument to Windows build script 2025-07-30 22:30:32 -04:00
Abe Jellinek
9d6669eb1e fx140: Add ChromeUtils.import() shim for plugins
We can decide if we want to keep this, but it's an easy change that
seems useful for dev builds.
2025-07-30 22:30:32 -04:00
Abe Jellinek
f0ac59f3dc Add functional shim for Zotero.Promise.method()
For translation.
2025-07-30 22:30:31 -04:00
Abe Jellinek
60983dadd5 fx140: Fix other bare imports in file.js 2025-07-30 22:30:31 -04:00
Abe Jellinek
1197683ab3 fx140: Fix other bare imports in utilities_internal.js 2025-07-30 22:30:31 -04:00
Abe Jellinek
4f8a1bc968 fx140: Fix ctypes imports in utilities_internal.js 2025-07-30 22:30:31 -04:00
Abe Jellinek
a9df0424e5 fx140: Fix InlineSpellChecker import in editorInstance.js 2025-07-30 22:30:31 -04:00
Abe Jellinek
6053091563 fx140: ESMify EPUB and uses 2025-07-30 22:30:31 -04:00
Abe Jellinek
95038a7da5 fx140: ESMify RemoteTranslate and uses 2025-07-30 22:30:30 -04:00
Abe Jellinek
ff72087da1 migrate-fx140: Use .mjs for Zotero modules everywhere 2025-07-30 22:30:30 -04:00
Abe Jellinek
4b7ac315b5 migrate-fx140: Assume Zotero ESMS are/will be renamed to .mjs 2025-07-30 22:30:30 -04:00
Abe Jellinek
33d97f585e Remove duplicate require.js from windows that import include.js 2025-07-30 22:30:30 -04:00
Abe Jellinek
27bd0708b6 fx140: Use ZOTERO_CONFIG as module in standalone.js and debugViewer.js 2025-07-30 22:30:30 -04:00
Abe Jellinek
c1f018eae8 fx140: ESMify actors 2025-07-30 22:30:30 -04:00
Abe Jellinek
c607b8f455 delayGenerator(): Use native Promise#then() 2025-07-30 22:30:29 -04:00
Abe Jellinek
496e067d11 fx140: Streamer: Don't use hiddenDOMWindow
WebSocket is available globally.
2025-07-30 22:30:29 -04:00
Abe Jellinek
c16b74ec5b fx140: ESMify/asyncify preference panes 2025-07-30 22:30:29 -04:00
Abe Jellinek
32f812d515 Load each preference pane in its own scope
To eliminate conflicts between top-level const/let declarations.
2025-07-30 22:30:29 -04:00
Abe Jellinek
ab5758c0b6 fx140: Restore menubar background on Linux
Not sure if these are the right colors or if this is the right place to
make this change.
2025-07-30 22:30:29 -04:00
Abe Jellinek
9be57b3110 Update ConcurrentCaller to use native promises 2025-07-30 22:30:29 -04:00
Abe Jellinek
06dc00af24 Migrate Promise#filter() in collectionTree.js 2025-07-30 22:30:29 -04:00
Abe Jellinek
4a1ca8ea96 Migrate Promise#isPending() in zotero.js 2025-07-30 22:30:28 -04:00
Abe Jellinek
37a6e0a61f Migrate Promise#delay() in schema.js 2025-07-30 22:30:28 -04:00
Abe Jellinek
c9a5a66020 fx140: Don't use hiddenDOMWindow for editable-text sizing
Causes an error on Linux.
2025-07-30 22:30:28 -04:00
Abe Jellinek
a61f7035ad fx140: Use ZOTERO_CONFIG as module in itemTree.jsx 2025-07-30 22:30:28 -04:00
Abe Jellinek
75a2c325ec Fix error on shutdown 2025-07-30 22:29:58 -04:00
Abe Jellinek
eeb94aea48 fx140: Fix more broken code 2025-07-30 22:18:36 -04:00
Abe Jellinek
a080b7c9c6 fx140: Give require()'d modules a window context 2025-07-30 22:18:36 -04:00
Abe Jellinek
f8826a9e8a fx140: Restore support for synchronous XHR in system code 2025-07-30 22:18:36 -04:00
Abe Jellinek
ece2b59700 fx140: More progress toward startup 2025-07-30 22:18:36 -04:00
Abe Jellinek
845b5a9006 migrate-fx140: Use native Promise.try(), fix Components.utils.import() 2025-07-30 22:18:36 -04:00
Abe Jellinek
c506e41072 fx140: Add working require.js, more progress toward startup 2025-07-30 22:18:36 -04:00
Abe Jellinek
14f4881d04 migrate-fx140: Assume all Firefox JSMs are ESMified 2025-07-30 22:18:35 -04:00
Abe Jellinek
741940dd9a migrate-fx140: Run jscodeshift on all JS files
This is probably why it wasn't doing much.
2025-07-30 22:18:35 -04:00
Abe Jellinek
e94ed24e20 Add Promise.defer() to Bluebird shim 2025-07-30 22:18:35 -04:00
Abe Jellinek
05d19fc6aa Replace Bluebird with async in xpcom/ 2025-07-30 22:18:35 -04:00
Abe Jellinek
8d0923f119 fx140: Remove Bluebird
All module code runs in strict mode, and that breaks Bluebird.

And add a partial shim for the simplest and most useful methods. Not
sure if we actually want that, or if we should just replace it entirely
with native methods.
2025-07-30 22:18:35 -04:00
Abe Jellinek
ee512680ac fx140: Rename esmify to migrate-fx140, add asyncify migration
See README.txt for run instructions.
2025-07-30 22:18:35 -04:00
Abe Jellinek
cd9f90da8d fx140: Add esmify script
This seems to produce decent output on many of our files, but it totally
misses some import statements. Not sure why that is yet.
2025-07-30 22:18:34 -04:00
Dan Stillman
163ed14f92 Add removed-files_linux-arm64 2025-07-30 22:18:34 -04:00
Dan Stillman
e6cd5f322f TEMP: Disable Linux updater
Since we haven't yet build an ARM64 updater
2025-07-30 22:18:34 -04:00
Dan Stillman
0f7590ad13 fx140: Comment out some lines that were failing
I didn't check to see which of these have just moved.
2025-07-30 22:18:34 -04:00
Dan Stillman
e715593eb7 fx140: browser-custom-element.js → browser-custom-element.mjs 2025-07-30 22:18:34 -04:00
Dan Stillman
fd4dde300d fx140: Remove obsolete AppConstants modifications 2025-07-30 22:18:29 -04:00
Dan Stillman
832575b7c0 Update build scripts for Linux ARM64
This doesn't include an ARM updater, so those lines in build.sh need to
be commented out for the build to succeed.

Addresses #3515
2025-07-30 22:15:31 -04:00
Tom Najdek
f5907ed3b2
Expose tag split option in item tags box (#5366) 2025-07-30 22:13:19 -04:00
Abe Jellinek
78534ff2d2 FileHandlers: Infer PDF pageIndex from annotation key
And:

- Update createAnnotation() test support function to create valid EPUB
  and snapshot annotations
- Add more FileHandlers tests
2025-07-30 11:37:52 -04:00
abaevbog
c3f8ba8eb1
Fix glitchy menupopups in dialogs opened from a context menu (#5409)
Opening a dialog window via a command from a menupopup leads to
unexpected glitches in the dialog's popups (e.g., collections menu
in newCollection dialog on macOS won't go away when you click away,
and on Windows, child collections don't appear when a parent is hovered).
This is due to some kind of race condition that arises when the dialog
appears when the popup is still closing.

This workaround captures command events from menupopups, closes the popup,
stops propagation of those events, and then dispatches a copy of
that event after a delay. This gives the popup enough time to close.

Fixes: #5408
Fixes: #4891
2025-07-30 01:56:11 -04:00
Abe Jellinek
735922a2bf
If custom file handler is set to Zotero itself, use reader (#5407) 2025-07-24 12:07:27 -04:00
Abe Jellinek
27affb9f14
Cache CSL Engine for item pane header (#5405)
Once styles are initialized.
2025-07-24 12:06:49 -04:00
Adomas Venčkauskas
11014be449 Server: Add support for UTF in server headers (RFC2047) 2025-07-24 13:26:23 +03:00
Dan Stillman
b31b71304d ZFS: Don't retry download for canceled redirect with mtime/hash match
This was a regression from the switch to `Zotero.HTTP.download()`. 302
wasn't a success code, so `HTTP.download()` would throw, and since the
status wasn't set correctly on the `XMLHttpRequest` within
`HTTP.UnexpectedStatusException`, it would think it was an interrupted
S3 connection and trigger another download after a delay.
2025-07-24 01:31:25 -04:00
Dan Stillman
a370ec8979 HTTP.download(): Don't save file for non-2xx response 2025-07-24 01:28:13 -04:00
Dan Stillman
cc0b1ad0c9 HTTP.request(): Return proper status for canceled redirect 2025-07-24 01:28:12 -04:00
Abe Jellinek
8041d7d4df Complain when Notifier/Prefs observer leaks window 2025-07-23 12:03:53 -04:00
Abe Jellinek
1eb248553a Unregister Zotero_Tabs observers when window is unloaded 2025-07-23 12:03:53 -04:00
Abe Jellinek
5e1541c5ee Extract activateZoteroPane(), try different activeness check
This should hopefully fix intermittent CI failures.
2025-07-23 11:23:36 -04:00
Abe Jellinek
d4a5fbbefe
Cache CiteProc Engine instances, pre-cache for Quick Copy (#5399) 2025-07-23 10:47:23 -04:00
Tom Najdek
cc691abeee
Add missing icons in context menus on Windows/Linux
* Add missing icon for "My Library" in the "Copy To" menu in cases where there are no other libraries in that menu
* Add icon to the "New Collection..." option in the item context menu
2025-07-23 12:56:06 +02:00
Dan Stillman
9b1950c9ea Clear HTTP.mock at end of ZFS tests 2025-07-22 22:12:52 -04:00
Dan Stillman
d1ea8a397b Use BrowserDownload for 403 when enforcing type in downloadFile()
Fixed regression (probably in 1f401f0897) causing Find Full Text not to
use BrowserDownload for a 403

https://forums.zotero.org/discussion/125581/zotero-suddenly-fails-to-retrieve-full-text-although-i-have-access
2025-07-22 22:12:00 -04:00
Tom Najdek
9698927d22
Fix unreadable tooltips in dark mode. Close #5397 2025-07-22 17:19:28 +02:00
Northword
e2a96e512a
Clear plugin cache on re-install (#5395) 2025-07-21 23:55:02 -04:00
Tom Najdek
2d4a086819
Fix picking a type for the creator removes the entry. Close #5396 2025-07-21 13:03:41 +02:00
SSCC
162fb4d51b
fix the missing "yield" in line 158 of storageEngine.js (#5394)
Fix the issue where Zotero.Sync.Runner.sync({background: true}) cannot obtain the itemIDs
2025-07-20 01:07:29 -04:00
windingwind
ce6c27ae7e
Fix tabs menu filtering out empty title (#5392) 2025-07-18 00:46:20 -04:00
Tom Najdek
9c82fed152
Add missing icons to the collections context menu (#5386)
Co-authored-by: yexingsha <yexingsha@gmail.com>
2025-07-16 12:18:58 +02:00
Tom Najdek
0e3b695a85
Use a more universal method of checking the element name 2025-07-15 19:25:41 +02:00
Tom Najdek
c945afb2e1
Fix auto-completion typo toLocaleLowerCase -> toLowerCase 2025-07-15 19:18:30 +02:00
Tom Najdek
248ddf05d2
Fix incorrect context menu when renaming collection #5374 2025-07-15 17:38:49 +02:00
Martynas Bagdonas
057c798202 Ignore reader and note-editor error when Fluent file is missing 2025-07-15 16:00:12 +03:00
Dan Stillman
59730c0fca Move "Show My Publications" localized string from .dtd to .ftl
Added to .dtd by mistake in #5367
2025-07-15 02:53:57 -04:00
Dan Stillman
dd10111e5d Close all tabs before item tags box tests to try to fix CI failures 2025-07-15 01:01:54 -04:00
Dan Stillman
42e21d4fa1 Disable citeproc-rs tests, which are failing after Chicago style update
Note sure why
2025-07-15 00:46:10 -04:00
Dan Stillman
01683b6ccb More chicago-note-bibliography → chicago-shortened-notes-bibliography 2025-07-15 00:46:10 -04:00
Dan Stillman
845361bdbc Update default Chicago style after upstream id changes 2025-07-15 00:46:10 -04:00
Dan Stillman
7a4aef1b32 Update styles and CSL locales
Including new Chicago styles
2025-07-15 00:46:09 -04:00
Tom Najdek
a85e1fb40a
Add ability to hide My Publications (#5367) 2025-07-15 00:31:22 -04:00
abaevbog
387d5966c3
fix trashed collection treated as an item row (#5371)
If the id of the collection happens to be the same
as an id of an existing item, those objects may end
up being used interchangeably in itemTree.
The trashed collection could appear in "My Library"
instead of the item, or the not-deleted item could appear
in the trash instead of the collection.
This is addressed by using .treeViewID instead of the
.id of the objects in ItemTree.refresh and ZoteroPane.itemSelected.
2025-07-15 00:30:28 -04:00
Tom Najdek
98d7d8c408
Re-implement "Create Parent Dialog" without using React (#5380)
* Fixed an issue where the window size did not match the content
* Replaced the semi-transparent progress bar with a spinner
* Fixed a problem that allowed triggering a search while one was already in progress
* Reduced code complexity
2025-07-15 00:29:54 -04:00
Abe Jellinek
3b8e8bea33 Register CookieSandbox observer at startup (#5357) 2025-07-14 14:00:43 -04:00
Abe Jellinek
aff57c2254 Fix all cookies being lost during remote translation (#5357) 2025-07-14 14:00:38 -04:00
Tom Najdek
b8e1b994e0
Add a missing icon for "Change Parent Item" #5373
Co-authored-by: yexingsha <yexingsha@gmail.com>
2025-07-14 17:07:53 +02:00
Tom Najdek
c32e050c6e Unify styles for headers of similar custom elements 2025-07-10 23:40:22 +02:00
Abe Jellinek
f9b3694dd1 Style Editor: Fix splitter
By removing flex from the preview box. The splitter doesn't work if the
elements on both sides have flexes.

Fixes #5361
2025-07-10 11:00:31 -04:00
Abe Jellinek
d9a45726d4 Fix read-only title box on non-file attachment
Regression in e625c0009e

https://forums.zotero.org/discussion/125214/attachment-title-of-web-links-cannot-be-modified
2025-07-07 13:24:46 -04:00
Tom Najdek
7beed24756
Fix truncated content in the Create Parent dialog. Close #5365 2025-07-01 15:00:33 +02:00
Martynas Bagdonas
114bd3c495 Increase reader initialization wait time to reduce test failures in CI 2025-06-30 22:29:08 +03:00
Martynas Bagdonas
bdcc5c551a Reader: Only show text context menu when interacting with a text box 2025-06-30 14:47:23 +03:00
Martynas Bagdonas
4808ebd2e9 Update reader submodule 2025-06-30 13:32:20 +03:00
Tom Najdek
c19032a004
Add missing tooltips for tab scroll buttons. Fix #5352 2025-06-30 12:14:16 +02:00
Martynas Bagdonas
9ba1580c3b Update note-editor submodule with Fluent support 2025-06-26 18:36:47 +03:00
Martynas Bagdonas
80d01c9d02 Add note-editor strings to FTL files 2025-06-26 17:46:52 +03:00
Martynas Bagdonas
355c61e6d8 Update reader submodule and support overriding FTL with current locale 2025-06-25 16:43:36 +03:00
abaevbog
4dbe0f7842
Citation dialog: fix broken item details popup with some non-english locales (#5354)
This allows the locator dropdown to be as wide as it wants
(depending on the longest option in the current locale),
with inputs occupying the remaining space. This is
a more flexible layout that avoids broken UI in
cases if <select> dropdown tries to become too wide
while <input>s occupy a fixed space to remain aligned.

Fixes: #5350
2025-06-25 02:17:41 -04:00
Martynas Bagdonas
774173f04d Update reader submodule and provide FTL data 2025-06-24 21:48:55 +03:00
Martynas Bagdonas
37f8c4d4f4 Fix strings for annotation deletion prompt
Fixes https://forums.zotero.org/discussion/124901/confirm-message-for-deleting-multiple-annotations-in-the-reader
2025-06-24 21:02:30 +03:00
windingwind
6eb8a80e12
Fix reader sidenav buttons wrongly disabled (#5348)
fix: #5332
2025-06-20 23:32:30 -04:00
Martynas Bagdonas
0b5012555d Fix regression triggering multiple reader context menus
Fixes https://forums.zotero.org/discussion/124939/persistent-context-menu-in-the-snapshot-reader
Fixes https://forums.zotero.org/discussion/124938/double-context-menus-on-annotations-in-the-pdf-reader
2025-06-19 21:11:03 +03:00
Adomas Venčkauskas
0acfcd3f99 Update LibreOffice submodule 2025-06-19 11:39:51 +03:00
windingwind
50cafe57a2
Fix linux menu checkmark icon overlap (#5343)
Fix linux menu checkmark icon overlap

fix: #5340
2025-06-19 10:57:53 +03:00
Tom Najdek
57bc913304
Fix tag swatches regressions. Resolve #5322
Fixes missing tag swatches borders, a regression due to a change of the
default values for the `focus-states` mixin in #4961 (61b62d0).

Also introduces opaque tag colors so that tag swatches are not
semi-transparent.
2025-06-17 15:09:55 +02:00
Bogdan Abaev
e6ce021b5f return tags in /connector/getSelectedCollection
Together with all collections and libraries.

To account for tags that may contain "," characters,
expect connector to send tags as an array. If tags
are sent as a string, it will be split by "," for
compatibility with older connector versions.

Addresses: #1706
2025-06-16 09:34:33 +03:00
Dan Stillman
408f1274f4 Select library tab before item tags box tests 2025-06-12 09:45:27 -04:00
Bogdan Abaev
f50e8b0b35 do not open hidden Zotero window on connector save
And fix regression where selecting a collection
in connector would always switch to the library tab.
It was originally added in https://github.com/zotero/zotero/pull/5020
but was lost after https://github.com/zotero/zotero/pull/5148.
2025-06-12 08:33:22 +03:00
Dan Stillman
1c469ce1a0 Remove duplicate general-back string from 3f84856e1 2025-06-11 22:59:33 -04:00
Dan Stillman
9dbac93dc9 Remove lines inadvertently included in ee35361
I meant to merge the test without those lines, since I fixed the problem
from #5297 properly in ded8dca66.

Fixes #5337
2025-06-11 21:57:59 -04:00
abaevbog
96d3f312b3
fix web link attachment row not selected on create (#5338)
Small followup to tweak 5721744386
2025-06-11 21:50:03 -04:00
Martynas Bagdonas
195629186b Update reader submodule and complete migration to Fluent localization 2025-06-11 22:48:49 +03:00
Martynas Bagdonas
3f84856e1e Update strings for reader localization with Fluent 2025-06-11 22:29:22 +03:00
abaevbog
c493a17b13
Connector server support to adding a note (#5064)
* support adding a note from connector

- pass canUserAddNote flag in ping response to tell
the connector that it should display a note field
- when the connector sends a note, create a child note item
if it does not yet exist and set its content. If the
child item for user-added notes is already created,
update it
- if the connector sends an empty note, user-added child
note item is deleted
- record child notes added by the user in _userAddedNotes
cache to distinguish between user added notes and
notes added by the connector automatically
- if the target library changes, child note items are
moved to the new library as well. _userAddedNotes
will be updated to reflect new itemIDs
- if there are multiple toplevel items being added,
the note is added to all of them

Also fix breakage when switching between collections
from different libraries. Fixes: #5331
2025-06-11 14:50:48 +03:00
Tom Najdek
e50787c94f
File renaming: Add pascal case. Fix unicode handling. (#4876) 2025-06-11 01:03:34 -04:00
Dan Stillman
15269763bb Don't use Firefox user agent for files.zotero.net requests 2025-06-11 00:47:28 -04:00
Dan Stillman
b05ddaf32b Fix escaping in rpl command 2025-06-11 00:46:04 -04:00
Dan Stillman
4f7f9ae75b Update reader submodule 2025-06-11 00:46:04 -04:00
Bogdan Abaev
65bc56b07b dont exclude image annotations when creating notes
from multiple selected items.
Regression from 48fd23ccec
2025-06-10 22:22:33 +03:00
Dan Stillman
7a74384b42 Update locales from Transifex and merge new English strings 2025-06-10 11:06:42 -04:00
Adomas Venčkauskas
27648bd73a Update LibreOffice and Word for Windows submodule 2025-06-09 12:23:42 +03:00
Dan Stillman
28ffdd4c10 Update CSL locales 2025-06-08 00:32:49 -04:00
Abe Jellinek
777d6f951f
Undo Scholaread changes to user.js (#5313)
And any other fileHandler changes
2025-06-08 00:30:10 -04:00
Martynas Bagdonas
8cf61fed9e Add spell checking options to reader annotation text box context menu
Fixes #5308, #2838, #2109
2025-06-06 18:40:40 +03:00
abaevbog
6ff349d65d
Select child attachment after a file is drag-dropped onto a top-level item (#5328)
And also handle attachments added via context menu

Fixes: zotero#5320
2025-06-05 02:20:13 -04:00
windingwind
51a62022e9
Improve tabs menu keyboard handle (#5319)
Use space to close tab if the close button is active
Focus next close button after closing tab with keyboard
2025-06-05 02:11:53 -04:00
Dan Stillman
0b7340a651 Update reader submodule 2025-06-05 01:58:58 -04:00
Abe Jellinek
110d04f5f7 Add localization for Focus Mode string 2025-06-04 12:01:06 -04:00
Tom Najdek
ee35361608 Test for subsequent updates providing stale changed object data
From #5297
2025-05-31 02:20:32 -04:00
Dan Stillman
ded8dca668 Properly clear previousData on save for primary data and item fields
Closes #5297
2025-05-31 02:18:07 -04:00
Dan Stillman
05a2539b81 Don't corrupt DataObject#_relations while saving
The predicate name used as an object key was being converted to an id,
so if relations weren't reloaded properly, they would be broken after a
save.

Discovered while working on #5297
2025-05-31 02:16:05 -04:00
abaevbog
1f8e05bfd8
Fix new annotation changing the scroll position in itemTree (#5309)
Extend scrollToRow in windowed-list.js to allow to
scroll the specified row to the top of the window
even if it is located below the current scroll window.
With this, when scroll position is restored in itemTree,
the same row remains at the top.

Fixes: #5233
2025-05-30 01:31:11 -04:00
windingwind
cdcac8a6e1
Fix sidenav drop indicator position (#5304)
regression after #5263
2025-05-30 00:59:08 -04:00
Abe Jellinek
769b4c8c34
Fixes for tags-box regressions (#4951)
* Show "Remove All Tags..." when right-clicking textbox

* Fix pasting multiple tags adding concatenated tag

* Remove unnecessary check

* Fix tag count changing when unsaved tag is removed
2025-05-30 00:56:56 -04:00
windingwind
3b151a0b18
Fix inconsistent collapsed sidenav state (#5311)
fix: #5276
2025-05-30 00:51:10 -04:00
Adomas Venčkauskas
9d04392eb0 Add a read-only library param to /connector/getSelectedCollection
Part of zotero/zotero-connectors#546
2025-05-26 13:41:07 +03:00
Abe Jellinek
3ff13bc08d Info box: Fix second right-click opening section menu
On Windows, after right-clicking on a title field.

this.querySelector('popupset') matched the popupset in the
collapsible-section head, not our own.

Fixes #5294
2025-05-21 12:12:31 -04:00
abaevbog
46f80ebdfb
Citation Dialog: more optimization of initial loading (#5296)
* citation dialog: interactive before io.getFields

- added io.allCitedDataLoadedPromise, which is resolved
when both io.fieldIndexPromise and io.citationsByItemIDPromise
are resolved. Resolved io.allCitedDataLoadedPromise essentially
means that calls to io.sort() and io.getItems()
will be fast because all necessary data is already loaded.
- citation dialog uses io.allCitedDataLoadedPromise to
not await for functions relying on io.sort() and io.getItems()
before the data is loaded, as it could take an arbitrary
amoung of time. Speicifcally, SearchHandler._getCitedItems() and
CitationDataManager.sort. As soon as allCitedDataLoadedPromise
is resolved, cited items will be sorted.
This means that when retrieving fields takes a long time, one can
still add new items, their bubbles will just not immediately
be sorted.
- this replaces earlier SearchHandler.loadCitedItemsPromise, which
was a special case of this handling.
- added a new test ensuring that bubbles can be added even
when io.allCitedDataLoadedPromise is not resolved yet
- cleanup for buildCitation function to remove handling
of io.citation.sortedItems, which is always empty on
load before io.sort() runs
- added a few Zotero.debug statements for future debugging
- Uses a dummy promise if no allCitedDataLoadedPromise 
(e.g. to accomodate the note editor)
2025-05-21 09:55:41 +03:00
Bogdan Abaev
dfc31d9961 citation dlg: data structure refactor, bug fixes
- replace objects with zoteroItem, citationItem, etc.
as the main representation of an entry in the citation
(CitationDataManager.items) with instances of a new BubbleItem class.
BubbleItem has two static methods to create
a new instance: BubbleItem.fromCitationItem() used
to create entries from io.citation.citationItem on load,
and BubbleItem.fromItem used to create entries from
Zotero.Item when a new item is being added into the citation.
BubbleItem.getCitationItem will return a citationItem
in a format that citeproc consumes. This streamlines
the flow of data, makes it easier to manage and avoids
unnecessary conversions between citationItem and Zotero.Item.
- This refactoring fixes the issue of added suffix/prefix
sticking to an item and persisting even if the bubble
is removed and re-added. Fixes: #5282
- add csl data to virtual item in io.customGetItem
to remain consistent with Zotero.Integration.Citation.loadItemData.
That way, csl data is always found on Zotero.Item
and we don't have to check citationItems.

Also:
- apply deduplication to cited items, so that a cited
item from the library that appears in selected or
opened section does not have a duplicate entry in
the cited section.
- added initial test suite for citation dialog
2025-05-19 09:24:01 +03:00
windingwind
086b0c3d84
Allow plugin to uninstall itself in update manifest (#5292)
Use `uninstall: true` in `update_url` JSON `updates` that is valid
for the installed plugin to uninstall the plugin the next time it
checks for updates
2025-05-16 06:10:45 -04:00
windingwind
e18bf2165a
Fix locate button for annotation item (#5298) 2025-05-16 06:05:02 -04:00
Tom Najdek
b0506aafe1
Unhide "title" column on "Restore Column Order" (#5299)
This actually checks for a "primary" column, so if an extension hides the "title" column but provides its own "primary" column, the "title" column remains hidden.
2025-05-15 07:48:25 -04:00
Dan Stillman
2bc0c4861c Fix changed object data not being passed in notifier extraData
Which I apparently broke a decade ago in afe0412c5

And add support for 'deleted' and 'tags', which now use a different
mechanism
2025-05-12 23:36:58 -04:00
Tom Najdek
ed24cce4a1
Fix custom trash header persisting after items are restored or deleted. Fixes #5279.
This was due to a logic bug in `getCurrentPane()`, which returned the wrong pane, causing the incorrect one to be updated.
2025-05-12 17:28:11 +02:00
abaevbog
1eacc8bbdf
Citation dialog: updated Enter handling in panels (#5281)
Unhandled Enter keypress from inside of a panel will now close it.
Enter on "Omit Author" checkbox or on the inactive locator
label dropdown will close the item details popup.
Enter on "Keep sources sorted" checkbox of settings
popup will close it for consistency.

Fixes: #5277
2025-05-10 00:29:44 -04:00
Abe Jellinek
9ed3f6363c Add test for synchronous Zotero.done() call
No one should do this nowadays, but EM still does.

zotero/translate@c858ce9613
2025-05-09 14:42:06 -04:00
Abe Jellinek
420b84d17e Update translate submodule 2025-05-09 14:25:48 -04:00
Abe Jellinek
51649b0045 Update reader submodule 2025-05-09 10:38:54 -04:00
Abe Jellinek
e54665cf49 ReaderTab constructor: Assign _contextPaneOpen from correct option 2025-05-09 10:38:45 -04:00
Abe Jellinek
1b61be71b9 isPaneCollapsed(): Use correct check
We don't know if collapsibleParent is non-null, but we already know that
its previous sibling is the splitter if it is.
2025-05-09 10:36:26 -04:00
abaevbog
e625c0009e
Fixes to annotation row behavior in trash (#5266)
- right-click on annotation rows in trash will display
  "Delete Permanently" option, instead of an empty menu
- in trash and other non-editable collection rows, disable
  "Add Note from Annotations" button in itemPane header
- remove ability to add tags to annotations and other
  items by dragging them into tag selector from trash
- remove ability to add items to collections by
  dragging them into collection tree from trash
- fix attachment row being removed from trash if
  its child annotation row is erased

Also:
- fix contextPane in reader tab being editable for item in trash
- fix title being editable in attachmentBox for item in trash

Fixes: #5261
2025-05-09 02:05:53 -04:00
Dan Stillman
f539d7439d Fix test failure after #5263 2025-05-09 01:52:26 -04:00
Dan Stillman
c1cea87743 Disable advanced search test
Completely, not just in CI, since it's failing for me locally
2025-05-09 01:41:05 -04:00
Abe Jellinek
3102b6b67a
Always show item/context pane toggle (#5263) 2025-05-09 01:27:20 -04:00
Abe Jellinek
c42e144f56
Make XULElementBase into a mixin to reduce code duplication (#5270) 2025-05-09 01:00:43 -04:00
windingwind
0b339a3a30 Fix attachmentsBox#_handleAddPopupShowing
Fix rebase regression after 0b8b4c0
2025-05-08 22:38:42 +02:00
windingwind
32009ccdb6
Fix annotation item pane title word cutoff (#5271) 2025-05-08 16:36:38 -04:00
Abe Jellinek
143829065f SequoiaContextMenuChild: Remove unused field 2025-05-07 10:19:49 -04:00
Tom Najdek
6eafb89e82
Import Wizard: Fix focus loss on options page
Also resolves an issue preventing the wizard from continuing when pressing the Enter key on this screen.
2025-05-07 13:52:52 +02:00
abaevbog
971c57fedd
Disable itemTree row drop between child rows (#5258)
Fixes: #5257
2025-05-07 02:00:22 -04:00
Dan Stillman
f8a517d286 Force "Show Tabs Menu" menu item to update with shortcut key at startup 2025-05-07 01:52:54 -04:00
windingwind
61b62d0a02 Improve tabs menu (#4961) 2025-05-07 01:52:54 -04:00
Abe Jellinek
c6e5e6c1e6 Revert "Show item pane toggle everywhere"
Mistaken push.

This reverts commit 4b5bee37b1.
2025-05-06 12:15:19 -04:00
Abe Jellinek
4b5bee37b1 Show item pane toggle everywhere 2025-05-06 12:05:31 -04:00
Tom Najdek
55423024ea Revert "Tweak column behaviour on visibility and order changes" (#5254)
This reverts commit 997ba12626.
2025-05-05 23:39:53 -04:00
Tom Najdek
de0907c8f7 Fix issues with flexbox width calculations (#5254)
Issues caused by differing padding values between the header cell and the table body cell
2025-05-05 23:39:53 -04:00
Tom Najdek
af6e4cc44c Adjust column resizing logic to account for extra width in the first column (#5254)
Fixes #5198
2025-05-05 23:39:53 -04:00
abaevbog
54763ff722
improve performance of annotation rows removal (#5259)
Do not update itemTree selection on each annotation row
removal when an attachment row is collapsed.
It causes a significant delay when attachment with
many annotations is collapsed if selection is on any
row below the attachment. It is also redundant because
the selection is updated at the end of _closeContainer anyway.

Fixes: #5255
2025-05-05 23:23:59 -04:00
Tom Najdek
31845457a2
Fix creator type dropmarker color in dark mode 2025-05-04 15:51:28 +02:00
Abe Jellinek
03b33d995e Reorder _openTagsPopup() to clarify logic
And only remove the keydown listener when the top-level popup hides.
2025-05-03 01:38:18 -04:00
Abe Jellinek
68aff7b23e Hide leftover popups when switching tabs (#5251) 2025-05-03 01:38:18 -04:00
abaevbog
22996da722
open annotation in new window on Shift+Enter (#5253)
Per https://forums.zotero.org/discussion/123944/shift-enter-to-open-the-annotations-in-separate-windows-of-the-reader
2025-05-03 01:35:46 -04:00
abaevbog
929bacaac7
Fix unintentional title transfer between attachments on item switch (#5245)
Also added similar test for item details table

Fixes #5244
2025-05-03 01:34:06 -04:00
abaevbog
3fc8e4d02e
Fix leftover annotation rows and disappearing attachment rows after drag-drop (#5248)
- collapse all rows before removing them. It makes
  sure that annotation rows will be cleaned up properly
  when an attachment row is moved into another parent.
- refactor conditionals to handle changes of parent
  regardless of whether the item is a container
  (attachment with annotations) or not (a note).
- added tests for itemTree's handling of changing the
  parent of attachments and notes, as well as ensuring
  there are no leftover annotation rows in Unfiled Items.

Fixes: zotero#5246
2025-05-03 01:30:50 -04:00
Dan Stillman
a7964f7116 Fix duplicate attachment row after retrieving metadata
https://forums.zotero.org/discussion/123939/double-pdf-file-after-doing-a-retrieve-metadata-operation-on-a-standalone-pdf-file

Regression from 48fd23ccec
2025-05-01 02:48:42 -04:00
Adomas Venčkauskas
d9b9046201 Fix Google Docs allowing to select note style (endnotes)
Report: https://forums.zotero.org/discussion/123928/zotero-citations-not-appearing-as-endnotes-despite-selected-setting
2025-04-30 16:47:25 +03:00
Martynas Bagdonas
36d979f71e Update build scripts for note-editor, pdf-worker, and reader:
- Finalize renaming of pdf-reader to reader
- Remove client- prefixes from URL paths
- Update pdf-worker to use the new document-worker path in preparation for repository rename #5052
- Ensure note-editor properly uses the ZIP build instead of silently falling back to a local rebuild
- Log errors to console
2025-04-30 13:46:32 +03:00
Dan Stillman
d7d1d03b8b Update reader submodule 2025-04-30 01:51:15 -04:00
Abe Jellinek
230e3d8065
Skip invalid relative path when changing LABD (#5239) 2025-04-30 01:06:40 -04:00
abaevbog
4521f750a4
ItemTree annotations: miscellaneous fixes (#5232)
- disable context menu on collapsible-sections of annotations in `itemPane`
- fix `arrowRight`/`arrowLeft` being able to expand/collapse annotation sections when they are focused
- set proper aria-label and aria-description on annotation cards
- fix misaligned annotation icons

Fixes: #5231
Fixes: #5234
Fixes: #5240
2025-04-30 00:54:45 -04:00
windingwind
bd9735baf3
Fix v-table selector dataKey escape (#5238)
Regression after 0b8b4c0
Fix https://forums.zotero.org/discussion/123877
DataKey from custom columns may include unexpected chars
2025-04-29 09:32:33 -04:00
Abe Jellinek
54006b7c8c
Make hidden readers inactive, firing visibilitychange (#5225) 2025-04-28 04:16:16 -04:00
Dan Stillman
70af1165a5 Update reader submodule 2025-04-28 04:12:32 -04:00
abaevbog
48fd23ccec
annotations showing in itemTree (#3416)
- Annotations are displayed in itemTree under their file attachments
  on a third level. The annotation spans the entire row.
- The title is constructed on the go. When possible, it
  includes annotation quote and comment as pseudo-columns
  of the row. The comment occupies about twice as much space as
  the quote. Otherwise, (if there is no quote) only the annotation
  comment
  is included as the "title" part of the row
- Non-CJK segments of the quote part of the annotation row are
  italicized. CJK segments are left as is. If CJK segments are present,
  there is more padding between quote and comment parts.
- Search matches the actual attachment instead of its parent file.
- Can create child notes from annotations of the same item or
  standalone notes from annotations across different items from
  the context menu or the header button.
- When an annotation (or multiple annotations) are selected, the
  annotationItemPane component is displayed where annotations are
  grouped by their top-level item. Annotations are displayed fully,
  without having their content cut off.
- Special treatment for annotations to always prompt
  to erase the item regardless of what collectionTree row
  is selected (e.g., if a collection is selected, we
  still want one to be able to delete the annotation).
  This only applies if all selected items are annotations.
  If multiple items are selected, some annotations and
  some not, do nothing. This is until the trash is
  ready. In the future, we may send annotations to trash
- strip all HTML tags from annotation for now, until the logic to
  properly render annotation markup is copied over from the reader
  (applies to both annotation-row component and the annotation
  item rendered in the itemTree)
- Added a generalized "expandToItem" function to itemTree to
  expand all ancestors of a given item, similar to "expandToCollection"
  from collectionTree
- add annotation conditions to advanced search
- show [Image not available] if no annotation file for ink or image
  annotations
- only keep annotation-specific context menu options when some
  annotations are selected in itemTree
- enable Quick Copy of annotations from itemTree via drag-drop,
  shortcut key, or Edit → Copy Annotation
- Minor refactoring of Zotero.Annotation.toJSON() to pull out async code
  that handles ink and image annotations, so that
  Zotero.Annotation.toJSONsync() for highlight, underline, and note
  annotations does not have to be awaited. Since ink and image
  annotation don't seem to work for drag-drop Quick Copy, they are just
  skipped for now.
2025-04-28 04:10:28 -04:00
Dan Stillman
3144314d20 Use parent app CFBundleIdentifier as prefix for Safari extension
Instead of org.zotero.SafariExtensionApp.

I don't know if this will help with spurious signature failures, but it
can't hurt.
2025-04-24 02:15:17 -04:00
windingwind
a63cb647ff
Fix item pane test after #5214 (#5220) 2025-04-23 17:09:24 -04:00
Abe Jellinek
d74db17cf1
Select Items dialog: Fix scrollbars and padding, add title (#5213) 2025-04-22 23:37:38 -04:00
windingwind
97f8d942a0
Keep item pane scroll position after refresh (#5214) 2025-04-22 23:35:30 -04:00
Abe Jellinek
56b4c15de9 Scaffold: Don't warn about \u2028/\u2029 in tests 2025-04-22 15:06:29 -04:00
Abe Jellinek
e80615eab8 Scaffold: Don't show deleted line for base-mapped title field
And remove dead code.
2025-04-22 12:23:31 -04:00
Adomas Venčkauskas
1655265189 Add a Connector version update warning for item saving changes (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
f2a86e4af9 Rename attachments imported from stream (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
cc87ff3af6 Remove old and unused Connector Server endpoints (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
2cd4ba205a Update Connector Server tests (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
69dcbda1a0 Misc test fixes (#5148)
Remove redundant and wrong call to changeCollectionTreeRow() in
advancedSearch which triggers test errors sometimes, and disable prompt
button delay when running tests everywhere, not just CI
2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
9579aa154a Do not return recognizer promises early (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
d6d01f66b9 Update recognizer tests to use sinon stub pattern (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
5b1ed9ac8b Save session library switching with filesEditable support (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
5e18337cc6 Simple attachment saving via Connector (instead of Zotero) (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
d781151b9a Make server.js use mozilla httpd and support application/octet-stream (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
6d206f98bb Remove old Z4-fx connector switching code (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
67b5a0bb19 Use the bundled mozilla httpd (#5148) 2025-04-22 09:47:58 -04:00
Adomas Venčkauskas
db1c542a50 Remove unused connector server code (#5148) 2025-04-22 09:47:58 -04:00
abaevbog
e690b0b18e
Expand collectionTree containers on dragover (#5024)
- when something is dragged over a collapsed collection
or group, it will be expanded to reveal children
so one could drop the dragged item/collection/ into them
- when drag is over, the expanded collections will
collapse, except for the ancestors of the newly selected
collection
- trigger collapse of expanded collections on dragend
on the entire document (in case dragend happens not over
the collection tree), or from onDrop handler, from where
we will wait for selection to change before collapsing
rows

Fixes: #4952
2025-04-22 00:17:56 -04:00
Abe Jellinek
1c089bf448 Fix broken spellchecking when only en-US is installed
Regression in 26221b6786
2025-04-21 10:51:28 -04:00
abaevbog
dc084b6961
Move note-editor popup a11y strings to Fluent (#5212)
Per https://github.com/zotero/zotero/pull/5205#issuecomment-2809055908
2025-04-18 00:14:26 -04:00
Abe Jellinek
a7778b93e8 Tags box: Disable Remove All Tags when not editable 2025-04-16 10:29:40 -04:00
Abe Jellinek
b067e0d940 Scaffold: Add tags to item preview 2025-04-16 10:29:40 -04:00
abaevbog
173660d5c7
add string for note-editor a11y link popup message (#5205)
String read out by screen readers when the link popup
appears instructing users to navigate it.

Addresses: #5199
2025-04-16 02:42:42 -04:00
abaevbog
1459ca210d
new collection dlg: fix menu keyboard nav on win (#5208)
Fixed non-working selection of collections in the
dropdown via keyboard on Windows.

Fixes: #5202
2025-04-16 01:57:13 -04:00
abaevbog
f568363098
a11y: clear aria-expanded on empty itemTree rows (#5201)
Per https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-expanded#treeitems
only treeitems that contain children should have
the aria-expanded attribute. This is instead of having
aria-expanded="false" on empty regular items or standalone
attachments, which erroneously indicates to screen readers
that the row has children but is collapsed.

Fixes: #5200
2025-04-16 01:53:54 -04:00
windingwind
0bff70b186
Fix item pane blank (#5207)
This is caused by the persisted splitter state value to be blank from either older versions of client or plugins.
Resetting the empty values fixes this bug.
2025-04-16 01:53:03 -04:00
Abe Jellinek
246005a097
Accept length-7 sortIndexes for snapshots (#5209) 2025-04-16 01:52:09 -04:00
Abe Jellinek
2db51eb0e5 Scaffold: Don't collapse item preview sections
Fixes sections disappearing when collapsed in the main window's item
pane.
2025-04-15 13:01:01 -05:00
Abe Jellinek
71a4742092 Scaffold: Fix code completion not working until pref changes 2025-04-14 11:21:30 -06:00
Dan Stillman
8c7ab1eb6a Remove unnecessary DB resets in storageLocal tests 2025-04-14 04:22:16 -04:00
Dan Stillman
f616d41546 Mark locally missing files marked for upload for download instead
Z7 removed an `OS.File.open()` in `_checkForUpdatedFiles()` that would
throw on missing files and cause them to be marked for download in a
`catch`.

This likely caused the fix for #1753 not to work in Z7.
2025-04-14 04:22:16 -04:00
abaevbog
060a15fe78
Do not switch to library tab while changing target collection in connector (#5020)
Do not switch to library tab while changing target
collection in connector.

Deprecate inLibraryRoot as an independent boolean
param in ZoteroPane.selectItems to instead have
a composite options param { inLibraryRoot, noTabSwitch }

Fixes: #4262
2025-04-10 07:27:20 -04:00
Tom Najdek
cef26fae09
Tweak column behaviour on visibility and order changes (#5162) 2025-04-10 06:23:09 -04:00
Martynas Bagdonas
1116be4a02 Fix PDF worker not loading CMap files 2025-04-09 18:53:41 +03:00
Dan Stillman
e480c78898 Update locales from Transifex 2025-04-09 02:00:53 -04:00
Dan Stillman
ba3cd9e094 Update builds to 128.9.0esr 2025-04-09 01:57:13 -04:00
abaevbog
3d1cf90934
runJS: better message if nothing is returned (#5181)
Display "===>undefined<=== (completed successfully)" instead
of current "===>undefined<=== (undefined)", which is a bit misleading.

Fixes: #5180
2025-04-09 01:31:22 -04:00
Abe Jellinek
650be7d350
Scaffold code completion (#5171) 2025-04-09 00:36:22 -04:00
windingwind
a4db5c9bd2
Fix can't move second-to-last item pane section down (#5187) 2025-04-09 00:34:05 -04:00
abaevbog
4fc7730181
Citation dialog: stricter cleanup of search value (#5185)
* citation dlg: cleaner search value

Additional cleanup of the search value to not include
some punctuation ("(),.;:"), localized "and",
ampersand, and localized "et al". That way, if one
copies a citation like "(Smith et al., 2020)" and pastes it into the
citation dialog, it is more likely that appropriate search
results will appear.

Fixes: #1864

* handle citations with locators

Clean the input before fetching the locator so that
if one copies and pastes (Smith et al., 2025, p. 15),
the locator is still correctly extracted.
2025-04-08 23:56:20 -04:00
abaevbog
b0b9442c95
load all tabs items' data when restoring tab state (#5193)
This fixes the breakage in tabs menu on initial load
if one of the tab is for an item from not-loaded group.
Now, every component relying on tab items (e.g. tabs menu)
can just assume that items are all loaded.

Fixes: #5192
2025-04-08 22:42:56 -04:00
Abe Jellinek
f279e9f33b
Use normal text color for selected context row (#5197) 2025-04-08 22:41:16 -04:00
abaevbog
c6688ba9d4
include mru types in main list of "New Item" menu (#5195)
Per https://forums.zotero.org/discussion/123269/bad-practice-in-new-item-drop-down-menu
2025-04-08 22:40:07 -04:00
Tom Najdek
db90eb9103
Fix misaligned attachment row delete button #5189 2025-04-07 13:03:00 +02:00
Abe Jellinek
c400d11c5c Remove dragstart listener from attachment row on destroy
Follow-up to e6f1983073
2025-04-04 12:36:53 -04:00
Abe Jellinek
e6f1983073
Make item pane attachments/notes/related item rows draggable (#5184) 2025-04-04 12:27:02 -04:00
abaevbog
3d538d71ca
citation dlg: search by identifier (#5182)
- if the search string looks like a DOI or ISBN, use
it as a search condition. https://forums.zotero.org/discussion/comment/489309/#Comment_489309
- have library use "All Fields & Tags" search mode.
Followup to zotero@b78bf8e
2025-04-04 00:27:46 -04:00
windingwind
0b8b4c0ff6
Draggable item pane sections (#5094) 2025-04-03 21:32:37 -04:00
Tom Najdek
fcfb6e16d0
Reword the confusing example in the #generateHTMLFromTemplate() test 2025-04-02 14:01:52 +02:00
Abe Jellinek
c59c1f7e7c
Windows: Reduce input style specificity to fix directory fields (#5112) 2025-04-02 05:33:26 -04:00
Tom Najdek
4f03c5d360
File renaming: add support for counting creators (#5175)
Also extends the templating engine to support inequality comparisons.
2025-04-02 05:31:50 -04:00
abaevbog
a8d32291a4
itemBox: fix overlapping rows on initial load (#5176)
On initial load, the grippy does not immediately
appear because it needs to be loaded. This means that
when itemBox is rendered for the first time, the grippy
has a width of 0, which later becomes 18px. If the
itemPane is narrow, it may push the columns out
and lead to text of value components wrapping
without the rows getting stretched.

This sets explicit width to the grippy to avoid such behavior.

Fixes: zotero#5166
2025-04-02 05:27:47 -04:00
abaevbog
f1244d824c
Remove old citation dialogs (#5177)
- removed quickFormat, insertNoteDialog, and addCitationDialog
.xhtml and .js files, since all of them are now replaced
with new citationDialog
- removed quickFormat and insertNote strings from
en-US localization files
- replaced all mentions of quickFormat in comments and
variables of integration.js and editorInstance.js with
citationDialog
- removed big-sur-specific styling for quickFormat

Closes #5046
2025-04-02 05:26:06 -04:00
abaevbog
b8ec6eb75e
transform menu: preserve cases in capitalize title (#5174)
Do not lowercase the entire title before capitalizing
the first letter of each word to preserve abbreviations
or spelling of unusual words (e.g. 3D or iPhone).

Fixes: #5165
2025-04-01 01:25:34 -04:00
Dan Stillman
2bff0ea93f Update reader submodule 2025-03-27 23:27:09 -04:00
Tom Najdek
3a536c647b
Add ability to delete attachment from item pane (#5133)
Also:

- Introduce a mixin for a toolbar button that appears on hover or focus
- Unifies the border radius of such buttons, which was previously different for the "Libraries and Collections" toolbar button.
2025-03-27 23:25:52 -04:00
Dan Stillman
d5d24f5d9d Update utilities submodule 2025-03-27 23:10:05 -04:00
Abe Jellinek
8eda90cdc0
Scaffold: Add item pane-like preview of translated items (#5079) 2025-03-27 23:02:31 -04:00
Abe Jellinek
5a5f2c2e59 FileHandlers: Fix unintended fallthrough in switch
May have been responsible for this:
https://forums.zotero.org/discussion/comment/483004#Comment_483004
2025-03-27 10:32:12 -04:00
abaevbog
929147b33a
citation dlg: miscellaneous followup fixes (#5095)
- last-closed -> last-used for citationDialogMode pref value
- pref label citationDialogLastClosedMode -> citationDialogLastUsedMode
- update Zotero.Prefs version. While migrating, if
integration.useClassicAddCitationDialog is true, set
integration.citationDialogLastUsedMode to 'library'
so the citation dialog opens in library mode on the first run.
If citationDialogLastClosedMode pref exists, it is
migrated to citationDialogLastUsedMode and cleared
- redone logic of extracting the locator to allow for better
detection of locators and to not rely on long regular
expressions that are hard to troubleshoot. Added tests for it.

These are the new rules for extracting locators:
- locator value can be either a set of numbers potentially
with some punctuation in between OR text surrounded with
single or double quotes
- locator labels can be typed in any of 3 formats:
full (chapter), short (chap.), or short with no
punctuation (chap)
- locator labels go before the locator values and there
may or may not be a space in between. For example,
chap.10 is the same as chap. 10.

Fixes: #5092
Fixes: #5093
2025-03-27 03:45:31 -04:00
Abe Jellinek
d9e8ea8ff7
Let creator autocomplete popup fill row width (#5141) 2025-03-27 03:11:00 -04:00
abaevbog
4421de63de
citation dlg: fix unloaded items errors (#5163)
- get opened items asynchronously and load their data
in case if there are items opened across multiple
libraries, and some of them are unloaded.
This fixes #5161, which makes the dialog unresponsive
upon initial load.
- enfore io.filterLibraryIDs in library mode, the same
way it already is being applies in list mode. This
is currently used to only show items from the current
library when adding citations in a note.
- account for the change above in collectionTree to
by focusing the first library from io.filterLibraryIDs,
if those are present, and only default to userLibraryID
if io.filterLibraryIDs is empty.
2025-03-27 03:05:46 -04:00
abaevbog
a366f16346
citation dialog: remove cited item placeholder in library mode (#5152)
Keep the placeholder node to retain the positioning
of the text consistent but remove the border
so the item card is invisible.

Also: remove spinner

Fixes: #5151
2025-03-27 03:03:03 -04:00
abaevbog
b3a3757716
Citation dialog: fixed errors thrown if you type in itemTree (#5143)
- pass ref to itemsView.changeCollectionTreeRow
to avoid errors thrown when typing numbers (related to
tags shortcuts)
- return string equivalents of true/false in getExtraField.
virtualized-table expects a string in its typing handler.
Previously returned boolean was causing an error.
2025-03-26 01:56:42 -04:00
abaevbog
314bd08c03
citation dlg: localized "Add to Citation" label (#5150)
Fixes: #5146
2025-03-26 01:35:37 -04:00
Tom Najdek
8ec81da1d9
Improve attachment column progress meter (#5156)
* Better styling for file download progress meter #5144

* Fix download progress meters not removed on completion #5145
2025-03-25 00:10:35 -04:00
abaevbog
951ed4fa4b
citation dlg: fix menubar missing on first open (#5154)
Do not wait for the itemTree to add the menubar but
create it immediately and just initialize it when
itemTree is ready.

Fixes: #5153
2025-03-25 00:06:49 -04:00
abaevbog
cf37bf5ce1
citation dlg: no input refocus on Enter from popup (#5159)
Remove refocusing of the input on Enter from item details
popup - just let focus go back to the bubble.
Input refocusing was initially added with the idea that once
you make some edits and confirm them with Enter,
you are ready to go back to typing in the search input.
But then, Enter on Done or Cancel is treated as equivalent
to click, so the focus goes back to the bubble. When
the dialog is cancelled via Escape, focus lands
on the bubble as well, in case you need to keep going to
locate the another item to customize.

This different focus behavior on Enter is indeed a bit
inconsistent and likely no longer needed, now that
you can always refocus input with Cmd/Ctrl+F.

https://forums.zotero.org/discussion/122823/some-advice-for-citation-dialog-in-beta
2025-03-25 00:04:13 -04:00
abaevbog
6787dcd596
Citation dialog: do not wait for cited items to load (#5127)
Before making the dialog interactable.

Initialize loading cited items via io.getItems when the dialog appears.
Once they are loaded, itemsList is refreshed to include
cited matching items and all subsequent searches will include
them too. During the refresh immediately after cited items load,
try to preserve focused/selected state of items.

Added IOManager.preInit that is ran as soon as the dialog
is loaded. Move accept/cancel initialization there.
IOManager.init runs at the very end of onLoad because
it relies on most data and layouts being loaded,
It means if there is some delay or error during loading,
the dialog cannot be closed. To avoid it, setup
cancel/accept buttons separately in the beginning.

Also, minor refactoring of SearchHandler to handle
refreshing of cited items separately from selected and
open items.

Fixes: #5121
2025-03-20 23:42:00 -04:00
abaevbog
41bb2784b5
Citation dialog: add a dropmarker to bubbles (#5130)
- add dropmarker to bubbles after the text to indicate
that bubbles are clickable
- remove X remove button that was there before
- no more mask over the bubble on hover, since dropmarker
is just always visible
- 4px distance between the dropmarker and bubble text

Fixes: #2712
2025-03-20 23:32:39 -04:00
Abe Jellinek
93b677aaf4
Fix HiddenBrowser test failures (#5134)
Wait for 'complete', not 'interactive', so we know the image has been
loaded.

And use single documentIsReady() implementation instead of copy-pasting.
2025-03-20 23:32:02 -04:00
abaevbog
eb54cd82f6
citation dlg: refocus input on cmd/ctrl-f (#5136)
Fixes: #5100
2025-03-20 22:32:09 -04:00
Tom Najdek
9a58be243e
Improve layout of the buttons in the locate manager (#5139) 2025-03-20 17:22:07 -04:00
abaevbog
753032a001
citation dlg: highlight bubble when table focused (#5142)
In library mode, apply highlighting to bubbles that
correspond to selected items in itemTree only when
the items table is focused. When the focus leaves,
highlighting is removed from bubbles.

Fixes: #5138
2025-03-20 17:20:22 -04:00
Abe Jellinek
d79baf8251 Prevent stacked context pane from expanding over toolbar/titlebar
Regression in b649a1a953

Fixes #5135
2025-03-19 15:20:25 -04:00
Abe Jellinek
01db0bfe86 Fix occasional error when reader is closed immediately 2025-03-19 13:09:31 -04:00
Abe Jellinek
2a31c1977e Include item tree SCSS in preferences to restore tick icon
Fixes #5131
2025-03-19 10:09:33 -04:00
Dan Stillman
ab2880471e Add Troubleshooting Mode checkbox to "Restart with Logging Enabled…"
Add "Restart in Troubleshooting Mode" checkbox, checked by default, and
remove "Restart Later" option, since it's not possible to restart later
in Troubleshooting Mode. I doubt anyone used that, and if they really
need it, they can use -ZoteroDebug at the command line or toggle the
`debug.store` pref manually.

Closes #4580
2025-03-19 01:10:52 -04:00
Dan Stillman
35659b599e Indicate Troubleshooting Mode in error reports
And show plugins as disabled

Closes #4345
2025-03-19 00:19:54 -04:00
Dan Stillman
ebc670bb56 Update translators, styles, and CSL locales 2025-03-18 23:46:45 -04:00
windingwind
ccf4fd20cc
Fix plugin scope not loaded if plugin is disabled when starting Zotero (#5129) 2025-03-18 14:36:53 -04:00
Dan Stillman
d92ddd18b7 Fix WebDAV progress updates for Zotero.HTTP.download() 2025-03-18 14:10:24 -04:00
Tom Najdek
087df1f0c1 Restore attachment download progress spinner in item row (#5124) 2025-03-18 14:10:24 -04:00
Dan Stillman
80f9622c65 Change setItemDownloadPercentage() throttle from 100 ms to 25 ms
https://github.com/zotero/zotero/pull/5124#issuecomment-2731535371
2025-03-18 10:39:14 -04:00
Dan Stillman
c9f43bcdb8 Update locales from Transifex 2025-03-18 03:11:47 -04:00
Dan Stillman
91546e2e20 Remove hack for Issue search condition in French locale
https://forums.zotero.org/discussion/comment/473643/#Comment_473643
2025-03-18 02:24:01 -04:00
abaevbog
d780eed06f
citation dialog: restore arrowDown on bubbles opening details popup (#5123)
- restore earlier behavior of arrowDown on a bubble
opening itemDetails popup
- remove arrowUp closing the popup
- remove arrow navigation between rows of bubbles on arrowUp/down
- remove no longer needed edge case of arrowDown
handling on the last row of bubbles

Fixes: #5118
2025-03-17 23:27:27 -04:00
Tom Najdek
fef389290a
Fix unable to select a custom theme. Close #5070 (#5125)
This resolves an issue where, after the selected theme was deleted, it was impossible to select a newly added custom theme. This happened because the preference key for the selected theme uses consecutive IDs and wasn't cleared when the theme was deleted. As a result, the newly created theme would receive exactly the same ID. When the newly created theme was selected, the preference observer did not trigger (because the ID did not change), leading to this bug.

The fix here is to clear the theme preference when it is deleted. The added benefit is that when this happens in dark mode, the dark theme is auto-picked instead of falling back to "Original".
2025-03-17 21:07:10 -04:00
Dan Stillman
0d934c846f Fix file downloads timing out after 30 seconds
We switched file downloads to use XHR to fix downloads failing via
authenticated proxies in fx115+, but that made them subject to our
default 30-second timeout in `Zotero.HTTP.request()`. That timeout uses
the XHR `timeout` property, which applies to the whole request, even if
data is actively being downloaded. As a result, file downloads would
fail for people downloading large files or on very slow connections.

This implements manual connect and inactivity timeouts when using
`Zotero.HTTP.download()`. Currently, these both use `options.timeout`,
or the default 30 seconds, but we could probably take separate options
and have lower defaults now that they no longer apply to the whole
request.
2025-03-15 22:50:49 -04:00
abaevbog
99b4171500
citation dlg: persist screen position (#5122)
Do not explicitly move the window to the center of the screen.

Fixes: #5119
2025-03-14 23:04:31 -04:00
abaevbog
4915f1fadf
citation dialog: fix unhandled click on accept button of unfocused dialog (#5113)
Fix issue where clicking accept button would not work
when the dialog itself is unfocused. Add a small delay in the
focus handler of the dialog to allow accept button clicks
to be handled before rerunning the search.

Fixes: #5110
2025-03-14 07:49:57 -04:00
abaevbog
583fbf4e95
citation dialog: stricter non-sql item search (#5109)
While filtering selected/open/cited items, include
items that match EVERY word from the search string,
as opposed to items that match ANY of the searched words.

Fixes: #5099
2025-03-14 07:33:08 -04:00
abaevbog
a49e4e5449
citation dlg: only do not hide X when search runs (#5116)
Instead, when the search runs, replace just the accept
button with the spinner.

Fixes: #5115
2025-03-14 07:31:58 -04:00
abaevbog
bbf91d65f9
Citation dialog: move locator label lower (#5096)
So it aligns with inputs a bit better. Positioning
is tricky with native select elements but with a bit extra
margins, it looks better.

Fixes: #5091
2025-03-13 23:48:10 -04:00
Abe Jellinek
de0f5693e8
Make the info box ellipsis behave (#5103) 2025-03-13 23:47:40 -04:00
abaevbog
ebdeab4b3e
citation dialog: deduplicate open items (#5105)
So that if there are multiple tabs opened for the
same top level item, it appears only once in search
results.
2025-03-13 23:35:09 -04:00
abaevbog
92d6e69b6e
citation dlg: fix collectionTree rendering (#5114)
After switching from list mode to library mode if
the selected collection row is close to the bottom.

Fixes: #5104
2025-03-13 23:33:12 -04:00
Abe Jellinek
a98d172cd7 Restore icon to Find Full Text
Regression in 7020d60351
2025-03-13 15:39:50 -04:00
Abe Jellinek
bbfede2b55 Merge window: Fix error & blank context menu on right-click
Fixes #5077
2025-03-13 12:21:03 -04:00
Adomas Venčkauskas
bcb7e08f2f Fix citation dialog item tree refreshing on feed notifications
Closes #5107
2025-03-13 11:45:25 +02:00
abaevbog
ea71725e7f
citation dialog: Enter adds row when only 1 match (#5106)
In library mode when there are no selected/open/cited items,
Enter keypress from input will add the first row of itemTree
into the citation only if it is the only row.

Fixes: #5098
2025-03-12 21:25:41 -04:00
Tom Najdek
38188ff7d0
Fix huge buttons in banner on Linux 2025-03-12 13:45:00 +01:00
Tom Najdek
e9c0e47a17
Suggest running git lfs install in check_requirements instructions (#5102) 2025-03-12 06:54:05 -04:00
Northword
2d95cad479
Fix update checker _checker.stopCurrentCheck is not function (#5089) 2025-03-12 06:18:59 -04:00
Dan Stillman
de24306c90 Revert "Info box: Capitalize "Open Context Menu""
This is done on purpose. We distinguish between tooltips that are
command/feature titles and those that are action descriptions. So we
have "New Note" but "List all tabs". Apple does the same -- e.g., "Edit
Tags" and "Perform tasks with the selected items" next to each other in
Finder.

This reverts commit 011c5bd6c0.
2025-03-12 05:36:11 -04:00
abaevbog
cb53c6eb2e
citation dialog: fix IO race condition on load (#5097)
IOManager relies on everything being loaded, so it
should be initialized last. Otherwise, any IO handler
trying to access currentLayout would throw an error.

Also, swap the order of operations in bubble-input to
set input width only after the first input is added.
Otherwise, first input will have an arbitrary width
during initial load.

https://forums.zotero.org/discussion/122554/beta-new-insertion-bar-problem-with-citations-but-not-notes
2025-03-12 00:48:40 -04:00
Abe Jellinek
011c5bd6c0 Info box: Capitalize "Open Context Menu"
Like other button tooltips.
2025-03-11 16:36:43 -04:00
Dan Stillman
b78bf8e24b Citation dialog: Use "All Fields & Tags Mode" in Library Mode
Quick fix for #5090
2025-03-11 08:04:41 -04:00
Abe Jellinek
9cdfadbbae
Fix space before ellipsis in editable-text (#5087) 2025-03-10 22:47:38 -04:00
Dan Stillman
764dd1a9da Take object instead of id in Utilities.Internal.showInLibrary()
Not updating quickFormat.js, since we'll be removing that
2025-03-10 22:40:43 -04:00
Bogdan Abaev
6d52656a92 integration.ftl file for "integration-*" strings
And a few minor .ftl string edits
2025-03-10 22:40:43 -04:00
Bogdan Abaev
4a00304925 Redesigned citation dialog (#4872)
Implemented redesigned citation dialog with library
and list modes one can switch between. This dialog is
a direct replacement of quickFormat and classic citation
dialogs.

In Library Mode:
- items table has a new + button column to add items from the
citation and rows of selected items are highlighted
- open, cited and selected items appear in a section
between the items table and bubbleInput. When there are no
matches, a message is shown.
- only top-level items are shown when citing
items and only notes/notes' parents - when adding a note
- selected items are gathered in a collapsible deck to save space.
Click on the deck will expand it. All selected items can be
added via "Add all" button.
- when an item is added, bubble-input may increase in height and
push itemTree lower. To try to preserve relative positioning
of the mouse, itemTree will scroll to be over the row that was just clicked

In List mode:
- arrow up/down from the input will change the selected item
with the focus remaining in the input
- selected items are a collapsible list section

Other behaviors and fixes:
- one can add any locator (not just pages) by typing its full or short name
in an input and pressing Enter (e.g. line 10, or l. 10, or chap. "test chapter")
- Added a new preference to select if the citation dialog
should always open in list mode, library mode or in the last
mode that was used
- bubbles whose items are selected in library or list mode
are highlighted
- arrow up/down from a bubble will focus the bubble
above/below it for easier navigation across bubble-input
- multi-select is supported on items in list mode or item cards in
library mode via Shift-arrow or Cmd+click to select multiple items
- Cmd/Ctrl + Enter will always accept the dialog no matter what
is focused
- when there are no bubbles, accept button is disabled
- after an item is added, bubble-input is always refocused
- added suppressed property to itemTreeMenubar to be able to
hide if in list mode, where it is not relevant. Fixed menubar
getting stuck or re-appearing on Alt keypress on Linux by setting
height: 0 vs hiding it via hidden.
- added initialFolder and onActivate prop to collectionTree to set which collection
should be reopened when dialog opens and to be able to set a custom
onActivate handler.
- added getExtraField prop to itemTree to get data on if
an item is in a citation or not

Implementation details:
- citationDialog.js is the main file. It relies on a number of
helper files in citationDialog/* directory to keep the main
file less cluttered. popupHandler.js contains the logic of
opening/closing the item details popup to add locator/prefix/suffix/etc.
keyboardHandler.js is responsible for overall keyboard navigation
throughout the dialog. searchHandler.js contains the logic
for running the search based on user's query. Finally, Helpers.js
has general helper functions that don't handle any actual logic.
- SearchHandler is set to run search in two ways: for cited/selected/open
items (which are cached and do not involve any actual SQL query) and
general search for items across all libraries. When layout.search
runs, firstly cited/selected/open items are updated after which the
SQL search runs.
- bubbleInput.js is a customElement responsible for bubbles interface.
bubbleInput.refresh takes a list of items and handles adding/removing/reordering
of bubbles as needed. It always has two inputs on each side of
a bubble, as opposed to having inputs inserted dynamically. It
allows keyboardHandler.js to handle navigation with arrows within
bubbleInput.
- when user interacts with bubbleInput, it emits custom events that are
handled by IOManager singleton in citationDialog.js, which
may update the items information and pass them back to bubbleInput.refresh
to have the list of bubbles updated
- CitationDataManager singleton is responsible for storing
items added into the citation in CitationDataManager.items
in an object with both Zotero.Item and the actual citation item.
It is easier to pass both items to other components
and helpers, as opposed to sharing functions to convert
items back and forth.
2025-03-10 22:40:43 -04:00
Bogdan Abaev
27f1103d88 Use regularOnly in "Change Parent Item" selectItemsDialog 2025-03-10 22:40:43 -04:00
Dan Stillman
cbd6ccbec3 Don't enable Citation Explorer for Google Docs for now 2025-03-10 22:40:43 -04:00
Adomas Venčkauskas
a3654652b2 Adds a Citation Explorer dialog for document integration (#3468) 2025-03-10 22:40:43 -04:00
Dan Stillman
8ddb6bfd17 Remove code to modify Zotero.DB properties via transaction options
No idea why this was ever here, but I don't think it's actually used
(and if it is, it shouldn't be). The only options I see are handled
directly within `executeTransaction()` itself.
2025-03-10 04:26:16 -04:00
Dan Stillman
e532e435f4 Fix very slow purging of itemDataValues at startup in large databases
When an item is erased (removed from the trash or cleaned up from a
feed), we set a flag to purge values in `itemDataValues` on the next
startup, with this query:

DELETE FROM itemDataValues WHERE valueID NOT IN (SELECT valueID FROM itemData);

For some people, that query was incredibly slow and would result in
Zotero intermittently hanging on "Loading items…" for a long time at
startup. It's possible this is mostly limited to people who subscribe to
high-volume feeds and have a lot of item churn. One affected person had
>900K values in `itemDataValues` despite having only 20K items.

It turns out the slow query is due to the foreign-key constraint on
`itemData(valueID)` that references`itemDataValues(valueID)`. SQLite is
checking every row being deleted from `itemDataValues` against
`itemData`, even though the query is specifically removing rows that
don't exist in `itemData`! For the 900K-value DB, disabling foreign-key
checks causes the `DELETE` query to take 25 seconds instead of...some
much longer time that I didn't wait for.

We already had an `executeTransaction()` flag, `disableForeignKeys`, to
temporarily disable foreign-key checks, but it didn't do so in a way
that was safe for post-initialization usage -- a write query outside of
a transaction could've run between the transaction commit and
foreign-key checks being re-enabled. This commit changes it to properly
block all other queries unless they include an `ignoreDBLock` option,
meaning that queries within the function passed to the transaction need
to include that option. (And since that's not realistic for the couple
other uses of `disableForeignKeys` -- one for a test and one in code
that almost certainly hasn't been run by anyone in 15 years -- those now
just run `PRAGMA foreign_keys=OFF|ON` explicitly, leaving this as the
only current use.)
2025-03-10 04:26:16 -04:00
Dan Stillman
659d5e864b build_and_run: Fix handling of ZOTERO_PROFILE containing spaces 2025-03-09 16:16:47 -04:00
Abe Jellinek
ac0813e0f5
Restore window controls on Windows/Linux (#5083) 2025-03-07 16:24:46 -05:00
PChemGuy
8a06edab49
Sort strings naturally or alphabetically (#5075)
Hidden setting. If true, use natural collection sorting matching the current behavior. If false, use conventional alphabetic sorting.
2025-03-07 02:08:11 -05:00
Abe Jellinek
679de040ec Add tests for renderItemTitle() 2025-03-06 11:00:33 -05:00
Abe Jellinek
8082516bf6 renderItemTitle(): Create <span> when inverting style
Creating an <i> with a style attribute that *removes* italics is just
too confusing.
2025-03-06 11:00:32 -05:00
Abe Jellinek
c9c9c500bf renderItemTitle(): Fix nested tag handling when rendering to string
Need to exit the loop regardless of whether we're rendering actual
nodes.

Fixes #5082
2025-03-06 10:20:13 -05:00
Dan Stillman
d931319b12 Prevent DNS/TCP connections in snapshots 2025-03-06 01:46:38 -05:00
Dan Stillman
d7ce4191b1 Remove chai-as-promised submodule
We've included this in package.json for many years, so this shouldn't be
here.
2025-03-05 22:59:49 -05:00
abaevbog
d0bf8f8993
always trim editableText from itemBox on blur (#5081)
So that whitespaces are removed even if the itemBox
does not refresh.

Fixes: #5080
2025-03-05 18:03:03 -05:00
Dan Stillman
05f3ea3b82 Update reader submodule 2025-03-05 07:30:21 -05:00
Dan Stillman
8021ca0a0b Fix /connector/saveSnapshot test for Zotero.HTTP.download() 2025-03-05 07:18:12 -05:00
Tom Najdek
6add721e68
Convert customThemes from a pref to a synced setting (#5071) 2025-03-05 06:43:52 -05:00
Dan Stillman
a464fe0408 Update "Find Full Text" tests for Zotero.HTTP.download()
Downloads are now included in request() call counts
2025-03-05 06:34:25 -05:00
abaevbog
d3cee95fb7
Click on itemBox label focus edits (#5065)
Click on a label of a focused field will refocus itemTree
or the reader content, depending on the tab. That way
focus is not lost.

Fixes: #5063
2025-03-05 05:59:11 -05:00
Dan Stillman
c262a5b392 Add Cr as glboal in eslint config 2025-03-05 05:51:04 -05:00
Dan Stillman
3a26ce3bb1 Avoid args is undefined logged error in file sync tests 2025-03-05 05:50:58 -05:00
Dan Stillman
1f401f0897 Replace Zotero.Utilities.Internal.saveURI() with Zotero.HTTP.download()
Adds a new function, Zotero.HTTP.download(), that uses
Zotero.HTTP.request(). This fixes downloads via authenticated proxies in
Zotero 7 and gives us other request() functionality (e.g., 5xx retrying)
for free.

The downside is that this is probably less efficient, potentially
loading large downloads in memory. We should create a replacement for
request() based on fetch() that supports getting the body as a
ReadableStream.

Fixes #5062
2025-03-05 04:46:51 -05:00
windingwind
ccc1800f21
Fix attachment preview background (#5067)
regression from 9c41874
2025-03-04 09:45:22 -05:00
Abe Jellinek
cab8e38806
Remove deleted actors to prevent console spam (#5074) 2025-03-04 09:45:08 -05:00
Abe Jellinek
5c8859b618 RemoteTranslate: Fix request methods not sending cookies 2025-02-28 15:22:37 -05:00
Abe Jellinek
c19f43ea46 Scaffold: Suppress ESLint JSON.parse() syntax errors 2025-02-28 15:16:26 -05:00
Abe Jellinek
1e08985dcd Scaffold: Accept shorthand URLs in URL box
Like "google.com" instead of "https://www.google.com/".
2025-02-28 15:16:12 -05:00
Dan Stillman
307753d313 "Select add-on to install" → "Select plugin to install"
https://forums.zotero.org/discussion/122235/title-in-the-plugin-selection-windows-dialog-add-on-plugin
2025-02-27 23:30:45 -05:00
windingwind
1b64e3417c
Fix web-ext support (#5068) 2025-02-27 22:28:42 -05:00
Abe Jellinek
6f8da4825e Advanced Search: Remove red background from condition tooltips
Fixes #5066
2025-02-26 16:41:07 -05:00
Abe Jellinek
ba7b521bb1 Scaffold: Clean up load() slightly 2025-02-26 12:09:06 -05:00
Abe Jellinek
ddb5977c48 Scaffold: Remove 2028/2029 fix
Hasn't been an issue since ES2019.
2025-02-26 11:27:02 -05:00
Martynas Bagdonas
9077310669 Update pdf-worker submodule 2025-02-26 18:10:58 +02:00
Abe Jellinek
3c8d50dd47 Remove loadBrowserWindow() test support function (#5050)
A browser window was no longer actually needed for charset detection
(on macOS, at least, and hopefully elsewhere) because we switched to a
HiddenFrame-based hidden browser. Remaining uses now call
`loadZoteroWindow()`.
2025-02-26 03:00:04 -05:00
Abe Jellinek
26221b6786 Fix broken browser window on print + remove many Mozilla files (#5050) 2025-02-26 03:00:04 -05:00
Abe Jellinek
90a9c5164a
macOS: Unescape dragged path when NSURL doesn't (#5059) 2025-02-25 05:28:12 -05:00
abaevbog
c049120efb
do not try to select deleted item in a collection (#5061)
After navigating to the collection from "Libraries and Collections"
section of an item in the trash. Deleted item does not appear in actual
collections and trying to select it will just re-select the trash.

Fixes: #5060
2025-02-25 05:24:59 -05:00
Martynas Bagdonas
a3ebb4ad41 Update reader submodule 2025-02-25 11:24:33 +02:00
Abe Jellinek
854f2188c2 Duplicates: Fix hidden elements remaining visible
And update comment.

Fixes #5056
2025-02-24 11:18:48 -05:00
abaevbog
54346b0ca7
fix broken QF cancellation from editor instance (#5054)
Define missing cancel method on io object passed from the
editor instance to quickFormat dialog. Without this
Escape keypresses throws an error because io.cancel is
undefined.

Followup to #4859
2025-02-21 22:13:41 +02:00
Martynas Bagdonas
96e71762f9 Normalize decomposed chars in PDF text, Zotero notes for proper indexing
Fixes #5051
2025-02-21 14:55:20 +02:00
Abe Jellinek
d3871d6864 editable-text: Forward onTextEntered() from input
Fixes #5043
2025-02-20 14:40:58 -05:00
Dan Stillman
45310397e5 Update Word for Mac submodule 2025-02-20 06:28:27 -05:00
Adomas Ven
2e9b9d4070
Close citation dialog if user interacts with the integration plugin (#4859)
Asks the user if they want to discard the changes in their existing dialog if
they have made changes.

If there is no citation dialog, but a command is running (like updating the document)
an alert is displayed.

Closes #4855
2025-02-18 05:52:23 -05:00
Abe Jellinek
eb30c23a0d
Scaffold: Infer types of parameters based on naming conventions (#5038) 2025-02-18 05:07:51 -05:00
Adomas Venčkauskas
239b68e6b8 Update Word for Windows submodule 2025-02-18 11:43:51 +02:00
Adomas Venčkauskas
d231fb3b53 Do not attempt to activate windows on wayland using X11 libraries
I don't know if something changed in my Ubuntu installation or in the
Firefox runtime (probably the former), but that code crashes Zotero on
wayland.

Window activation (e.g. citation window) does not seem to be affected.
2025-02-18 10:55:22 +02:00
Adomas Venčkauskas
e765a4d3af Update ScienceDirect cf captcha selector 2025-02-18 10:53:02 +02:00
windingwind
3b7dd58c50
Update eslint ecmaVersion to es2022 (#5049)
Current firefox version supports all features of es2022
Fix errors with features like WeakRef
2025-02-17 05:53:15 -05:00
Dan Stillman
fc3b7e0f83 Local API: Fix handling of style ids without standard prefix
Follow-up to #5032
2025-02-17 03:16:22 -05:00
Dan Stillman
9c6b3368f2 Revert "Zotero.Styles: Serialize init() and remove Zotero.Promise.coroutine()"
This breaks style initialization if you trigger it manually before the
normal startup initialization.

This reverts commit d141ab25b1.

https://forums.zotero.org/discussion/121879/missing-item-pane-header
https://forums.zotero.org/discussion/121878/cannot-copy-citation-bibliography-in-reader
2025-02-17 02:19:59 -05:00
Adomas Venčkauskas
8e08527991 Update Word for Windows submodule 2025-02-13 10:36:23 +02:00
Adomas Venčkauskas
51d668db56 Update Word for Windows submodule 2025-02-13 10:30:40 +02:00
Dan Stillman
c51133f43c Update reader submodule 2025-02-13 03:13:54 -05:00
abaevbog
506f3b14e3
Allow dragging items, collections, and searches into trash (#5033)
Fixes: #1943
2025-02-13 02:58:25 -05:00
Abe Jellinek
5978353d57
Fix faulty siblings-of-type check in setAutoAttachmentTitle() (#5045) 2025-02-12 22:29:49 -05:00
Adomas Ven
8dbc6177ce
Macos integration progress bar (#5016)
* Display the progress bar on macOS integration commands more

Avoid displaying before Add/Edit Citations, Add Note and Doc Prefs
since that brings Zotero window to the foreground along with the
relevant dialog, but display the progress bar after confirming those
dialogs, which will bring Zotero to the front, but is still better than
no progress indication.

Display the progress bar for Refresh, Doc Prefs, Unlink and Add/Edit
Bibliography

* Fix progress bar indeterminate state not being visible and not working.

- Update _progressMeter.scss for fx115+
- Some adjustments/resizing to progress bar display in quick format
2025-02-12 08:43:11 +02:00
Abe Jellinek
87f8229b32 macOS-normalize-controls mixin: Cleanup 2025-02-11 15:23:16 -05:00
Dan Stillman
4d8d4bf6a9 Remove issue template
Now in .github repo
2025-02-10 23:25:03 -05:00
Abe Jellinek
98a5b7eaec Local API: Handle out-of-repo styles correctly (#5032) 2025-02-07 07:43:28 -05:00
Abe Jellinek
d25b96643c Zotero.Styles: Serialize init() and remove Zotero.Promise.coroutine() 2025-02-07 07:43:28 -05:00
Dan Stillman
fb8b65b329 Update reader submodule 2025-02-07 03:27:42 -05:00
Dan Stillman
7256072c5b Merge new English strings 2025-02-05 05:10:14 -05:00
Dan Stillman
083b4f69c9 Pretty-print zoteroTypeSchemaData.js
We should get rid of this, but in the meantime, pretty-print it (though
it will need to be generated in a new data directory to be consistent).
2025-02-05 03:27:33 -05:00
Dan Stillman
d358915707 Update sample data for podcast/date mapping 2025-02-05 03:08:32 -05:00
Dan Stillman
46879a8241 Add Date field on Podcast
zotero/zotero-bits#2
2025-02-05 02:52:17 -05:00
Dan Stillman
5d5c043161 Fix sync not downloading queued objects on first sync of session
`firstInSession` wasn't being properly set, so previously failed data
objects weren't being retried until a manual sync. Local files also
weren't being scanned for changes for three hours, but that's probably
better in the age of DB-stored annotations, so leave that off for now
and address properly in #5025.
2025-02-05 02:06:15 -05:00
Dan Stillman
41396c8148 Update Mac build to Firefox 128.7.0esr
Fixes #4762
2025-02-05 01:24:20 -05:00
Martynas Bagdonas
553f0f3994 Implement reader appearance popup with custom themes support
Implements #4949, #5005
2025-02-04 13:43:50 +02:00
Dan Stillman
553e2466ed Revert "Add script to install .desktop file on Linux (#5019)"
After further consideration, doing this in-app makes more sense. This
way, we'd never be able to update the desktop file automatically (e.g.,
to add a new MimeType), and removing the template zotero.desktop file
would break all existing installs using our symlink instructions.
In-app, we can update the existing desktop file on restart after an
upgrade, and we can remove the existing symlink.

This reverts commit 0ac57f8262.
2025-01-31 01:19:18 -05:00
Dan Stillman
3d75801a84 Don't open new copies of runJS/scaffold/csledit
Possible we'll get complaints about no longer being able to open
multiple copies of at least runJS and csledit, and we can reconsider if
so, but reopening the existing window is certainly the intended
behavior.
2025-01-31 00:58:37 -05:00
Martynas Bagdonas
6582f7ffda Fix tags overflowing in attachment annotation preview
Fixes https://forums.zotero.org/discussion/comment/477579/#Comment_477579
2025-01-30 17:49:29 +02:00
Dan Stillman
0ac57f8262
Add script to install .desktop file on Linux (#5019) 2025-01-30 02:56:10 -05:00
Dan Stillman
7466fa479f fx128: Further tweaks to "Check for Updates…" code
Don't use internal interface if we can help it

Relevant Mozilla changes:
https://bugzilla.mozilla.org/show_bug.cgi?id=1875502#c60
2025-01-29 01:07:51 -05:00
Dan Stillman
eb776f8e6e fx128: Fix "No updates found" if update was already downloaded
Previously fixed for fx115 in #3148, but `nsIUpdateManager` changed in
fx128

https://forums.zotero.org/discussion/121482/zotero-7-1beta-does-not-find-updates
2025-01-29 00:46:00 -05:00
Abe Jellinek
51349a5a97 Local API: Read headers case-insensitively 2025-01-28 10:45:08 -05:00
Abe Jellinek
a2a99c12f5 Local API: Fix test 2025-01-28 10:44:37 -05:00
Abe Jellinek
2a7f4fb2a5 Local API: Return everything when since=0
Addresses #5011 (option A)
2025-01-28 10:39:43 -05:00
Abe Jellinek
2aee7ae8d5 RemoteTranslate: Fix updated tests not inheriting defer 2025-01-28 10:07:11 -05:00
Adomas Venčkauskas
2afbe030d5 Update Word for Windows submodule 2025-01-28 10:39:44 +02:00
Dan Stillman
d1e67987bd Fix broken test after #5004
Reverts a change I made to the PR. Only `since=0` would be valid in the
web API, but local items don't have a version by default, so it has to
be `-1` to match those. This probably isn't a great solution, since tools
targeting the web API would use `0`, but this will fix the test for now.
2025-01-28 01:25:08 -05:00
Abe Jellinek
65393d09eb
Check editability based on tab when adding attachments (#4936)
And make `get[Selected/Sorted]Items()` and `canEdit[Library/Files]()` tab-aware
2025-01-28 01:00:17 -05:00
Abe Jellinek
a7fff8cada
Local API: Add /fulltext endpoints (#5004) 2025-01-28 00:58:12 -05:00
Abe Jellinek
6cdae37e5b
Local API: Fix 500 error when attachment has unparseable path (#5010) 2025-01-28 00:54:29 -05:00
Abe Jellinek
48b50064e9
Item box: Fix missing Copy/Paste menuitems on URL fields (#5001) 2025-01-28 00:48:41 -05:00
Abe Jellinek
be023c3a95 RemoteTranslate: Fix error when creating test 2025-01-27 15:23:51 -05:00
Dan Stillman
71d697801e Fix banner style issues
- Fix background color on post-upgrade banner
- Add drop shadow between stacked banners (e.g., post-upgrade banner and
  Mac Word banner, which will generally appear together on an upgrade
  and have the same background color)
- Align right edge of buttons in post-upgrade and Mac Word
- Unify other spacing and font sizes
2025-01-27 07:34:31 -05:00
Dan Stillman
7a467b8bfa Fix selectItems() not selecting non-top-level item in trash
https://forums.zotero.org/discussion/comment/484380/#Comment_484380

Adds `Zotero.Item#isInTrash()` that returns true if the item or any of
its ancestors is in the trash (i.e., `.deleted == true`)
2025-01-27 04:02:10 -05:00
Dan Stillman
814e59f694 Use commas in file size in over-quota error 2025-01-27 04:02:10 -05:00
Dan Stillman
39cbf5e520 Fix double URL launch via keyboard in sync error panel
Space bar or Enter are (now?) equivalent to `click` on a button
2025-01-27 04:02:10 -05:00
Abe Jellinek
465cc89ad6 Scaffold: Use native checkboxes in select dialog 2025-01-24 15:04:52 -05:00
Abe Jellinek
9510eef69a Scaffold: Fix select dialog overflow (again!)
And restore button spacing.
2025-01-24 15:04:43 -05:00
Abe Jellinek
ff3ab619d1 Scaffold: Fix appearance regressions, simplify margin/padding
- Move 16px padding into the tabbox, which lets us eliminate the
  negative margin on #right-pane and the 5px padding to make room for
  the focus ring
- Remove width: 100% from the textarea - not necessary and causes
  horizontal overflow on macOS

Tested on macOS and Windows.
2025-01-24 14:26:05 -05:00
Abe Jellinek
679b1df116 Update translate submodule 2025-01-24 14:10:31 -05:00
Abe Jellinek
3a816fad55 Scaffold & RemoteTranslate: Improve defer handling
- Use extracted delay routine
- Pass defer into newTest()
2025-01-24 12:10:33 -05:00
Abe Jellinek
5f983d71c1 RemoteTranslate: Improve page readiness checks
Treat page as immediately ready after readyState == 'complete', or give
it 100ms after 'interactive'. This is rough, but 'interactive' isn't
always fully ready and 'complete' sometimes never happens.
2025-01-24 12:10:15 -05:00
Abe Jellinek
79092fadcd Update translate submodule 2025-01-24 12:02:43 -05:00
Abe Jellinek
b6ca3eac37 Remove dead contentType branch
Already handled in isCachedMIMEType().
2025-01-23 17:56:32 -05:00
Tom Najdek
dcf809961a
Add a gap between the buttons in the progress queue 2025-01-23 14:59:42 +01:00
Dan Stillman
ed045e640a Tests for Item#fromJSON() changes (#3422) 2025-01-22 03:23:06 -05:00
Dan Stillman
ec280c5bdf Item#fromJSON(): Don't process any Extra rows in strict mode
And enable migration of Extra rows to valid fields in non-strict mode,
but since that's only used by translators now, that probably won't do
anything

Fixes #3422
2025-01-22 03:16:22 -05:00
Dan Stillman
8642091d8e Don't auto-select item after sync on upload error
Bug from 2017
2025-01-22 03:14:07 -05:00
Dan Stillman
e3387bbe96 Adjust debug line if file not available for uploading
Files can be missing locally and flagged for uploading, so not just a
files still being saved
2025-01-22 03:10:40 -05:00
Dan Stillman
aaf7984a8e Fix alignment of tag rows in long tag fixer
Rows were center-aligned
2025-01-22 03:09:31 -05:00
Dan Stillman
39b92d6cdb Fix incorrect spacing around link in Z7 post-upgrade banner
Regression from 564b1766aa

Fixes #4926
2025-01-22 03:07:36 -05:00
Dan Stillman
d2952d3e71 Remove duplicate padding rule 2025-01-22 03:07:19 -05:00
Dan Stillman
9e569c33f8 Adjust nesting of #post-upgrade-banner SCSS 2025-01-22 03:06:40 -05:00
Dan Stillman
a2f3b28b48 Remove unnecessary code after ec50539e80
`getBaseIDFromTypeAndField()` now returns the field itself if it's
already a base field
2025-01-22 01:06:03 -05:00
Dan Stillman
6308ea6a18 Update reader submodule 2025-01-21 22:30:51 -05:00
Abe Jellinek
00c0679333 Add key to Advanced Search menuitem
Fixes #4978
2025-01-21 11:06:35 -05:00
Dan Stillman
296edc3fdd
Unset Mozilla environment variables when launching files and URLs (#4983)
Fixes #4981
2025-01-21 05:40:27 -05:00
Tom Najdek
a4414cc2f0
Remove last few legacy icons #3691 (#4996) 2025-01-21 05:37:56 -05:00
windingwind
67da025e3f
Fix attachments box race condition (#4997)
fix: #4611
Since the attachment preview already has a task queue,
don't skip render calls when rendering
2025-01-21 05:27:33 -05:00
Dan Stillman
810062a1af Remove reference to Zotero for Firefox in incompatible-DB error 2025-01-21 05:17:36 -05:00
Dan Stillman
850431af93 Fix display of button bar in sync error panel 2025-01-21 05:17:36 -05:00
Dan Stillman
e1cee8cc45 Don't try to initialize Places, since we removed its files 2025-01-21 05:17:36 -05:00
Martynas Bagdonas
b84f1a70f0 Update pdf-worker submodule and extract only worker.js from zip build
The pdf-worker zip builds now include cmaps and standard_fonts but in Zotero client we reuse them from Zotero Reader path
2025-01-20 19:12:50 +02:00
Adomas Venčkauskas
94650e2ee9 Fix integration session breaking when an error is thrown during getDocument() 2025-01-20 12:11:50 +02:00
windingwind
998315dd58
Fix menu popup dblclick on MacOS (#4989) 2025-01-17 13:00:01 -05:00
Abe Jellinek
e11d93f0bc
Restore whitespace in search Collection condition menu (#4990) 2025-01-16 23:13:06 -05:00
Dan Stillman
9f83790ed6 fx128: Fix broken update channel after an update on macOS
Don't delete ChannelPrefs.framework during updates
2025-01-16 19:05:43 -05:00
Abe Jellinek
c8051aa3f8 Scaffold: Make Defer checkbox interactive 2025-01-16 11:07:45 -05:00
Abe Jellinek
0bdae76e49 Scaffold: Cleanup 2025-01-16 10:39:05 -05:00
Yichao Yu
8cfcdeb8e6
Use uname -m instead of arch (#4982)
`uname` is used in the script elsewhere already and `arch` does not seem to be available everywhere
2025-01-13 23:37:49 -05:00
Dan Stillman
484eb1f1d7 Fix ids showing instead of creator names for creator autocomplete
https://forums.zotero.org/discussion/121089/beta-author-suggestions-broken-only-suggest-numbers-instead-of-names

Related Mozilla bugs (though I don't see a change that would be directly
responsible for this):

https://bugzilla.mozilla.org/show_bug.cgi?id=1886064
https://bugzilla.mozilla.org/show_bug.cgi?id=1910073
2025-01-13 02:36:28 -05:00
windingwind
524660e10b
Fix dense call to setItemDownloadPercentage freezes app (#4979) 2025-01-11 03:17:56 -05:00
abaevbog
459d00448a
Hide "Report" button from plugin context menu (#4976)
Fixes: #4975
2025-01-10 01:25:51 -05:00
Dan Stillman
52c342b697 Update ESLint and Babel
Updated ESLint is necessary for flat config changes from 54db192be8
2025-01-09 03:11:01 -05:00
Dan Stillman
b86305f40e Fix json[c.field] is undefined sync error
Fix error if local note or attachment is added to a parent item while
the remote version was added to a collection. Now, the new local parent
item will be added to any collections the remote child item was added
to.

Fixes #2934
2025-01-09 03:11:01 -05:00
Dan Stillman
c44d845c8c Indent some switch case statements
For current style
2025-01-09 03:11:01 -05:00
Tom Najdek
c41eb54278
Fix Metadata Retrieval to correctly display the recognized item's title 2025-01-09 01:44:43 +01:00
Adomas Venčkauskas
b538b12d51 Refresh document with delayed citation updates when changing style
Closes #4974
2025-01-08 14:03:44 +02:00
Dan Stillman
ef8de73d5a Update note-editor submodule 2025-01-08 01:48:28 -05:00
Dan Stillman
2c377316e3 Update Mozilla langpack files 2025-01-08 01:41:29 -05:00
Dan Stillman
ab9849e71a fx128: Add arrowscrollbox.ftl from Mozilla
Fixes locales not being fully loaded, including for plugins, in 128

https://groups.google.com/g/zotero-dev/c/kcQ-A3EclBQ/m/ShIFTfM6DAAJ
2025-01-08 01:41:29 -05:00
Dan Stillman
46d5d2f5ee merge_mozilla_files improvements
- Remove duplicate/missorted locales
- Treat empty files in langpacks as missing and use en-US
- Skip empty lines and comments when using en-US file for empty
  non-en-US files, in line with Mozilla langpack files
2025-01-08 01:39:24 -05:00
Dan Stillman
bad01686d8 Fix some spaces instead of tabs in fetch_xulrunner 2025-01-08 00:30:21 -05:00
windingwind
c1c2bc76b7 fx128: Enable -moz-context-properties for SVG context properties 2025-01-07 23:50:46 -05:00
Dan Stillman
bc3c05d064 CI: Update to dpl v2 2025-01-07 01:32:25 -05:00
Dan Stillman
74216a5887 Update note-editor and reader submodules 2025-01-07 01:24:40 -05:00
Dan Stillman
9bee1b1d79 Update builds to 128.6.0esr
128.5.0 launcher still works
2025-01-07 01:15:49 -05:00
abaevbog
5fd94e22df
remove wrong aria-autocomplete from search-textbox (#4966)
searchButton setter in search-textbox element adds aria-autocomplete
to the input. It makes screen readers announce that there is an autocomplete
popup, which is misleading (even though it is appropriate for
firefox that actually does have autocomplete-like dropdown).
This overrides the searchButton setter to remove aria-autocomplete
attribute.

Addresses: zotero#4932
2025-01-03 05:14:36 -05:00
Dan Stillman
805378e49f Update version to 7.1 2025-01-03 01:21:57 -05:00
Dan Stillman
c672392fe4 Move misplaced line in build script 2025-01-03 01:21:57 -05:00
Dan Stillman
7e7ceffd77 Move utility functions for build to separate file
`replace_line`, etc.

To make them available to both build.sh and fetch_xulrunner
2025-01-03 01:21:57 -05:00
windingwind
9777d21001
Fix export error of item.tags being undefined (#4962)
Item generated from embedded image attachment may not have attribute `tags`.
2025-01-03 00:48:31 -05:00
abaevbog
7dad8377df
qf: default arrowUp handling on locator menulist (#4965)
Do not close details panel on arrowUp when locator
type menulist is focused to allow for default handling
(e.g. select the next/previous option on windows).

Fixes: #4953
2025-01-02 22:42:38 -05:00
Dan Stillman
0b44461a66 Fix Zotero.locked getting stuck on in data directory tests
This caused later DB tests to fail due to backups not running.
2025-01-02 07:41:16 -05:00
Dan Stillman
6ee556500d fx128: Implement non-incremental backup option
A new function, `Zotero.DB.backUpDatabase()`, by default makes offline
backups, by closing the database, doing a regular file copy, and
reopening the database. It takes an options object with an `online`
flag to make online, incremental backups that can take multiple minutes
to complete, though for now we're still running them on idle.

The old function, `Zotero.DB.backupDatabase(suffix, force)`, is
deprecated and proxies to the new function, making offline backups.

Fixes #4935
2025-01-02 02:56:41 -05:00
Dan Stillman
54db192be8 Update eslint config to new flat config format
https://eslint.org/docs/latest/use/configure/migration-guide
2025-01-02 01:31:31 -05:00
abaevbog
7b7a0b9e46
save tagsBox updates when another item is selected (#4945)
When tagsBox item is changed by clicking on an itemTree row,
the blur even never fires on the currently focused tag. So
whatever changes were made will be discarded. To avoid it,
blur any opened tag rows (which triggers a saveTx)
when an item is being set, same way it is done in itemBox.

Also, a small tweak to properly fetch the focused tag
via editable-text:focus-within selector, since editable-text:focus is
always empty because the focus is on the input inside of
editable-text.

Fixes: #4942
2024-12-20 02:55:33 -05:00
abaevbog
a89e0a1554
hide switch creator type in non-editable itembox (#4940)
Fixes: #4937
2024-12-20 02:52:06 -05:00
Dan Stillman
001c94b585 Update reader submodule 2024-12-20 02:49:54 -05:00
Dan Stillman
20e28d3f9e Fix handling of redirecting OpenURL base URLs with url query parameter
For a URL like
`https://ezproxy.school.edu/login?url=http://resolver.ebscohost.com/openurl`,
without a trailing `?`, we were just adding a `%` to the end, making the
URL invalid after the redirection (`/openurl&url_ver=Z39.88-2004`
instead of `/openurl?url_ver=Z39.88-2004`).

(And apparently no one who sent in these URLs actually tested them
in-app?)
2024-12-20 02:47:36 -05:00
Dan Stillman
244d182293 Fix logged error in integration progress bar
After b007bd4364
2024-12-20 02:37:25 -05:00
Dan Stillman
e8b1ea5b55 Update Word for Windows submodule 2024-12-20 02:31:25 -05:00
Abe Jellinek
fa48e978c7 Serialize _ensureFeedAbstractBrowserExists()
Prevent a potential race condition.
2024-12-18 12:20:00 -05:00
Abe Jellinek
013a814765 fx128: Fix feed abstracts
Closes #4938
2024-12-18 12:19:30 -05:00
abaevbog
cddbbdd208
fx128: fix broken database backup (#4931)
Per https://bugzilla.mozilla.org/show_bug.cgi?id=1869060#c18,
the backup utility was removed from the storage service and
exposed durectly on the connection itself in fx128.

Fixes: #4929
2024-12-18 07:11:27 -05:00
Dan Stillman
7031d1928a Update note-editor submodule 2024-12-18 07:00:36 -05:00
Dan Stillman
878d1cc401 fetch_xulrunner: Automatically cache unmodified Windows Firefox 2024-12-18 04:25:47 -05:00
Dan Stillman
485033b4a4 fx128: Fix new errors trying to load removed remote-settings.sys.mjs
Fixed previously in 297af9b40
2024-12-18 04:15:38 -05:00
Martynas Bagdonas
b806b41b6f Render ink annotation images when previewing attachment annotations 2024-12-17 18:29:48 +02:00
Martynas Bagdonas
aa4f942b26 Update pdf-worker submodule
Fixes https://forums.zotero.org/discussion/120554/consistent-failures-to-export-annotated-pdf
2024-12-17 17:58:09 +02:00
Bogdan Abaev
b007bd4364 fx128: hide titlebars in dialogs on win and mac
Use titlebar.js to hide titlebars in windows and mac
in all relevant dialogs and windows.
2024-12-16 16:15:18 -05:00
Abe Jellinek
4d90fe72bd CookieSandbox: Fix XHR interface check
Actually broken since fx115.
2024-12-16 12:21:55 -05:00
Dan Stillman
e80b5787fe Switch 'dev' channel builds back to main 2024-12-14 01:20:49 -05:00
windingwind
28e7966973 Fix ZoteroPane#buildFieldTransformMenu document variable (#4927)
fix: https://groups.google.com/g/zotero-dev/c/FwfrxviKi8I
2024-12-14 01:16:06 -05:00
abaevbog
b0fab21cc3 fx128: tweak titlebar border color (#4928)
Followup to: #4924
Addresses: #4922

Pre-fx128, --toolbar-field-border-color was ThreeDShadow,
while right now it is --input-border-color, which looks
more prominent.

Setting border color explicitly for ThreeDShadow now would make
the border look a bit blurry, while transparent border looks closest
to pre-fx128 look.
2024-12-14 01:16:06 -05:00
windingwind
42b97d8fcd fx128: dialog window background (#4921)
fix: #4917
2024-12-14 01:16:06 -05:00
abaevbog
d63c39acff fx128: hide 'Run on sites with restrictions' from plugins page (#4925)
Remove 'Run on sites with restrictions' row from Tools > Plugins tab

Fixes: #4885
2024-12-14 01:16:06 -05:00
Dan Stillman
f118d49d24 update-packaging updates from Mozilla 2024-12-14 01:16:05 -05:00
Dan Stillman
1fa34daeef Better fix for filename with spaces in xz_to_bzip
And some associated Mozilla fixes for the same file ("Firefox Media
Plugin Helper")
2024-12-14 01:16:05 -05:00
abaevbog
ee66d2042b fx128: cover transparent titlebar gap on linux (#4924)
Fixes: #4922
2024-12-14 01:16:05 -05:00
Adomas Ven
e3b243ba45 Remove missing-component integration error. (#4923)
This is no longer a thing since word plugins are not Zotero extensions.
If that code throws - it's because we've done something wrong and it's
a bug
2024-12-14 01:16:05 -05:00
Dan Stillman
d580e420d3 Quick fix for inconsistent logging in updates window 2024-12-14 01:16:05 -05:00
Dan Stillman
ca1e9bc467 fx128: Fix app updates 2024-12-14 01:16:05 -05:00
Dan Stillman
967b0c6df4 xz_to_bzip: Wrap filenames in quotes
fx128 has a helper app with spaces in the filename
2024-12-14 01:16:05 -05:00
Dan Stillman
fed4097384 Use fx128 branch for 'dev' channel builds 2024-12-14 01:16:05 -05:00
Dan Stillman
0152769201 Handle dev channel properly in set-channel-prefs-channel 2024-12-14 01:16:04 -05:00
Dan Stillman
fd7e0ec640 Adjust macOS signing
- Only use entitlements when necessary
- Pass list of files from `find` command to single `codesign` call
2024-12-14 01:16:04 -05:00
Dan Stillman
507fd519bb Omit minidump-analyzer and nmhproxy from Firefox on macOS 2024-12-14 01:16:04 -05:00
Dan Stillman
a9e68d9530 fetch_xulrunner: Automatically cache unmodified Linux Firefox 2024-12-14 01:16:04 -05:00
Dan Stillman
468155c548 fx128: Fix broken Help menu on first open on Linux
Only assign a key to "Support and Documentation" on macOS

Fixes #4918
2024-12-14 01:16:04 -05:00
Dan Stillman
43f9d98c16 fx128: Fix Authorization handling in WebDAV test
Caching Basic Auth credentials with an OPTIONS on /zotero no longer
seems to cause Firefox to pass an Authorization header for
/zotero/AAAAAAAA.prop, so update test to send 401 for the .prop request.
(Presumably /zotero/BBBBBBBB.prop would at least still get the Authorization
header without a 401, but I didn't test that.)
2024-12-14 01:16:04 -05:00
Dan Stillman
fa20733353 fx128: Fix login manager for WebDAV
Only `addLogin` → `addLoginAsync` is necessary at this time, but
`findLogins` is deprecated, so switch to `searchLoginsAsync` as well.
2024-12-14 01:16:04 -05:00
Dan Stillman
d6f3acfede fetch_xulrunner: Automatically cache unmodified Mac Firefox
To avoid redownloading from Mozilla whenever fetch_xulrunner runs. We
might want to add cleanup of old versions at some point.

We should add this for Windows and Linux too.
2024-12-14 01:16:04 -05:00
Dan Stillman
7c2720e8fc Fix translate tests after #4903 2024-12-14 01:16:04 -05:00
Dan Stillman
4319b8d78e Revert modifyLoginAsync to modifyLogin 2024-12-14 01:16:03 -05:00
Dan Stillman
a0bdeab401 fx128: Fix login manager
Use async methods (since the sync ones seem to already be broken from JS
despite https://bugzilla.mozilla.org/show_bug.cgi?id=1832756 still being
open) and avoid loading Firefox Accounts module
2024-12-14 01:16:03 -05:00
Dan Stillman
26bcc49174 Use arrays for DataTransfer#types in tree tests
After the fix for #4893

(And fix a bug that was revealed in the process)
2024-12-14 01:16:03 -05:00
Abe Jellinek
d9550bb5df Server: Expose actual bound port (#4903)
And:
- Use a different port for tests so server tests don't break when Zotero
  is already running
- Remove no-op httpServer.enabled pref updates in tests
   - It's already enabled, no test disables it, and any test that did
     would clean up after itself
   - Updating that pref has no effect without a separate call to
     Zotero.Server.init()
- Remove unused arg to Zotero.Server.init()
2024-12-14 01:16:03 -05:00
Abe Jellinek
b858645957 fx128: -moz-nativehyperlinktext -> LinkText (#4902) 2024-12-14 01:16:03 -05:00
Abe Jellinek
67b5545b93 Un-break global-shared.css with new remove_between function (#4901) 2024-12-14 01:16:03 -05:00
windingwind
7833c6fc29 fx128: Fix macOS menupopup style (#4895)
fix: #4880
2024-12-14 01:16:03 -05:00
Abe Jellinek
275fc4a230 Scaffold: Fix off-center toolbar
Addresses #4900
2024-12-14 01:16:03 -05:00
Abe Jellinek
268ba4821c Scaffold: Fix tabbox styling issues on macOS
- Cramped tabs due to insufficient padding (new in fx128)
- Off-center text due to margins (our styles)
- Off-center text due to padding-top (platform styles)
2024-12-14 01:16:02 -05:00
windingwind
a32e327ef8 fx128: Fix Windows menupopup style (#4896) 2024-12-14 01:16:02 -05:00
Dan Stillman
c963e5785f Update Windows build to 128.5.0esr 2024-12-14 01:16:02 -05:00
Abe Jellinek
d271c6c931 fx128: Remove non-native text input styles (#4890) 2024-12-14 01:16:02 -05:00
Abe Jellinek
72b56e7a59 Pass BrowsingContext to nsIFilePicker init
Fixes #4892
2024-12-14 01:16:02 -05:00
Abe Jellinek
f28fa763cc DataTransfer#types: contains() -> includes()
Not sure what this was before, but it's a standard read-only array now.

Fixes #4893
2024-12-14 01:16:02 -05:00
Abe Jellinek
a96b7f1b68 HiddenBrowserTest: Don't test HTML served as attachment
Something funky is going on with Content-Disposition: attachment on
displayable content in fx128. It's correctly blocking the download, but
we're no longer getting a STATE_STOP notification when that happens.
The HTML test didn't really test anything different from the
octet-stream one, so we can just remove it.

Fixes #4881
2024-12-14 01:16:02 -05:00
Dan Stillman
160cc8ba72 Update Firefox build process for fx128 on Windows 2024-12-14 01:16:01 -05:00
Dan Stillman
45e681431b fx128: Remove uses of nsIDOMChromeWindow
https://bugzilla.mozilla.org/show_bug.cgi?id=1522052
2024-12-14 01:16:01 -05:00
Dan Stillman
9606fca3ad fx128: XPCOMUtils.defineLazyGetter → ChromeUtils.defineLazyGetter
Avoids deprecation warning
2024-12-14 01:16:01 -05:00
Dan Stillman
f11fb6c2a0 Update Word submodules to fx128 branches 2024-12-14 01:16:01 -05:00
Dan Stillman
78975f0f2a Update Linux build to 128.5.0esr 2024-12-14 01:16:01 -05:00
Dan Stillman
9b4961a31f Build deployment ZIPs for fx128 branch 2024-12-14 01:16:01 -05:00
Dan Stillman
f341341012 Use custom ChannelPrefs.framework to set Mac update channel
Mac Firefox builds now specify the update channel via a custom macOS
framework:

https://bugzilla.mozilla.org/show_bug.cgi?id=1799332

We set the update channel in the build script, so bundle a custom
ChannelPrefs framework set to "source" and then patch the binary as
necessary for non-source channels. What could go wrong?
2024-12-14 01:16:01 -05:00
Dan Stillman
5c4a0c3b30 Disable copying of XUL for Mac builds 2024-12-14 01:16:01 -05:00
Dan Stillman
187b1649c1 fx128: Handle changed error message for IOUtils.makeDir on broken symlink 2024-12-14 01:16:00 -05:00
Dan Stillman
24bf583b78 fx128: Remove uses of nsIScriptableUnicodeConverter
Replace convertToByteArray() and convertToInputStream()
2024-12-14 01:16:00 -05:00
Dan Stillman
6b3ea14aa1 fx128: Remove try/catch for Services.jsm not being available
Not sure if there are other errors here that we want to ignore, but the
specified one should no longer be an issue.
2024-12-14 01:16:00 -05:00
Dan Stillman
97329752a1 fx128: Remove Services.jsm imports
Now included automatically
2024-12-14 01:16:00 -05:00
Dan Stillman
7f619e3fe3 fx128: nsIOSFileConstantsService is gone 2024-12-14 01:16:00 -05:00
Dan Stillman
3fde2a3e43 Update Mac build to 128.5.0esr 2024-12-14 01:16:00 -05:00
Dan Stillman
b18b15fc08 Update Mozilla patch and mozconfig for fx128 on macOS 2024-12-14 01:16:00 -05:00
Dan Stillman
05ae75d3b4 Update build process for Firefox 128 2024-12-14 01:16:00 -05:00
Dan Stillman
d970f00243 Add check_line function in fetch_xulrunner
For checking whether a line exists without replacing it
2024-12-14 01:15:59 -05:00
Dan Stillman
31bbf2acfa Update locales from Transifex 2024-12-14 01:08:36 -05:00
Dan Stillman
67486965dd Update reader submodule 2024-12-14 00:56:08 -05:00
abaevbog
4196b61dc5
let Zotero_Tabs.undoClose reopen duplicate tabs (#4920)
Fixes: #4919
2024-12-14 00:42:56 -05:00
Abe Jellinek
85deee9631 Update reader submodule 2024-12-11 13:35:10 -05:00
Tom Najdek
d3ef6cfebd
Improve importing from Mendeley
- Make the annotations importer more resilient to missing data
- Fix notebooks not being imported in some cases
- Fix an instance of a hidden browser leaked by the Mendeley Importer
2024-12-11 12:44:13 +01:00
Abe Jellinek
6c437e3df2 Update reader submodule 2024-12-10 15:47:29 -05:00
Dan Stillman
e4f2894424 Remove debug line 2024-12-10 03:06:31 -05:00
Dan Stillman
8c58d887bf Shower nicer error for malformed WebDAV URL
We fix `://` or `//` automatically after #3483, but a leading ':'
character would still show an internal `NS_ERROR_MALFORMED_URI` error.
Instead, just say "[url] is not a valid WebDAV URL".
2024-12-10 03:06:31 -05:00
Dan Stillman
021c004923 Strip leading :// or // in WebDAV URL
Closes #3483
2024-12-10 03:06:31 -05:00
Dan Stillman
64c03792ea Reset file sync history when changing WebDAV URL
Fixes #3535
2024-12-10 03:06:30 -05:00
Dan Stillman
404fc41b88 Clear library storage version when resetting file sync history
Fixes #1753
2024-12-10 00:59:04 -05:00
windingwind
f3d274afeb
Item pane scroll pos fix after switching from note (#4897)
Follow up to #4879
fix: #4276
2024-12-09 02:26:20 -05:00
Dan Stillman
61b034898d Update version 2024-12-06 09:41:59 -05:00
1817 changed files with 228235 additions and 101362 deletions

View file

@ -1,17 +0,0 @@
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/*

View file

@ -1,63 +0,0 @@
{
"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": {}
}

View file

@ -1,9 +0,0 @@
**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.

View file

@ -1,12 +1,88 @@
name: CI name: CI
on: [push, pull_request] on:
push:
pull_request:
workflow_dispatch:
schedule:
# Monthly, to catch runner image and OS changes
- cron: '0 6 1 * *'
concurrency: concurrency:
group: ${{ github.ref }} group: ${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
build: # Decide whether to run the network-filesystem and Windows tests: always for manual and
name: Build, Upload, Test # scheduled runs, and otherwise only when the changes touch the Gecko version, the files
# responsible for network-filesystem database handling, or (for Windows) the files that
# make up the Windows build
changes:
name: Detect changes
runs-on: ubuntu-latest 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: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@ -16,14 +92,9 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 24
#cache: npm #cache: npm
# On GitHub
- name: Install xvfb
if: env.ACT != 'true'
run: sudo apt update && sudo apt install -y xvfb
# Local via act # Local via act
- name: Install packages for act - name: Install packages for act
if: env.ACT == 'true' if: env.ACT == 'true'
@ -45,7 +116,7 @@ jobs:
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: node_modules path: node_modules
key: node-modules-${{ hashFiles('package-lock.json') }} key: node-modules-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules - name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true' if: steps.node-cache.outputs.cache-hit != 'true'
@ -53,45 +124,241 @@ jobs:
- name: Build Zotero - name: Build Zotero
run: npm run build run: npm run build
# Currently necessary for pdf-worker Webpack: https://stackoverflow.com/a/69746937 # Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env: env:
NODE_OPTIONS: --openssl-legacy-provider NODE_OPTIONS: --openssl-legacy-provider
# Somehow forces dpl v1 to be installed, even though v2 is installed without # Create deployment ZIP from the build output, then replace build/ with the
- uses: ruby/setup-ruby@v1 # unzipped contents so that tests run against the same artifact that gets
with: # deployed. This catches problems that only manifest after a zip round-trip
ruby-version: '3.3' # Not needed with a `.ruby-version` or `.tool-versions` # (e.g., resolved symlinks, missing files).
bundler-cache: true # runs 'bundle install' and caches installed gems automatically - 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: | run: |
mkdir build-zip
cd build cd build
zip -r ../build-zip/$GITHUB_SHA.zip * zip -r ../build.zip *
cd .. cd ..
# Once dpl 2 works:
#sudo gem install --no-document dpl - name: Replace build with ZIP contents
#dpl s3 --bucket zotero-download --local_dir build-zip --upload_dir ci/client --acl public_read run: |
sudo gem install --no-document dpl dpl-s3 rm -rf build
dpl --provider=s3 --bucket=zotero-download --local-dir=build-zip --upload-dir=ci/client --acl=public-read --skip_cleanup=true mkdir build
cd build
unzip ../build.zip
- name: Set up SMB share
if: matrix.shard == 'smb'
timeout-minutes: 10
run: |
sudo apt install -y --no-install-recommends samba cifs-utils
sudo mkdir -p /srv/zotero-smb /mnt/zotero-smb
sudo chown $(whoami) /srv/zotero-smb
sudo tee -a /etc/samba/smb.conf > /dev/null <<EOF
[ztest]
path = /srv/zotero-smb
read only = no
EOF
(echo smbtestpass; echo smbtestpass) | sudo smbpasswd -s -a $(whoami)
sudo systemctl restart smbd
sudo mount -t cifs //127.0.0.1/ztest /mnt/zotero-smb \
-o user=$(whoami),pass=smbtestpass,vers=3.0,uid=$(id -u),gid=$(id -g)
- name: Set up NFS share
if: matrix.shard == 'nfs'
timeout-minutes: 10
run: |
sudo apt install -y --no-install-recommends nfs-kernel-server
sudo mkdir -p /srv/zotero-nfs /mnt/zotero-nfs
sudo chown $(whoami) /srv/zotero-nfs
echo "/srv/zotero-nfs localhost(rw,no_subtree_check,insecure)" | sudo tee -a /etc/exports
sudo exportfs -ra
sudo mount -t nfs4 localhost:/srv/zotero-nfs /mnt/zotero-nfs
- name: Run tests - name: Run tests
run: xvfb-run test/runtests.sh -f if: matrix.shard != 'smb' && matrix.shard != 'nfs'
run: xvfb-run test/runtests.sh -f -r 3 -p ${{ matrix.shard }}/4
# The step timeout turns a startup hang into a failure
- name: Run tests on network share
if: matrix.shard == 'smb' || matrix.shard == 'nfs'
timeout-minutes: 10
run: TMPDIR=/mnt/zotero-${{ matrix.shard }} xvfb-run test/runtests.sh -f -r 3 -b db file
# Run the db and file tests on macOS with the test data directory on a loopback NFS
# mount. On macOS, SQLite selects locking methods without shared-memory support on
# network filesystems, which is what the network-filesystem database handling has to
# detect and handle, so this runs the real code path that crashed on SMB/NFS/sshfs
# data directories.
test-mac:
name: Test (macOS NFS)
runs-on: macos-15
timeout-minutes: 30
needs: changes
if: >
needs.changes.outputs.network == 'true'
&& (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository)
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/Firefox.app
key: xulrunner-mac-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p m
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-mac-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
- name: Set up NFS share
run: |
sudo mkdir -p /private/var/zotero-nfs
sudo chown $(whoami) /private/var/zotero-nfs
echo "/private/var/zotero-nfs -mapall=$(whoami) localhost" | sudo tee /etc/exports
sudo nfsd enable || sudo nfsd start
sleep 2
showmount -e localhost
mkdir -p "$HOME/zotero-nfs"
sudo mount_nfs -o vers=3 localhost:/private/var/zotero-nfs "$HOME/zotero-nfs"
# The step timeout turns a startup hang into a failure
- name: Run tests on network share
timeout-minutes: 10
run: TMPDIR="$HOME/zotero-nfs" test/runtests.sh -f -r 3 -b db file
# Boot the Windows build -- which uses custom-built Firefox components (xul.dll) rather
# than stock Mozilla binaries -- and run the db and file tests. A Gecko bump commit
# updates the custom-component hashes in config.sh along with the version, so this
# tests each new set of custom components.
test-win:
name: Test (Windows ${{ matrix.arch }})
runs-on: ${{ matrix.os }}
timeout-minutes: 40
needs: changes
if: >
needs.changes.outputs.windows == 'true'
&& (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository)
strategy:
fail-fast: false
matrix:
include:
- arch: x64
os: windows-latest
- arch: arm64
os: windows-11-arm
defaults:
run:
shell: bash
steps:
# The repository contains symlinks, which Git checks out as plain files on Windows
# by default, breaking the build
- name: Enable symlinks
run: git config --global core.symlinks true
- uses: actions/checkout@v4
with:
submodules: recursive
lfs: true
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
# Git Bash has unzip but not zip, which fetch_xulrunner needs to repack omni.ja
- name: Install zip
run: choco install zip --no-progress -y
- name: Cache xulrunner
id: xulrunner-cache
uses: actions/cache@v4
with:
path: app/xulrunner/firefox-win-${{ matrix.arch }}
key: xulrunner-win-${{ matrix.arch }}-${{ hashFiles('app/config.sh', 'app/scripts/fetch_xulrunner') }}
- name: Fetch xulrunner
if: steps.xulrunner-cache.outputs.cache-hit != 'true'
run: app/scripts/fetch_xulrunner -p w -a ${{ matrix.arch }}
- name: Cache Node modules
id: node-cache
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-win-24-${{ hashFiles('package-lock.json') }}
- name: Install Node modules
if: steps.node-cache.outputs.cache-hit != 'true'
run: npm install
# Committed symlinks point into node_modules, which doesn't exist at checkout time,
# so Git creates the directory ones with the wrong symlink type on Windows. Recreate
# them now that the targets exist.
- name: Recreate symlinks
run: |
git ls-files -s | awk '$1 == 120000 { print $4 }' | xargs rm
git checkout -- .
- name: Build Zotero
run: npm run build
# Currently necessary for document-worker Webpack: https://stackoverflow.com/a/69746937
env:
NODE_OPTIONS: --openssl-legacy-provider
# The step timeout turns a startup hang into a failure
- name: Run tests
timeout-minutes: 15
run: test/runtests.sh -f -r 3 -b -x app/staging/Zotero_win-${{ matrix.arch }}/zotero.exe db file
utilities:
name: Utilities Tests
runs-on: ubuntu-latest
if: >
github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name != github.repository
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 24
- name: Cache utilities Node modules - name: Cache utilities Node modules
id: utilities-node-cache id: utilities-node-cache
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: chrome/content/zotero/xpcom/utilities/node_modules path: chrome/content/zotero/xpcom/utilities/node_modules
key: utilities-node-modules-${{ hashFiles('chrome/content/zotero/xpcom/utilities/package-lock.json') }} key: utilities-node-modules-24-${{ hashFiles('chrome/content/zotero/xpcom/utilities/package-lock.json') }}
- name: Install utilities Node modules - name: Install utilities Node modules
if: steps.utilities-node-cache.outputs.cache-hit != 'true' if: steps.utilities-node-cache.outputs.cache-hit != 'true'
@ -100,3 +367,73 @@ jobs:
- name: Run utilities tests - name: Run utilities tests
run: | run: |
npm test --prefix chrome/content/zotero/xpcom/utilities -- -j resource/schema/global/schema.json 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
View file

@ -18,10 +18,6 @@
path = test/resource/mocha path = test/resource/mocha
url = https://github.com/mochajs/mocha.git url = https://github.com/mochajs/mocha.git
branch = master 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"] [submodule "resource/schema/global"]
path = resource/schema/global path = resource/schema/global
url = https://github.com/zotero/zotero-schema.git url = https://github.com/zotero/zotero-schema.git
@ -34,8 +30,8 @@
url = https://github.com/zotero/reader.git url = https://github.com/zotero/reader.git
branch = master branch = master
[submodule "pdf-worker"] [submodule "pdf-worker"]
path = pdf-worker path = document-worker
url = https://github.com/zotero/pdf-worker.git url = https://github.com/zotero/document-worker.git
branch = master branch = master
[submodule "note-editor"] [submodule "note-editor"]
path = note-editor path = note-editor

110
CLAUDE.md Normal file
View file

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

View file

@ -3,12 +3,12 @@ Vendor=Zotero
Name=Zotero Name=Zotero
Version={{VERSION}} Version={{VERSION}}
BuildID={{BUILDID}} BuildID={{BUILDID}}
Copyright=Copyright (c) 2006-2022 Contributors Copyright=Copyright (c) 2006-2025 Contributors
ID=zotero@zotero.org ID=zotero@zotero.org
[Gecko] [Gecko]
MinVersion=115.0 MinVersion=153.0
MaxVersion=115.99.* MaxVersion=153.99.*
[XRE] [XRE]
EnableExtensionManager=1 EnableExtensionManager=1

View file

@ -4,4 +4,5 @@
-brand-product-name = Zotero -brand-product-name = Zotero
-vendor-short-name = Zotero -vendor-short-name = Zotero
-app-name = Zotero -app-name = Zotero
-subscription-name = Zotero Storage
trademarkInfo = Zotero is a trademark of the Corporation for Digital Scholarship. trademarkInfo = Zotero is a trademark of the Corporation for Digital Scholarship.

View file

@ -50,8 +50,10 @@ if (processTestOptions) {
TestOptions.bail = cmdLine.handleFlag("bail", false); TestOptions.bail = cmdLine.handleFlag("bail", false);
TestOptions.startAt = cmdLine.handleFlagWithParam("startAtTestFile", false); TestOptions.startAt = cmdLine.handleFlagWithParam("startAtTestFile", false);
TestOptions.stopAt = cmdLine.handleFlagWithParam("stopAtTestFile", false); TestOptions.stopAt = cmdLine.handleFlagWithParam("stopAtTestFile", false);
TestOptions.shard = cmdLine.handleFlagWithParam("shard", false);
TestOptions.grep = cmdLine.handleFlagWithParam("grep", false); TestOptions.grep = cmdLine.handleFlagWithParam("grep", false);
TestOptions.timeout = cmdLine.handleFlagWithParam("ZoteroTestTimeout", false); TestOptions.timeout = cmdLine.handleFlagWithParam("ZoteroTestTimeout", false);
TestOptions.retries = cmdLine.handleFlagWithParam("retries", false) || 0;
Services.ww.openWindow( Services.ww.openWindow(
null, null,
@ -68,8 +70,8 @@ if (cmdLine.handleFlag("debugger", false)) {
try { try {
let portOrPath = Services.prefs.getBranch('').getIntPref('devtools.debugger.remote-port'); let portOrPath = Services.prefs.getBranch('').getIntPref('devtools.debugger.remote-port');
const { DevToolsLoader } = ChromeUtils.import( const { DevToolsLoader } = ChromeUtils.importESModule(
"resource://devtools/shared/loader/Loader.jsm" "resource://devtools/shared/loader/Loader.sys.mjs"
); );
const loader = new DevToolsLoader({ const loader = new DevToolsLoader({
freshCompartment: true, freshCompartment: true,

View file

@ -194,7 +194,7 @@ caption {
/* styles for the link elements copied from .text-link in global.css */ /* styles for the link elements copied from .text-link in global.css */
.inline-link { .inline-link {
color: -moz-nativehyperlinktext; color: LinkText;
text-decoration: underline; text-decoration: underline;
} }

View file

@ -6,6 +6,10 @@ pref("javascript.options.showInConsole", true);
// Don't retrieve unrequested links when performing standalone translation // Don't retrieve unrequested links when performing standalone translation
pref("network.prefetch-next", false); 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 // Let operations run as long as necessary
pref("dom.max_chrome_script_run_time", 0); pref("dom.max_chrome_script_run_time", 0);
@ -50,6 +54,14 @@ pref("dom.disable_open_during_load", true);
// scraping the page, since we don't provide any information to the site. // scraping the page, since we don't provide any information to the site.
pref("security.warn_viewing_mixed", false); 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 // Preferences for add-on discovery
pref("extensions.getAddons.cache.enabled", false); pref("extensions.getAddons.cache.enabled", false);
//pref("extensions.getAddons.maxResults", 15); //pref("extensions.getAddons.maxResults", 15);

View file

@ -92,7 +92,7 @@ radio[pane=paneSync] {
/* styles for the link elements copied from .text-link in global.css */ /* styles for the link elements copied from .text-link in global.css */
.inline-link { .inline-link {
color: -moz-nativehyperlinktext; color: LinkText;
text-decoration: underline; text-decoration: underline;
} }

View file

@ -91,7 +91,7 @@ radio[pane=paneSync] {
/* styles for the link elements copied from .text-link in global.css */ /* styles for the link elements copied from .text-link in global.css */
.inline-link { .inline-link {
color: -moz-nativehyperlinktext; color: LinkText;
text-decoration: underline; text-decoration: underline;
} }

View file

@ -20,6 +20,7 @@
CALLDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" CALLDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
. "$CALLDIR/config.sh" . "$CALLDIR/config.sh"
. "$CALLDIR/scripts/utils.sh"
if [ "`uname`" = "Darwin" ]; then if [ "`uname`" = "Darwin" ]; then
MAC_NATIVE=1 MAC_NATIVE=1
@ -34,12 +35,16 @@ fi
function usage { function usage {
cat >&2 <<DONE cat >&2 <<DONE
Usage: $0 [-d DIR] [-f FILE] -p PLATFORMS [-c CHANNEL] [-d] Usage: $0 [-d DIR] [-f FILE] -p PLATFORMS [-a ARCH] [-c CHANNEL] [options]
Options Options
-d DIR build directory to build from (from build_xpi; cannot be used with -f) -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) -f FILE ZIP file to build from (cannot be used with -d)
-t add devtools -t add devtools
-p PLATFORMS build for platforms PLATFORMS (m=Mac, w=Windows, l=Linux) -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 -c CHANNEL use update channel CHANNEL
-e enforce signing -e enforce signing
-s don't package; only build binaries in staging/ directory -s don't package; only build binaries in staging/ directory
@ -54,16 +59,14 @@ function cleanup {
} }
trap cleanup EXIT trap cleanup EXIT
function replace_line { # Copy the contents of a directory into another directory, which may already exist,
pattern=$1 # using rsync if it's available (not on Windows)
replacement=$2 function copy_dir {
file=$3 if command -v rsync > /dev/null; then
rsync -a "$1/" "$2/"
if egrep -q "$pattern" "$file"; then
perl -pi -e "s/$pattern/$replacement/" "$file"
else else
echo "$pattern" not found in "$file" -- aborting 2>&1 mkdir -p "$2"
exit 1 cp -a "$1/." "$2/"
fi fi
} }
@ -86,7 +89,8 @@ BUILD_LINUX=0
PACKAGE=1 PACKAGE=1
DEVTOOLS=0 DEVTOOLS=0
quick_build=0 quick_build=0
while getopts "d:f:p:c:tseq" opt; do arch=""
while getopts "d:f:p:a:c:tseq" opt; do
case $opt in case $opt in
d) d)
SOURCE_DIR="$OPTARG" SOURCE_DIR="$OPTARG"
@ -95,19 +99,21 @@ while getopts "d:f:p:c:tseq" opt; do
ZIP_FILE="$OPTARG" ZIP_FILE="$OPTARG"
;; ;;
p) p)
for i in `seq 0 1 $((${#OPTARG}-1))` for (( i=0; i<${#OPTARG}; i++ )); do
do
case ${OPTARG:i:1} in case ${OPTARG:i:1} in
m) BUILD_MAC=1;; m) BUILD_MAC=1 ;;
w) BUILD_WIN=1;; w) BUILD_WIN=1 ;;
l) BUILD_LINUX=1;; l) BUILD_LINUX=1 ;;
*) *)
echo "$0: Invalid platform option ${OPTARG:i:1}" echo "$0: Invalid platform option ${OPTARG:i:1}" >&2
usage usage
;; ;;
esac esac
done done
;; ;;
a)
arch="$OPTARG"
;;
c) c)
UPDATE_CHANNEL="$OPTARG" UPDATE_CHANNEL="$OPTARG"
;; ;;
@ -130,31 +136,56 @@ while getopts "d:f:p:c:tseq" opt; do
shift $((OPTIND-1)); OPTIND=1 shift $((OPTIND-1)); OPTIND=1
done done
function check_xulrunner_hash { xul_hash_file() {
platform=$1 local _platform=$1
local _arch=$2
if [ $platform == "m" ]; then case $_platform in
platform_hash_file="hash-mac" m)
elif [ $platform == "w" ]; then echo "hash-mac";;
platform_hash_file="hash-win"
elif [ $platform == "l" ]; then w)
platform_hash_file="hash-linux" [[ $_arch == x64 ]] && _arch="win-x64"
else [[ $_arch == arm64 ]] && _arch="win-arm64"
echo "Platform parameter incorrect. Acceptable values: m/w/l" echo "hash-${_arch}";;
l)
[[ $_arch == x64 ]] && _arch="x86_64"
echo "hash-linux-${_arch}";;
*)
echo "Invalid platform '$_platform'" >&2
exit 1 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 fi
if [ ! -e "$CALLDIR/xulrunner/$platform_hash_file" ]; then if [ ! -e "$CALLDIR/xulrunner/$platform_hash_file" ]; then
echo "xulrunner not found -- downloading" echo "$platform_hash_file not found -- downloading"
echo echo
$CALLDIR/scripts/fetch_xulrunner -p $platform $CALLDIR/scripts/fetch_xulrunner -p $_platform ${_arch:+-a $_arch}
else else
recalculated_xulrunner_hash=$("$CALLDIR/scripts/xulrunner_hash" -p $platform) recalculated_xulrunner_hash=$("$CALLDIR/scripts/xulrunner_hash" -p $_platform ${_arch:+-a $_arch})
current_xulrunner_hash=$(< "$CALLDIR/xulrunner/$platform_hash_file") 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 "xulrunner hashes don't match -- redownloading"
echo echo
"$CALLDIR/scripts/fetch_xulrunner" -p $platform "$CALLDIR/scripts/fetch_xulrunner" -p $_platform ${_arch:+-a $_arch}
current_xulrunner_hash=$(< "$CALLDIR/xulrunner/$platform_hash_file") 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 after running fetch_xulrunner!" echo "xulrunner hashes don't match after running fetch_xulrunner!"
@ -164,15 +195,36 @@ function check_xulrunner_hash {
fi fi
} }
#Check if xulrunner and GECKO_VERSION for each platform match # 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
if [ $BUILD_MAC == 1 ]; then if [ $BUILD_MAC == 1 ]; then
check_xulrunner_hash m check_xulrunner_hash m
fi fi
if [ $BUILD_WIN == 1 ]; then if [ $BUILD_WIN == 1 ]; then
check_xulrunner_hash w 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
fi fi
if [ $BUILD_LINUX == 1 ]; then if [ $BUILD_LINUX == 1 ]; then
check_xulrunner_hash l 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
fi fi
@ -205,7 +257,7 @@ BUILD_ID=`date +%Y%m%d%H%M%S`
# Paths to Gecko runtimes # Paths to Gecko runtimes
MAC_RUNTIME_PATH="$CALLDIR/xulrunner/Firefox.app" MAC_RUNTIME_PATH="$CALLDIR/xulrunner/Firefox.app"
WIN_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-" WIN_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-"
LINUX_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-" LINUX_RUNTIME_PATH_PREFIX="$CALLDIR/xulrunner/firefox-linux-"
base_dir="$BUILD_DIR/base" base_dir="$BUILD_DIR/base"
app_dir="$BUILD_DIR/base/app" app_dir="$BUILD_DIR/base/app"
@ -233,17 +285,96 @@ set +e
if [ $BUILD_MAC == 1 ]; then if [ $BUILD_MAC == 1 ]; then
cp -Rp "$MAC_RUNTIME_PATH"/Contents/Resources/browser/omni "$app_dir" 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/ unzip -qj "$MAC_RUNTIME_PATH"/Contents/Resources/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/
elif [ $BUILD_WIN == 1 ]; then
# Non-arch-specific files, so just use 64-bit version elif [[ $BUILD_WIN == 1 ]]; then
cp -Rp "${WIN_RUNTIME_PATH_PREFIX}win-x64"/browser/omni "$app_dir" # These are non-arch-specific files, so just find a runtime we fetched:
unzip -qj "${WIN_RUNTIME_PATH_PREFIX}win-x64"/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/ # - If the caller specified -a, that arch is guaranteed present.
elif [ $BUILD_LINUX == 1 ]; then # - Otherwise prefer win-x64 → win-arm64 → win32, falling back to the first
# Non-arch-specific files, so just use 64-bit version # directory that exists under xulrunner/.
cp -Rp "${LINUX_RUNTIME_PATH_PREFIX}x86_64"/browser/omni "$app_dir" if [[ -n $arch ]]; then
unzip -qj "${LINUX_RUNTIME_PATH_PREFIX}x86_64"/omni.ja "hyphenation/*" -d "$app_dir"/hyphenation/ 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/
fi fi
set -e set -e
cd $omni_dir 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 # Move some Firefox files that would be overwritten out of the way
mv chrome.manifest chrome.manifest-fx mv chrome.manifest chrome.manifest-fx
mv defaults defaults-fx mv defaults defaults-fx
@ -254,11 +385,18 @@ if [ -n "$ZIP_FILE" ]; then
echo "Building from $ZIP_FILE" echo "Building from $ZIP_FILE"
unzip -q $ZIP_FILE -d "$omni_dir" unzip -q $ZIP_FILE -d "$omni_dir"
else else
if command -v rsync > /dev/null; then
rsync_params="" rsync_params=""
if [ $include_tests -eq 0 ]; then if [ $include_tests -eq 0 ]; then
rsync_params="--exclude /test" rsync_params="--exclude /test"
fi fi
rsync -a $rsync_params "$SOURCE_DIR/" ./ rsync -a $rsync_params "$SOURCE_DIR/" ./
else
copy_dir "$SOURCE_DIR" .
if [ $include_tests -eq 0 ]; then
rm -rf ./test
fi
fi
fi fi
mv defaults defaults-z mv defaults defaults-z
@ -270,7 +408,7 @@ prefs_file=defaults/preferences/zotero.js
# - network.captive-portal-service.enabled # - network.captive-portal-service.enabled
# Disable the captive portal check against Mozilla servers # Disable the captive portal check against Mozilla servers
# - extensions.systemAddon.update.url # - extensions.systemAddon.update.url
egrep -v '(network.captive-portal-service.enabled|extensions.systemAddon.update.url)' defaults/preferences/firefox.js > $prefs_file grep -E -v '(network.captive-portal-service.enabled|extensions.systemAddon.update.url)' defaults/preferences/firefox.js > $prefs_file
rm defaults/preferences/firefox.js rm defaults/preferences/firefox.js
# Combine app and "extension" Zotero prefs # Combine app and "extension" Zotero prefs
@ -284,6 +422,8 @@ echo "" >> $prefs_file
echo "# Zotero extension prefs" >> $prefs_file echo "# Zotero extension prefs" >> $prefs_file
echo "" >> $prefs_file echo "" >> $prefs_file
cat defaults-z/preferences/zotero.js >> $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 rm -rf defaults-z
@ -305,7 +445,7 @@ elif [ $BUILD_LINUX == 1 ]; then
perl -pi -e 's/pref\("general\.autoScroll", false\);/pref\("general.autoScroll", true);/' $prefs_file perl -pi -e 's/pref\("general\.autoScroll", false\);/pref\("general.autoScroll", true);/' $prefs_file
fi fi
# Clear list of built-in add-ons # Clear list of built-in add-ons, but leave the en-US dictionary
echo '{"dictionaries": {"en-US": "dictionaries/en-US.dic"}, "system": []}' > chrome/browser/content/browser/built_in_addons.json echo '{"dictionaries": {"en-US": "dictionaries/en-US.dic"}, "system": []}' > chrome/browser/content/browser/built_in_addons.json
# chrome.manifest # chrome.manifest
@ -336,6 +476,58 @@ if [ $DEVTOOLS -eq 1 ]; then
replace_line 'let command = Services.dirsvc.get\("XREExeF", Ci.nsIFile\).path;' \ 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");' \ '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 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 fi
# 5.0.96.3 / 5.0.97-beta.37+ddc7be75c # 5.0.96.3 / 5.0.97-beta.37+ddc7be75c
@ -363,6 +555,7 @@ for locale in `ls chrome/locale/`; do
cp "$CALLDIR/assets/branding/locale/brand.ftl" localization/$locale/branding/brand.ftl cp "$CALLDIR/assets/branding/locale/brand.ftl" localization/$locale/branding/brand.ftl
mkdir -p localization/$locale/toolkit/global 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/textActions.ftl localization/$locale/toolkit/global
cp chrome/locale/$locale/zotero/mozilla/wizard.ftl localization/$locale/toolkit/global cp chrome/locale/$locale/zotero/mozilla/wizard.ftl localization/$locale/toolkit/global
@ -385,10 +578,11 @@ cat "$CALLDIR/assets/chrome.manifest" >> chrome.manifest
replace_line 'handle: function bch_handle\(cmdLine\) {' 'handle: function bch_handle(cmdLine) { replace_line 'handle: function bch_handle\(cmdLine\) {' 'handle: function bch_handle(cmdLine) {
\/\/ TEST_OPTIONS_PLACEHOLDER \/\/ TEST_OPTIONS_PLACEHOLDER
' modules/BrowserContentHandler.sys.mjs ' modules/BrowserContentHandler.sys.mjs
# prevent color scheme getting reset to 'light' during printing
replace_line 'new LightweightThemeConsumer\(document\);' '\/\/new LightweightThemeConsumer\(document\);' chrome/browser/content/browser/browser.js
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 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 # Move test files to root directory
if [ $include_tests -eq 1 ]; then if [ $include_tests -eq 1 ]; then
cat test/chrome.manifest >> chrome.manifest cat test/chrome.manifest >> chrome.manifest
@ -398,11 +592,11 @@ fi
# Copy platform-specific assets # Copy platform-specific assets
if [ $BUILD_MAC == 1 ]; then if [ $BUILD_MAC == 1 ]; then
rsync -a "$CALLDIR/assets/mac/" ./ copy_dir "$CALLDIR/assets/mac" .
elif [ $BUILD_WIN == 1 ]; then elif [ $BUILD_WIN == 1 ]; then
rsync -a "$CALLDIR/assets/win/" ./ copy_dir "$CALLDIR/assets/win" .
elif [ $BUILD_LINUX == 1 ]; then elif [ $BUILD_LINUX == 1 ]; then
rsync -a "$CALLDIR/assets/unix/" ./ copy_dir "$CALLDIR/assets/unix" .
fi fi
# Add word processor plug-ins # Add word processor plug-ins
@ -500,16 +694,25 @@ if [ $BUILD_MAC == 1 ]; then
# Merge relevant assets from Firefox # Merge relevant assets from Firefox
mkdir "$CONTENTSDIR/MacOS" mkdir "$CONTENTSDIR/MacOS"
cp -r "$MAC_RUNTIME_PATH/Contents/MacOS/"!(firefox|firefox-bin|crashreporter.app|pingsender|updater.app) "$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|browser|defaults|precomplete|removed-files|updater.ini|update-settings.ini|webapprt*|*.icns|*.lproj) "$CONTENTSDIR/Resources" 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
# Use our own launcher # Use our own launcher
check_lfs_file "$CALLDIR/mac/zotero.xz" check_lfs_file "$CALLDIR/mac/zotero.xz"
xz -d --stdout "$CALLDIR/mac/zotero.xz" > "$CONTENTSDIR/MacOS/zotero" xz -d --stdout "$CALLDIR/mac/zotero.xz" > "$CONTENTSDIR/MacOS/zotero"
chmod 755 "$CONTENTSDIR/MacOS/zotero" chmod 755 "$CONTENTSDIR/MacOS/zotero"
# TEMP: Custom version of XUL with some backported Mozilla bug fixes # Overlay custom Firefox components (e.g., XUL or libmozglue.dylib)
cp "$MAC_RUNTIME_PATH/../MacOS/XUL" "$CONTENTSDIR/MacOS/" if [ -n "$custom_components_hash_mac" ]; then
cp -R "$MAC_RUNTIME_PATH/../MacOS/." "$CONTENTSDIR/MacOS/"
fi
# Use our own updater, because Mozilla's requires updates signed by Mozilla # Use our own updater, because Mozilla's requires updates signed by Mozilla
cd "$CONTENTSDIR/MacOS" cd "$CONTENTSDIR/MacOS"
@ -536,7 +739,7 @@ if [ $BUILD_MAC == 1 ]; then
echo echo
# Copy app files # Copy app files
rsync -a "$base_dir/" "$CONTENTSDIR/Resources/" copy_dir "$base_dir" "$CONTENTSDIR/Resources"
# Add word processor plug-ins # Add word processor plug-ins
mkdir "$CONTENTSDIR/Resources/integration" mkdir "$CONTENTSDIR/Resources/integration"
@ -547,12 +750,66 @@ if [ $BUILD_MAC == 1 ]; then
find "$CONTENTSDIR" -depth -type d -name .git -exec rm -rf {} \; find "$CONTENTSDIR" -depth -type d -name .git -exec rm -rf {} \;
find "$CONTENTSDIR" \( -name .DS_Store -or -name update.rdf \) -exec rm -f {} \; find "$CONTENTSDIR" \( -name .DS_Store -or -name update.rdf \) -exec rm -f {} \;
# Add Safari App Extension -- this depends on signing but needs to be done before generating # Add Safari extensions -- this depends on signing but needs to be done before generating
# the precomplete file # 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 [[ $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" mkdir "$APPDIR/Contents/PlugIns"
cp -R $SAFARI_APPEX "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex"
rm -rf "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Resources/safari/test/" 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
fi fi
# Copy over removed-files and make a precomplete file # Copy over removed-files and make a precomplete file
@ -574,45 +831,50 @@ if [ $BUILD_MAC == 1 ]; then
# Clear extended attributes, which can cause codesign to fail # Clear extended attributes, which can cause codesign to fail
/usr/bin/xattr -cr "$APPDIR" /usr/bin/xattr -cr "$APPDIR"
# Sign app # Sign libraries and updater without entitlements
entitlements_file="$CALLDIR/mac/entitlements.xml" /usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" \
/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" \
"$APPDIR/Contents/MacOS/XUL" \ "$APPDIR/Contents/MacOS/XUL" \
"$APPDIR/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater" "$APPDIR/Contents/MacOS/updater.app/Contents/Frameworks/UpdateSettings.framework" \
find "$APPDIR/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \; "$APPDIR/Contents/MacOS/updater.app" \
find "$APPDIR/Contents" -name '*.app' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \; "$APPDIR/Contents/Frameworks/ChannelPrefs.framework"
/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" "$APPDIR/Contents/MacOS/zotero" find "$APPDIR/Contents" -name '*.dylib' -print0 | xargs -0 /usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID"
# Sign bundled apps and main app bundle
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"
# Sign .jnilib (Java native shared library) within LibreOffice extension, since notarization # 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 # started failing without this. The .jnilib is within a .jar within the .oxt, so we have to
# extract both, sign the library, and then update each ZIP. # extract both, sign the libraries, and then update each ZIP.
#
# 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" pushd "$BUILD_DIR"
mkdir libreoffice-repack mkdir libreoffice-repack
cd libreoffice-repack cd libreoffice-repack
unzip -q "$APPDIR/Contents/Resources/integration/libreoffice/Zotero_LibreOffice_Integration.oxt" external_jars/jna.jar 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 unzip -q external_jars/jna.jar 'com/sun/jna/darwin*/libjnidispatch.jnilib'
/usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" 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 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 zip -u "$APPDIR/Contents/Resources/integration/libreoffice/Zotero_LibreOffice_Integration.oxt" external_jars/jna.jar
cd .. cd ..
rm -rf libreoffice-repack rm -rf libreoffice-repack
popd popd
# Sign Safari App Extension # Sign Safari extensions
# #
# Even though it's signed by Xcode, we sign it again to make sure it matches the parent app signature # Even though they're signed by Xcode, we sign them again to make sure they match the parent app signature
if [ -d "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex" ]; then for appex in "$APPDIR"/Contents/PlugIns/*.appex; do
if [ ! -d "$appex" ]; then
continue
fi
echo echo
# Extract entitlements, which differ from parent app # Extract entitlements, which differ from parent app
/usr/bin/codesign -d --entitlements "$BUILD_DIR/safari-entitlements.plist" --xml "$SAFARI_APPEX" /usr/bin/codesign -d --entitlements "$BUILD_DIR/safari-entitlements.plist" --xml "$appex"
# Add suffix to appex bundle identifier find "$appex/Contents" -name '*.dylib' -exec /usr/bin/codesign --force --options runtime --entitlements "$entitlements_file" --sign "$DEVELOPER_ID" {} \;
if [ $UPDATE_CHANNEL == "beta" ] || [ $UPDATE_CHANNEL == "dev" ] || [ $UPDATE_CHANNEL == "source" ]; then /usr/bin/codesign --force --options runtime --entitlements "$BUILD_DIR/safari-entitlements.plist" --sign "$DEVELOPER_ID" "$appex"
perl -pi -e "s/org\.zotero\.SafariExtensionApp\.SafariExtension/org.zotero.SafariExtensionApp.SafariExtension-$UPDATE_CHANNEL/" "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex/Contents/Info.plist" done
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 # Sign final app package
echo echo
@ -620,11 +882,21 @@ if [ $BUILD_MAC == 1 ]; then
# Verify app # Verify app
/usr/bin/codesign --verify -vvvv "$APPDIR" /usr/bin/codesign --verify -vvvv "$APPDIR"
# Verify Safari App Extension # Verify Safari extensions
if [[ -n "$SAFARI_APPEX" ]] && [[ -d "$SAFARI_APPEX" ]]; then for appex in "$APPDIR"/Contents/PlugIns/*.appex; do
echo if [ ! -d "$appex" ]; then
/usr/bin/codesign --verify -vvvv "$APPDIR/Contents/PlugIns/ZoteroSafariExtension.appex" continue
fi fi
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 -
fi fi
# Build and notarize disk image # Build and notarize disk image
@ -679,10 +951,15 @@ if [ $BUILD_WIN == 1 ]; then
fi fi
for arch in win32 win-x64 win-arm64; do if [[ -n $arch ]]; then
archs=("$(get_canonical_arch w $arch)");
else
archs=(win-x64 win-arm64 win32);
fi
for arch in "${archs[@]}"; do
echo "Building Zotero_$arch" echo "Building Zotero_$arch"
runtime_path="${WIN_RUNTIME_PATH_PREFIX}${arch}" runtime_path="${WIN_RUNTIME_PATH_PREFIX}$arch"
# Set up directory # Set up directory
APPDIR="$STAGE_DIR/Zotero_$arch" APPDIR="$STAGE_DIR/Zotero_$arch"
@ -692,7 +969,7 @@ if [ $BUILD_WIN == 1 ]; then
# #
# 'i686' is a huge directory containing x86 versions of xul.dll and other files in # '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 # Firefox ARM64 builds for use with the EME DRM plugins
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" 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"
# Copy zotero.exe, which is built directly from Firefox source and then modified by # Copy zotero.exe, which is built directly from Firefox source and then modified by
# ResourceHacker to add icons # ResourceHacker to add icons
@ -717,11 +994,11 @@ if [ $BUILD_WIN == 1 ]; then
fi fi
# Copy app files # Copy app files
rsync -a "$base_dir/" "$APPDIR/" copy_dir "$base_dir" "$APPDIR"
#mv "$APPDIR/app/application.ini" "$APPDIR/" #mv "$APPDIR/app/application.ini" "$APPDIR/"
# Copy in common files # Copy in common files
rsync -a "$COMMON_APPDIR/" "$APPDIR/" copy_dir "$COMMON_APPDIR" "$APPDIR"
cat "$CALLDIR/win/installer/updater_append.ini" >> "$APPDIR/updater.ini" cat "$CALLDIR/win/installer/updater_append.ini" >> "$APPDIR/updater.ini"
@ -860,14 +1137,14 @@ fi
# Linux # Linux
if [ $BUILD_LINUX == 1 ]; then if [ $BUILD_LINUX == 1 ]; then
# Skip 32-bit build in tests if [[ -n $arch ]]; then
if [[ "${ZOTERO_TEST:-}" = "1" ]] || [[ "${SKIP_32:-}" = "1" ]]; then archs=("$(get_canonical_arch l $arch)")
archs="x86_64"
else else
archs="i686 x86_64" archs=(x64 arm64)
fi fi
for arch in "${archs[@]}"; do
[[ $arch == x64 ]] && arch="x86_64"
for arch in $archs; do
runtime_path="${LINUX_RUNTIME_PATH_PREFIX}${arch}" runtime_path="${LINUX_RUNTIME_PATH_PREFIX}${arch}"
# Set up directory # Set up directory
@ -877,7 +1154,7 @@ if [ $BUILD_LINUX == 1 ]; then
mkdir "$APPDIR" mkdir "$APPDIR"
# Merge relevant assets from Firefox # Merge relevant assets from Firefox
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" 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"
# Use our own launcher that calls the original Firefox executable with -app # Use our own launcher that calls the original Firefox executable with -app
mv "$APPDIR"/firefox-bin "$APPDIR"/zotero-bin mv "$APPDIR"/firefox-bin "$APPDIR"/zotero-bin
@ -893,12 +1170,14 @@ if [ $BUILD_LINUX == 1 ]; then
chmod 755 "$APPDIR/updater" chmod 755 "$APPDIR/updater"
# Copy app files # Copy app files
rsync -a "$base_dir/" "$APPDIR/" copy_dir "$base_dir" "$APPDIR"
# Add word processor plug-ins # Add word processor plug-ins
mkdir "$APPDIR/integration" mkdir "$APPDIR/integration"
cp -RH "$CALLDIR/modules/zotero-libreoffice-integration/install" "$APPDIR/integration/libreoffice" 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 # Copy icons
cp "$CALLDIR/linux/icons/icon32.png" "$APPDIR/icons/" cp "$CALLDIR/linux/icons/icon32.png" "$APPDIR/icons/"
cp "$CALLDIR/linux/icons/icon64.png" "$APPDIR/icons/" cp "$CALLDIR/linux/icons/icon64.png" "$APPDIR/icons/"
@ -921,9 +1200,9 @@ if [ $BUILD_LINUX == 1 ]; then
if [ $PACKAGE == 1 ]; then if [ $PACKAGE == 1 ]; then
# Create tar # Create tar
rm -f "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.bz2" rm -f "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.xz"
cd "$STAGE_DIR" cd "$STAGE_DIR"
tar -cjf "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.bz2" "Zotero_linux-$arch" tar -cJf "$DIST_DIR/Zotero-${VERSION}_linux-$arch.tar.xz" "Zotero_linux-$arch"
fi fi
done done
fi fi

View file

@ -1,14 +1,17 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Version of Gecko to build with # Version of Gecko to build with
GECKO_VERSION_MAC="115.9.1esr" GECKO_VERSION_MAC="153.3.0esr"
GECKO_VERSION_LINUX="115.14.0esr" GECKO_VERSION_LINUX="153.3.0esr"
GECKO_VERSION_WIN="115.14.0esr" GECKO_VERSION_WIN="153.3.0esr"
RUST_VERSION=1.69.0 RUST_VERSION=1.94.0
# URL prefix for custom builds of Firefox components # URL prefix for custom builds of Firefox components
custom_components_url="https://download.zotero.org/dev/firefox-components/" custom_components_url="https://download.zotero.org/dev/firefox-components/"
custom_components_hash_mac="bec8e3adebf8d5021f1f35fd2c65d752d4979839cbdd1ee1aa4b1d3d5ba0953b" custom_components_hash_mac="6b586ed383a480fed9d9786ad18a6e6d90b140739e5e4491096df291ec887571"
custom_components_hash_win_x64="db7c9556bd0f9e36120069391ce66cc199ed1ec4e2fdd3b931f71e35b4586d2b"
custom_components_hash_win_arm64="fd0f44b38a471009ce1cdd926f7f48be43ed773dad350c62383b8a7235da8711"
custom_components_hash_win32="694944349fdfd91296f272a68ac5d03183f02d47fdaa7ba9f2b1c74747652aa2"
APP_NAME="Zotero" APP_NAME="Zotero"
APP_ID="zotero\@zotero.org" APP_ID="zotero\@zotero.org"
@ -25,6 +28,9 @@ NOTARIZATION_BUNDLE_ID=""
NOTARIZATION_USER="" NOTARIZATION_USER=""
NOTARIZATION_TEAM_ID="" NOTARIZATION_TEAM_ID=""
NOTARIZATION_PASSWORD="" 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 # Paths for Windows installer build
NSIS_DIR='C:\Program Files (x86)\NSIS\' NSIS_DIR='C:\Program Files (x86)\NSIS\'
@ -42,8 +48,7 @@ S3_CI_ZIP_PATH="ci/client"
S3_DIST_PATH="client" S3_DIST_PATH="client"
DEPLOY_HOST="deploy.zotero" DEPLOY_HOST="deploy.zotero"
DEPLOY_PATH="www/www-production/public/download/client/manifests" DEPLOY_PATH="www/www-production/public/download/client"
DEPLOY_CMD="ssh $DEPLOY_HOST update-site-files"
BUILD_PLATFORMS="" BUILD_PLATFORMS=""
NUM_INCREMENTALS=6 NUM_INCREMENTALS=6

44
app/linux/build Executable file
View file

@ -0,0 +1,44 @@
#!/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

View file

@ -1,6 +1,3 @@
# Uncomment to build 32-bit
#ac_add_options --target=i686
ac_add_options --enable-bootstrap ac_add_options --enable-bootstrap
mk_add_options AUTOCLOBBER=1 mk_add_options AUTOCLOBBER=1
@ -15,7 +12,8 @@ ac_add_options --disable-tests
ac_add_options --disable-debug ac_add_options --disable-debug
ac_add_options --disable-debug-symbols ac_add_options --disable-debug-symbols
ac_add_options --disable-webrtc ac_add_options --disable-webrtc
ac_add_options --disable-eme # This causes aarch64 build of 140.0esr to fail
#ac_add_options --disable-eme
export MOZILLA_OFFICIAL=1 export MOZILLA_OFFICIAL=1
export RELEASE_OR_BETA=1 export RELEASE_OR_BETA=1
@ -24,4 +22,4 @@ MOZ_REQUIRE_SIGNING=
ac_add_options --enable-official-branding ac_add_options --enable-official-branding
# Build updater without MAR signature verification # Build updater without MAR signature verification
ac_add_options --disable-verify-mar ac_add_options --enable-unverified-updates

43
app/linux/update_updater Executable file
View file

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

View file

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

Binary file not shown.

View file

@ -161,8 +161,8 @@
<string>zotero</string> <string>zotero</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>Zotero {{VERSION}}, © 2006-2018 Contributors</string> <string>Zotero {{VERSION}}, © 2006-2018 Contributors</string>
<key>CFBundleIconFile</key> <key>CFBundleIconName</key>
<string>zotero</string> <string>AppIcon</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>org.zotero.zotero</string> <string>org.zotero.zotero</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>

Binary file not shown.

View file

@ -9,10 +9,10 @@ fx_app_name=Firefox.app
# Get Mozilla source directory from command line # Get Mozilla source directory from command line
if [ -z "${1:-}" ]; then if [ -z "${1:-}" ]; then
echo "Usage: $0 /path/to/mozilla-unified" >&2 echo "Usage: $0 /path/to/firefox" >&2
exit 1 exit 1
fi fi
GECKO_PATH=$1 GECKO_PATH="$(cd "$1" && pwd)"
mach=$GECKO_PATH/mach mach=$GECKO_PATH/mach
BUILD_DIR=`mktemp -d` BUILD_DIR=`mktemp -d`
@ -27,8 +27,7 @@ export MOZ_BUILD_DATE=`date "+%Y%m%d%H%M%S"`
# Install required Rust version # Install required Rust version
rustup toolchain install $RUST_VERSION rustup toolchain install $RUST_VERSION
rustup target add aarch64-apple-darwin rustup target add --toolchain $RUST_VERSION aarch64-apple-darwin x86_64-apple-darwin
rustup target add x86_64-apple-darwin
rustup default $RUST_VERSION rustup default $RUST_VERSION
cp "$SCRIPT_DIR/mozconfig" "$GECKO_PATH" cp "$SCRIPT_DIR/mozconfig" "$GECKO_PATH"
@ -56,8 +55,36 @@ cp x64/$fx_app_name/Contents/MacOS/firefox zotero
xz zotero xz zotero
mv zotero.xz "$APP_ROOT_DIR/mac/zotero.xz" mv zotero.xz "$APP_ROOT_DIR/mac/zotero.xz"
# TEMP: Copy a unified XUL for now # Package custom Firefox components for fetch_xulrunner. Add XUL to this list if needed.
cp x64/$fx_app_name/Contents/MacOS/XUL "$APP_ROOT_DIR/mac/XUL" # Upload the archive to ${custom_components_url}mac/ and set custom_components_hash_mac
# in config.sh to the hash printed below.
custom_components=(libmozglue.dylib)
mkdir MacOS
for component in "${custom_components[@]}"; do
cp "x64/$fx_app_name/Contents/MacOS/$component" MacOS/
done
tar cfJ MacOS.tar.xz MacOS
components_hash=$(shasum -a 256 MacOS.tar.xz | cut -d ' ' -f 1)
components_file="$APP_ROOT_DIR/mac/$components_hash.tar.xz"
mv MacOS.tar.xz "$components_file"
echo "Custom Firefox components: $components_file"
echo "custom_components_hash_mac=\"$components_hash\""
# Save a copy of ChannelPrefs.framework
#
# Frameworks need to be signed, and we can't sign source builds, so store a signed copy with
# 'default' replaced with 'source'. Deployed builds will then replace 'source' with
# 'release'/'beta'/'dev'.
rm -rf "$APP_ROOT_DIR/mac/ChannelPrefs.framework"
cp -R x64/$fx_app_name/Contents/Frameworks/ChannelPrefs.framework "$APP_ROOT_DIR/mac/ChannelPrefs.framework"
pushd "$APP_ROOT_DIR/mac"
./set-channel-prefs-channel ChannelPrefs.framework/ChannelPrefs default source
security -v unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/$KEYCHAIN.keychain-db
/usr/bin/codesign --force --options runtime --sign "$DEVELOPER_ID" ChannelPrefs.framework
/usr/bin/codesign -vvvv ChannelPrefs.framework
tar cfvJ ChannelPrefs.framework.tar.xz ChannelPrefs.framework
rm -rf ChannelPrefs.framework
popd
# Uncomment to build updater # Uncomment to build updater
#cd x64/$fx_app_name/Contents/MacOS/ #cd x64/$fx_app_name/Contents/MacOS/

View file

@ -4,7 +4,7 @@ elif [ "$Z_ARCH" == "aarch64" ]; then
ac_add_options --target=aarch64-apple-darwin ac_add_options --target=aarch64-apple-darwin
fi fi
ac_add_options --enable-bootstrap ac_add_options --enable-bootstrap
ac_add_options --with-macos-sdk=$HOME/tmp/MacOSX13.3.sdk ac_add_options --with-macos-sdk=$HOME/src/MacOSX26.5.sdk
mk_add_options AUTOCLOBBER=1 mk_add_options AUTOCLOBBER=1
@ -24,6 +24,11 @@ export MOZILLA_OFFICIAL=1
export RELEASE_OR_BETA=1 export RELEASE_OR_BETA=1
MOZ_REQUIRE_SIGNING= 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 ac_add_options --enable-official-branding
# Build updater without MAR signature verification # Build updater without MAR signature verification

242
app/mac/mozilla-153.patch Normal file
View file

@ -0,0 +1,242 @@
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

View file

@ -303,6 +303,7 @@ my(@gCleanup, %gConfig, $gDarwinMajor, $gDryRun, $gVerbosity);
'cmd_chmod' => 'chmod', 'cmd_chmod' => 'chmod',
'cmd_diskutil' => 'diskutil', 'cmd_diskutil' => 'diskutil',
'cmd_du' => 'du', 'cmd_du' => 'du',
'cmd_find' => 'find',
'cmd_hdid' => 'hdid', 'cmd_hdid' => 'hdid',
'cmd_hdiutil' => 'hdiutil', 'cmd_hdiutil' => 'hdiutil',
'cmd_mkdir' => 'mkdir', 'cmd_mkdir' => 'mkdir',
@ -311,6 +312,7 @@ my(@gCleanup, %gConfig, $gDarwinMajor, $gDryRun, $gVerbosity);
'cmd_rm' => 'rm', 'cmd_rm' => 'rm',
'cmd_rsync' => 'rsync', 'cmd_rsync' => 'rsync',
'cmd_SetFile' => '/Developer/Tools/SetFile', 'cmd_SetFile' => '/Developer/Tools/SetFile',
'cmd_xattr' => 'xattr',
# create_directly indicates whether hdiutil create supports # create_directly indicates whether hdiutil create supports
# -srcfolder and -srcdevice. It does on >= 10.3 (Panther). # -srcfolder and -srcdevice. It does on >= 10.3 (Panther).
@ -996,6 +998,65 @@ sub diskImageMaker($$$$$$$$) {
cleanupDie('unlink hybridImage failed: '.$!); 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 { else {
# makehybrid is not available, fall back to making a UDRW and # makehybrid is not available, fall back to making a UDRW and

View file

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

View file

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

Binary file not shown.

@ -1 +1 @@
Subproject commit 39a4b0586c110d9ed42561295bc36e4e0383c793 Subproject commit 2e78b1010ad49b4dde70ec9f0094b34a7b6f8965

@ -1 +1 @@
Subproject commit 13c92c090c12f6555bf6bab98780025e93d75077 Subproject commit 300886d1984de7a0006d7a48310baa0c8c99bea0

@ -1 +1 @@
Subproject commit 9883ac3cdadb5e452b17750230f1b305164d169d Subproject commit c0aa6e4bef039d94e17e81cb28b1fe9170c45b96

View file

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

View file

@ -1,30 +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"
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

View file

@ -1,27 +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"
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

View file

@ -1,29 +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="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

View file

@ -1,27 +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"
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

View file

@ -37,11 +37,14 @@ win_path="$STAGE_DIR/Zotero_win-x64"
linux_path="$STAGE_DIR/Zotero_linux-x86_64" linux_path="$STAGE_DIR/Zotero_linux-x86_64"
added=0 added=0
build_id=$(date +%Y%m%d%H%M%S)
for path in "$mac_path" "$win_path" "$linux_path"; do for path in "$mac_path" "$win_path" "$linux_path"; do
if [ -d "$path" ]; then if [ -d "$path" ]; then
echo "$path/app/omni.ja" echo "$path/app/omni.ja"
echo "Updating $(basename $(dirname $(dirname $path)))" echo "Updating $(basename $(dirname $(dirname $path)))"
zip "$path/app/omni.ja" $files 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 added=1
fi fi
done done

View file

@ -4,6 +4,7 @@ Update a builds manifest with info on a given build
""" """
import argparse import argparse
import os import os
import re
import sys import sys
import shutil import shutil
import json import json
@ -25,7 +26,7 @@ def main():
try: try:
file = args.file file = args.file
version = args.version version = args.version
short_version = version[0:3] short_version = re.match(r'\d+\.\d+', version).group(0)
# Back up JSON file # Back up JSON file
shutil.copy2(file, file + '.bak') shutil.copy2(file, file + '.bak')

View file

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

View file

@ -1,14 +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
}

View file

@ -5,17 +5,28 @@ APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname $APP_ROOT_DIR)" ROOT_DIR="$(dirname $APP_ROOT_DIR)"
# Set ZOTERO_PROFILE environment variable to choose profile # Set ZOTERO_PROFILE environment variable to choose profile
profile_args=()
if [ -n "${ZOTERO_PROFILE:-}" ]; then if [ -n "${ZOTERO_PROFILE:-}" ]; then
profile="-p $ZOTERO_PROFILE" profile_args=(-p "$ZOTERO_PROFILE")
fi fi
REBUILD=0 FORCE_FULL=0
NO_REBUILD=0
SKIP_BUNDLED_FILES=0 SKIP_BUNDLED_FILES=0
DEBUGGER=0 DEBUGGER=0
while getopts "rbd" opt; do while getopts "rfnbd" opt; do
case $opt in case $opt in
r) r)
REBUILD=1 # Deprecated -- rebuilding is now the default
echo "-r is deprecated -- rebuilding is now the default (use -n to skip)" >&2
;;
f)
FORCE_FULL=1
;;
n)
NO_REBUILD=1
;; ;;
b) b)
@ -27,7 +38,6 @@ while getopts "rbd" opt; do
;; ;;
\?) \?)
echo "Invalid option: -$OPTARG" >&2
exit 1 exit 1
;; ;;
esac esac
@ -36,11 +46,14 @@ done
# Remove options from $@ # Remove options from $@
shift $((OPTIND-1)) shift $((OPTIND-1))
if [ $REBUILD -eq 1 ]; then if [ $NO_REBUILD -eq 0 ]; then
PARAMS="" PARAMS=""
if [ $DEBUGGER -eq 1 ]; then if [ $DEBUGGER -eq 1 ]; then
PARAMS="-t" PARAMS="-t"
fi fi
if [ $FORCE_FULL -eq 1 ]; then
PARAMS="$PARAMS -f"
fi
# Check if build watch is running # Check if build watch is running
# If not, run now # If not, run now
@ -49,16 +62,11 @@ if [ $REBUILD -eq 1 ]; then
echo echo
cd $ROOT_DIR cd $ROOT_DIR
# TEMP: --openssl-legacy-provider avoids a build error in pdf.js # TEMP: --openssl-legacy-provider avoids a build error in pdf.js
NODE_OPTIONS=--openssl-legacy-provider npm run build NODE_OPTIONS=--openssl-legacy-provider node js-build/build.js
echo echo
fi fi
"$SCRIPT_DIR/dir_build" -q $PARAMS "$SCRIPT_DIR/dir_build" $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 fi
PARAMS="" PARAMS=""
@ -74,7 +82,11 @@ fi
if [ "`uname`" = "Darwin" ]; then if [ "`uname`" = "Darwin" ]; then
command="Zotero.app/Contents/MacOS/zotero" command="Zotero.app/Contents/MacOS/zotero"
elif [ "`uname`" = "Linux" ]; then elif [ "`uname`" = "Linux" ]; then
if [[ "`uname -m`" = "aarch64" ]]; then
command="Zotero_linux-arm64/zotero"
else
command="Zotero_linux-x86_64/zotero" command="Zotero_linux-x86_64/zotero"
fi
elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then elif [ "`uname -o 2> /dev/null`" = "Cygwin" ]; then
command="Zotero_win-x64/zotero.exe" command="Zotero_win-x64/zotero.exe"
else else
@ -82,4 +94,5 @@ else
exit 1 exit 1
fi fi
"$APP_ROOT_DIR/staging/$command" $profile -ZoteroDebugText -purgecaches $PARAMS "$@" echo
"$APP_ROOT_DIR/staging/$command" "${profile_args[@]}" -ZoteroDebugText $PARAMS "$@"

View file

@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
# #
# Builds and deploys Zotero with full and incremental updates # Builds Zotero and uploads to S3 and deploy server, but does not make the build live.
# To make the build live, run the deploy script on the deploy server (or via SSH).
# #
set -euo pipefail set -euo pipefail
@ -60,8 +61,8 @@ if [ -z "$VERSION" ]; then
exit 1 exit 1
fi fi
# Enforce signing unless a test build # Enforce signing unless a test build or macOS, which doesn't start otherwise
if [ "$CHANNEL" != "test" ]; then if [[ "$CHANNEL" != "test" || $PLATFORMS = "m" ]]; then
SIGN="-e" SIGN="-e"
else else
SIGN="" SIGN=""
@ -111,6 +112,16 @@ do
echo echo
echo "Getting $platform_name incrementals" echo "Getting $platform_name incrementals"
INCREMENTALS="`\"$SCRIPT_DIR/manage_incrementals\" -c $CHANNEL -p ${PLATFORMS:i:1} -n $NUM_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 "$INCREMENTALS"
echo echo
@ -126,46 +137,31 @@ done
# Upload builds to S3 # Upload builds to S3
"$SCRIPT_DIR/upload_builds" $CHANNEL $VERSION "$SCRIPT_DIR/upload_builds" $CHANNEL $VERSION
# Upload file lists for each platform # Upload file lists and build info for each platform
channel_deploy_path="$DEPLOY_PATH/$CHANNEL" channel_deploy_path="$DEPLOY_PATH/manifests/$CHANNEL"
mkdir "$TEMP_DIR/version_info" mkdir "$TEMP_DIR/version_info"
chmod g+ws "$TEMP_DIR/version_info" chmod g+ws "$TEMP_DIR/version_info"
cp "$DIST_DIR"/files-* "$TEMP_DIR/version_info" cp "$DIST_DIR"/files-* "$TEMP_DIR/version_info"
chmod g+w "$TEMP_DIR"/version_info/files-* chmod g+w "$TEMP_DIR"/version_info/files-*
rsync -rv "$TEMP_DIR/version_info/" $DEPLOY_HOST:"$channel_deploy_path/$VERSION/" # Generate build-{os}.json for each platform
SHORT_VERSION=$(echo "$VERSION" | grep -oE '^[0-9]+\.[0-9]+')
# Download updates JSON for each platform, update it, and reupload it DETAILS_URL="https://www.zotero.org/support/${SHORT_VERSION}_changelog"
for i in `seq 0 1 $((${#PLATFORMS}-1))` for i in `seq 0 1 $((${#PLATFORMS}-1))`
do do
case ${PLATFORMS:i:1} in case ${PLATFORMS:i:1} in
m) m) os_name="mac" ;;
architectures="mac" w) os_name="win" ;;
;; l) os_name="linux" ;;
w)
architectures="win32 win-x64 win-arm64"
;;
l)
architectures="linux-i686 linux-x86_64"
;;
esac esac
printf '{\n "buildID": "%s",\n "detailsURL": "%s"\n}\n' "$BUILD_ID" "$DETAILS_URL" \
for arch in $architectures; > "$TEMP_DIR/version_info/build-${os_name}.json"
do chmod g+w "$TEMP_DIR/version_info/build-${os_name}.json"
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 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"/* rm -rf "$STAGE_DIR"/*
echo
echo "Build $VERSION uploaded successfully."
echo "To deploy, run on the deploy server:"
echo " ./deploy $CHANNEL $VERSION $PLATFORMS"

View file

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

View file

@ -5,7 +5,7 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")" APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_DIR="$(dirname "$APP_ROOT_DIR")" ROOT_DIR="$(dirname "$APP_ROOT_DIR")"
. "$APP_ROOT_DIR/config.sh" . "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/bootstrap.sh" . "$SCRIPT_DIR/utils.sh"
cd "$ROOT_DIR" cd "$ROOT_DIR"
@ -42,7 +42,7 @@ for lfs_file in $lfs_files; do
file_type=$(file -b $lfs_file) file_type=$(file -b $lfs_file)
echo -n $file_type" " echo -n $file_type" "
if [ -z "$(echo -n $file_type | grep XZ)" ]; then if [ -z "$(echo -n $file_type | grep XZ)" ]; then
{ $FAIL_CMD_INLINE; FAILED=1; echo " -- run 'git lfs pull'"; } { $FAIL_CMD_INLINE; FAILED=1; echo " -- run 'git lfs install; git lfs pull'"; }
fi fi
echo echo
done done
@ -56,9 +56,6 @@ which python3 || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for curl: " echo -n "Checking for curl: "
which curl || { $FAIL_CMD; FAILED=1; } which curl || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for wget: "
which wget || { $FAIL_CMD; FAILED=1; }
echo -n "Checking for zip: " echo -n "Checking for zip: "
which zip || { $FAIL_CMD; FAILED=1; } which zip || { $FAIL_CMD; FAILED=1; }

View file

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

View file

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

View file

@ -22,13 +22,21 @@ set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")" APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh" . "$APP_ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
cd "$APP_ROOT_DIR" cd "$APP_ROOT_DIR"
function usage { usage() {
cat >&2 <<DONE cat >&2 <<'DONE'
Usage: $0 -p platforms [-s] Usage: fetch_xulrunner.sh -p <m|w|l> [-a ARCH]
Options
-p PLATFORMS Platforms to build (m=Mac, w=Windows, l=Linux) Required
-p PLATFORM m (Mac), w (Windows), l (Linux)
Optional / Conditional
-a ARCH - Mac: disallowed (builds are universal)
- Windows: x64 | arm64 | win32
- Linux: x64 (or x86_64) | arm64
If omitted on Windows/Linux, all standard archs are fetched.
DONE DONE
exit 1 exit 1
} }
@ -36,22 +44,20 @@ DONE
BUILD_MAC=0 BUILD_MAC=0
BUILD_WIN=0 BUILD_WIN=0
BUILD_LINUX=0 BUILD_LINUX=0
while getopts "p:s" opt; do arch=""
while getopts 'p:a:' opt; do
case $opt in case $opt in
p) p)
for i in `seq 0 1 $((${#OPTARG}-1))` for (( i=0; i<${#OPTARG}; i++ )); do
do
case ${OPTARG:i:1} in case ${OPTARG:i:1} in
m) BUILD_MAC=1;; m) BUILD_MAC=1;;
w) BUILD_WIN=1;; w) BUILD_WIN=1;;
l) BUILD_LINUX=1;; l) BUILD_LINUX=1;;
*) *) echo "Invalid platform: ${OPTARG:i:1}"; echo; usage;;
echo "$0: Invalid platform option ${OPTARG:i:1}"
usage
;;
esac esac
done done;;
;; a) arch="$OPTARG";;
*) usage;;
esac esac
shift $((OPTIND-1)); OPTIND=1 shift $((OPTIND-1)); OPTIND=1
done done
@ -61,31 +67,30 @@ if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; th
usage usage
fi fi
function replace_line { # Sanity‑check arch flag vs platform selections
pattern=$1 if [[ $BUILD_MAC == 1 ]]; then
replacement=$2 [[ -n $arch ]] && { echo "-a is not allowed for Mac" >&2; echo; usage; }
file=$3 fi
if [[ $BUILD_WIN == 1 || $BUILD_LINUX == 1 ]]; then
if egrep -q "$pattern" "$file"; then # arch optional; if provided validate & map aliases now
perl -pi -e "s/$pattern/$replacement/" "$file" if [[ -n $arch ]]; then
else if [[ $BUILD_WIN == 1 && $BUILD_LINUX == 1 ]]; then
echo "$pattern" not found in "$file" -- aborting 2>&1 echo "-a cannot be used when building both Windows and Linux simultaneously" >&2; echo; usage
exit 1
fi fi
} if [[ $BUILD_WIN == 1 ]]; then
case $arch in
function remove_line { x64) arch="win-x64" ;;
pattern=$1 arm64) arch="win-arm64" ;;
file=$2 win32|win-x64|win-arm64) ;;
*) echo "Invalid Windows arch: $arch" >&2; echo; usage;;
if egrep -q "$pattern" "$file"; then esac
egrep -v "$pattern" "$file" > "$file.tmp"
mv "$file.tmp" "$file"
else
echo "$pattern" not found in "$file" -- aborting 2>&1
exit 1
fi fi
} if [[ $BUILD_LINUX == 1 ]]; then
[[ $arch == x64 ]] && arch="x86_64"
case $arch in x86_64|arm64) ;; *) echo "Invalid Linux arch: $arch" >&2; echo; usage;; esac
fi
fi
fi
function get_utf16_chars { function get_utf16_chars {
str=$(echo -n "$1" | xxd -p | fold -w 2 | sed -r 's/(.+)/\\\\x{\1}\\\\x{00}/') str=$(echo -n "$1" | xxd -p | fold -w 2 | sed -r 's/(.+)/\\\\x{\1}\\\\x{00}/')
@ -115,16 +120,32 @@ function modify_omni {
unzip omni.ja unzip omni.ja
rm 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 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 # 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 # 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_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_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_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_CRASHREPORTER:' 'MOZ_CRASHREPORTER: false \&\&' modules/AppConstants.sys.mjs
replace_line 'MOZ_UPDATE_CHANNEL:.+' 'MOZ_UPDATE_CHANNEL: "none",' 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 replace_line '"https:\/\/[^\/]+mozilla.com.+"' '""' modules/AppConstants.sys.mjs
@ -149,6 +170,20 @@ function modify_omni {
# Prevent error during network requests # Prevent error during network requests
replace_line 'async lazyInit\(\) \{' 'async lazyInit() { if (this.features) return false;' modules/UrlClassifierExceptionListService.sys.mjs 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.captive-portal-service.enabled".+' 'pref("network.captive-portal-service.enabled", false);' greprefs.js
replace_line 'pref\("network.connectivity-service.enabled".+' 'pref("network.connectivity-service.enabled", false);' greprefs.js replace_line 'pref\("network.connectivity-service.enabled".+' 'pref("network.connectivity-service.enabled", false);' greprefs.js
replace_line 'pref\("toolkit.telemetry.server".+' 'pref("toolkit.telemetry.server", "");' greprefs.js replace_line 'pref\("toolkit.telemetry.server".+' 'pref("toolkit.telemetry.server", "");' greprefs.js
@ -164,8 +199,21 @@ function modify_omni {
# Disable unwanted components # Disable unwanted components
remove_line '(RemoteSettings|services-|telemetry|Telemetry|URLDecorationAnnotationsService)' components/components.manifest 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 # Do not trigger LoginManager event that logs an error on autocomplete submission
remove_line 'DOMInputPasswordAdded: \{\},' modules/ActorManagerParent.sys.mjs remove_line 'DOMInputPasswordAdded: \{\},' $file
# On Mac/Linux, ignore relative paths in PATH in Subprocess.pathSearch(), used when a bare command is passed to Utilities.Internal.subprocess() # 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 if [[ $platform != win* ]]; then
@ -176,6 +224,7 @@ function modify_omni {
pushd modules pushd modules
rm Bookmarks* rm Bookmarks*
rm FxAccounts* rm FxAccounts*
echo '' > FingerprintingWebCompatService.sys.mjs
echo '' > PartitioningExceptionListService.sys.mjs echo '' > PartitioningExceptionListService.sys.mjs
rm Places* rm Places*
echo '' > PlacesExpiration.sys.mjs echo '' > PlacesExpiration.sys.mjs
@ -190,6 +239,29 @@ function modify_omni {
rm -rf default-theme rm -rf default-theme
popd 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 # Clear most WebExtension manifest properties
replace_line 'manifest = normalized.value;' 'manifest = normalized.value; replace_line 'manifest = normalized.value;' 'manifest = normalized.value;
if (this.type == "extension") { if (this.type == "extension") {
@ -211,19 +283,33 @@ function modify_omni {
}' modules/Extension.sys.mjs }' modules/Extension.sys.mjs
# Use applications.zotero instead of applications.gecko # Use applications.zotero instead of applications.gecko
replace_line 'let bss = manifest.applications\?.gecko' 'let bss = manifest.applications?.zotero' modules/addons/XPIInstall.jsm replace_line 'let bss = manifest.applications\?.gecko' 'let bss = manifest.applications?.zotero' modules/addons/XPIInstall.sys.mjs
replace_line 'manifest.applications\?.gecko' 'manifest.applications?.zotero' modules/Extension.sys.mjs replace_line 'manifest.applications\?.gecko' 'manifest.applications?.zotero' modules/Extension.sys.mjs
# When installing addon, use app version instead of toolkit version for targetApplication # When installing addon, use app version instead of toolkit version for targetApplication
replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/XPIInstall.jsm replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/XPIInstall.sys.mjs
# Set strictCompatibility to false in beta/dev/source builds to skip strict_max_version check
replace_line 'addon.strictCompatibility = true;' \
'let version = Services.appinfo.version;
addon.strictCompatibility = !version.includes("-beta") && !version.includes("-dev") && !version.includes("SOURCE");' modules/addons/XPIInstall.sys.mjs
# In beta/dev/source builds, ignore the persisted strictCompatibility flag for already-installed
# plugins, so plugins installed on a stable build (where the flag was set to true) aren't marked
# incompatible after upgrading. The manifest isn't re-parsed on app upgrade, so the flag set at
# install time sticks around.
replace_line '!this\.addon\.strictCompatibility' \
'(!this.addon.strictCompatibility || Services.appinfo.version.includes("-beta") || Services.appinfo.version.includes("-dev") || Services.appinfo.version.includes("SOURCE"))' modules/addons/XPIInstall.sys.mjs
replace_line '!this\.strictCompatibility &&' \
'(!this.strictCompatibility || Services.appinfo.version.includes("-beta") || Services.appinfo.version.includes("-dev") || Services.appinfo.version.includes("SOURCE")) &&' modules/addons/XPIDatabase.sys.mjs
# Accept zotero@chnm.gmu.edu for target application to allow Zotero 6 plugins to remain # Accept zotero@chnm.gmu.edu for target application to allow Zotero 6 plugins to remain
# installed in Zotero 7 # installed in Zotero 7
replace_line "if \(targetApp.id == Services.appinfo.ID\) \{" "if (targetApp.id == 'zotero\@chnm.gmu.edu') targetApp.id = '$APP_ID'; if (targetApp.id == Services.appinfo.ID) {" modules/addons/XPIDatabase.jsm replace_line "if \(targetApp.id == Services.appinfo.ID\) \{" "if (targetApp.id == 'zotero\@chnm.gmu.edu') targetApp.id = '$APP_ID'; if (targetApp.id == Services.appinfo.ID) {" modules/addons/XPIDatabase.sys.mjs
# TEMP: Skip addons without ids until we figure out what's going on with the default theme. # TEMP: Skip addons without ids until we figure out what's going on with the default theme.
# This fixes plugin updating and other things. # This fixes plugin updating and other things.
replace_line 'return addons;' 'return addons.filter(addon => addon.id);' modules/addons/XPIDatabase.jsm replace_line 'return addons;' 'return addons.filter(addon => addon.id);' modules/addons/XPIDatabase.sys.mjs
# For updates, look for applications.zotero instead of applications.gecko in manifest.json and # 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 # use the app id and version for strict_min_version/strict_max_version comparisons
@ -234,13 +320,22 @@ function modify_omni {
replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/AddonUpdateChecker.sys.mjs replace_line "id: TOOLKIT_ID," "id: '$APP_ID'," modules/addons/AddonUpdateChecker.sys.mjs
replace_line 'lazy.AddonManagerPrivate.webExtensionsMinPlatformVersion' '"7.0"' modules/addons/AddonUpdateChecker.sys.mjs replace_line 'lazy.AddonManagerPrivate.webExtensionsMinPlatformVersion' '"7.0"' modules/addons/AddonUpdateChecker.sys.mjs
replace_line 'result.targetApplications.push' 'false && result.targetApplications.push' 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 # Prevent automatic installation of default theme
# (chrome/toolkit/content/mozapps/extensions/default-theme, removed above) # (chrome/toolkit/content/mozapps/extensions/default-theme, removed above)
replace_line 'this.maybeInstallBuiltinAddon\(' 'if (false) this.maybeInstallBuiltinAddon(' modules/addons/XPIProvider.jsm replace_line 'this.maybeInstallBuiltinAddon\(' 'if (false) this.maybeInstallBuiltinAddon(' modules/addons/XPIProvider.sys.mjs
# Don't log a warning when checking 'distribution/extensions' for addons # Don't log a warning when checking 'distribution/extensions' for addons
replace_line 'logger.warn\(`Checking' 'logger.debug(`Checking' modules/addons/XPIProvider.jsm replace_line 'logger.warn\(`Checking' 'logger.debug(`Checking' modules/addons/XPIProvider.sys.mjs
# Allow addon installation by bypassing confirmation dialogs. If we want a confirmation dialog, # 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 # we need to either add gXPInstallObserver from browser-addons.js [1][2] or provide our own with
@ -249,7 +344,7 @@ function modify_omni {
# [1] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/browser/base/content/browser.js#1902-1923 # [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 # [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 # [3] https://searchfox.org/mozilla-esr102/rev/5a6d529652045050c5cdedc0558238949b113741/toolkit/mozapps/extensions/AddonManager.sys.mjs#3114-3124
replace_line 'if \(info.addon.userPermissions\) \{' 'if (false) {' modules/AddonManager.sys.mjs replace_line 'if \(info.addon.installPermissions\) \{' 'if (false) {' modules/AddonManager.sys.mjs
replace_line '\} else if \(info.addon.sitePermissions\) \{' '} else if (false) {' modules/AddonManager.sys.mjs replace_line '\} else if \(info.addon.sitePermissions\) \{' '} else if (false) {' modules/AddonManager.sys.mjs
replace_line '\} else if \(requireConfirm\) \{' '} else if (false) {' modules/AddonManager.sys.mjs replace_line '\} else if \(requireConfirm\) \{' '} else if (false) {' modules/AddonManager.sys.mjs
@ -263,9 +358,35 @@ function modify_omni {
replace_line 'listener\[aMethod\].apply\(listener, aArgs\);' \ replace_line 'listener\[aMethod\].apply\(listener, aArgs\);' \
'let maybePromise = listener[aMethod].apply(listener, aArgs); 'let maybePromise = listener[aMethod].apply(listener, aArgs);
if (maybePromise && maybePromise.then) await maybePromise;' modules/AddonManager.sys.mjs if (maybePromise && maybePromise.then) await maybePromise;' modules/AddonManager.sys.mjs
replace_line 'AddonManagerPrivate.callAddonListeners' 'await AddonManagerPrivate.callAddonListeners' modules/addons/XPIInstall.jsm replace_line 'AddonManagerPrivate.callAddonListeners' 'await AddonManagerPrivate.callAddonListeners' modules/addons/XPIInstall.sys.mjs
replace_line 'let uninstall = \(\) => \{' 'let uninstall = async () => {' modules/addons/XPIInstall.jsm replace_line 'let uninstall = \(\) => \{' 'let uninstall = async () => {' modules/addons/XPIInstall.sys.mjs
replace_line 'cancelUninstallAddon\(aAddon\)' 'async cancelUninstallAddon(aAddon)' modules/addons/XPIInstall.jsm replace_line 'cancelUninstallAddon\(aAddon\)' 'async cancelUninstallAddon(aAddon)' modules/addons/XPIInstall.sys.mjs
# Check for `uninstall` flag in update config and uninstall the addon if set
replace_line 'if \(update && !this.addon.location.locked\) \{' \
'if (update && !this.addon.location.locked) {
if (update.uninstall) {
let aId = this.addon.id;
logger.debug(`AddonUpdateChecker: uninstall addon \${aId} with uninstall flag`);
let addon = await AddonManager.getAddonByID(aId);
if (addon && (addon.permissions & AddonManager.PERM_CAN_UNINSTALL)) {
try {
addon.uninstall();
} catch (err) {
logger.error(`AddonUpdateChecker: error uninstalling addon \${aId} with uninstall flag`, err);
}
sendUpdateAvailableMessages(this, null);
return;
}
else {
logger.error(`AddonUpdateChecker: addon \${aId} with uninstall flag not found or cannot be uninstalled`);
sendUpdateAvailableMessages(this, null);
return;
}
}
' modules/addons/XPIInstall.sys.mjs
# Prevent login manager from trying to load FxAccountsCommon.sys.mjs
replace_line 'return login.hostname == lazy.FXA_PWDMGR_HOST;' 'return false;' modules/storage-json.sys.mjs
# No idea why this is necessary, but without it initialization fails with "TypeError: "constructor" is read-only" # 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 replace_line 'LoginStore.prototype.constructor = LoginStore;' '\/\/LoginStore.prototype.constructor = LoginStore;' modules/LoginStore.sys.mjs
@ -294,48 +415,67 @@ function modify_omni {
file="chrome/toolkit/content/mozapps/extensions/aboutaddons.css" file="chrome/toolkit/content/mozapps/extensions/aboutaddons.css"
echo >> $file echo >> $file
# Hide search bar, Themes and Plugins tabs, and sidebar footer # Hide the search bar and the whole sidebar, since we only ever show plugins and the main pane
echo '.main-search, button[name="theme"], button[name="plugin"], sidebar-footer { display: none; }' >> $file # is already headed "Manage Your Plugins"
echo '.main-search, #sidebar { display: none; }' >> $file
# Center the content in the window now that it isn't offset by the sidebar
echo '#full { grid-template-columns: 1fr; }' >> $file
echo '#content { max-width: calc(var(--page-main-content-width) + var(--main-margin-start)); margin-inline: auto; }' >> $file
echo '.main-heading { margin-top: 2em; }' >> $file echo '.main-heading { margin-top: 2em; }' >> $file
# Hide Details/Permissions tabs in addon details so we only show details # Hide Details/Permissions tabs in addon details so we only show details
echo 'addon-details > button-group { display: none !important; }' >> $file 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" # 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 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 # Show cursor feedback on plugin homepage links
echo '.addon-detail-row-homepage .text-link { cursor: pointer; color: -moz-nativehyperlinktext; }' >> $file echo '.addon-detail-row-homepage .text-link { cursor: pointer; color: LinkText; }' >> $file
echo '.addon-detail-row-homepage .text-link:hover { text-decoration: underline; }' >> $file echo '.addon-detail-row-homepage .text-link:hover { text-decoration: underline; }' >> $file
file="chrome/toolkit/content/mozapps/extensions/aboutaddons.js" file="chrome/toolkit/content/mozapps/extensions/aboutaddons-utils.mjs"
# Hide unsigned-addon warning # Hide unsigned-addon warning
replace_line 'if \(!isCorrectlySigned\(addon\)\) \{' 'if (!isCorrectlySigned(addon)) {return {};' $file replace_line 'export function isUnsignedWarningMessageDisabled\(\) \{' \
'export function isUnsignedWarningMessageDisabled() {if (true) return true;' $file
# Use our own localized string for blocked plugin notification
replace_line 'details-notification-hard-blocked-\$\{typeSuffix\}' 'plugins-blocked-plugin' $file
# Hide Recommendations tab in sidebar and recommendations in main pane
replace_line 'function isDiscoverEnabled\(\) \{' 'function isDiscoverEnabled() {return false;' $file
file="chrome/toolkit/content/mozapps/extensions/components/addon-details.mjs"
# Hide Private Browsing setting in addon details # Hide Private Browsing setting in addon details
replace_line 'pbRow\.' '\/\/pbRow.' $file replace_line 'pbRow\.' '\/\/pbRow.' $file
replace_line 'let isAllowed = await isAllowedInPrivateBrowsing' '\/\/let isAllowed = await isAllowedInPrivateBrowsing' $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 # Use our own strings for the removal prompt
replace_line 'let \{ BrowserAddonUI \} = windowRoot.ownerGlobal;' '' $file replace_line 'let \{ BrowserAddonUI \} = windowRoot.window;' '' $file
replace_line 'await BrowserAddonUI.promptRemoveExtension' 'promptRemoveExtension' $file replace_line 'await BrowserAddonUI.promptRemoveExtension' 'promptRemoveExtension' $file
# Customize empty-list message # Customize empty-list message
replace_line 'createEmptyListMessage\(\) {' 'createEmptyListMessage() { replace_line 'createEmptyListMessage\(\) {' 'createEmptyListMessage() {
var p = document.createElement("p"); var p = document.createElement("p");
p.id = "empty-list-message"; p.id = "empty-list-message";
return p;' $file return p;' chrome/toolkit/content/mozapps/extensions/components/addon-list.mjs
# 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 # Add zotero.ftl to the addons page for our own localized strings
replace_line 'let homepageURL = homepageRow.querySelector\(\"a\"\);' 'let homepageURL = homepageRow.querySelector(\"\.text-link\");' $file replace_line 'href="toolkit\/about\/aboutAddons.ftl"' 'href="toolkit\/about\/aboutAddons.ftl" \/>\n <link rel="localization" href="zotero.ftl"' \
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 chrome/toolkit/content/mozapps/extensions/aboutaddons.html
# TODO: Use our own strings, but for now fix the bundled English ones # 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 '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-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 replace_line 'pref\("extensions.htmlaboutaddons.recommendations.enabled".+' 'pref("extensions.htmlaboutaddons.recommendations.enabled", false);' greprefs.js
# Hide Report option # Hide Report option
@ -350,20 +490,23 @@ function modify_omni {
replace_line 'id=\"commonDialogWindow\"' \ replace_line 'id=\"commonDialogWindow\"' \
'id=\"commonDialogWindow\" class=\"zotero-dialog\" drawintitlebar-platforms=\"mac\"' \ 'id=\"commonDialogWindow\" class=\"zotero-dialog\" drawintitlebar-platforms=\"mac\"' \
chrome/toolkit/content/global/commonDialog.xhtml chrome/toolkit/content/global/commonDialog.xhtml
replace_line '<\?xml-stylesheet href=\"chrome:\/\/global\/skin\/commonDialog.css\" type=\"text\/css\"\?>' \ # commonDialog.css link is split across multiple lines, so we have to do a weird substitution,
'<\?xml-stylesheet href=\"chrome:\/\/global\/skin\/commonDialog.css\" type=\"text\/css\"\?> # so check the one-line global.css to make sure the format hasn't changed
<\?xml-stylesheet href=\"chrome:\/\/zotero-platform\/content\/zotero.css\" type=\"text\/css\"\?>' \ check_line '<html:link rel="stylesheet" href="chrome:\/\/global\/skin\/global.css" \/>' chrome/toolkit/content/global/commonDialog.xhtml
replace_line 'chrome:\/\/global\/skin\/commonDialog.css"' \
'chrome:\/\/global\/skin\/commonDialog.css"\/>
<html:link rel="stylesheet" href="chrome:\/\/zotero-platform\/content\/zotero.css"' \
chrome/toolkit/content/global/commonDialog.xhtml chrome/toolkit/content/global/commonDialog.xhtml
# commonDialog.xhtml can be opened via Services.prompt in the Profile Manager, so check for a profile first # commonDialog.xhtml can be opened via Services.prompt in the Profile Manager, so check for a profile first
replace_line '<script>' \ replace_line 'function commonDialogOnLoad\(\) \{' \
'<script> 'function commonDialogOnLoad() {
try { try {
Services.dirsvc.get(\"ProfD\", Ci.nsIFile); Services.dirsvc.get(\"ProfD\", Ci.nsIFile);
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/include.js\", this); Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/include.js\", this);
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/titlebar.js\", this); Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/titlebar.js\", this);
Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/customElements.js\", this); Services.scriptloader.loadSubScript(\"chrome:\/\/zotero\/content\/customElements.js\", this);
} catch (e) {}' \ } catch (e) {}' \
chrome/toolkit/content/global/commonDialog.xhtml chrome/toolkit/content/global/commonDialog.js
# Use native checkbox instead of Firefox-themed version in prompt dialogs # 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 replace_line '<xul:checkbox' '<xul:checkbox native=\"true\"' chrome/toolkit/content/global/commonDialog.xhtml
@ -377,11 +520,41 @@ function modify_omni {
popupset = document.createXULElement("popupset"); popupset = document.createXULElement("popupset");
document.documentElement.appendChild(popupset); document.documentElement.appendChild(popupset);
} }
popupset.appendChild(this._autoScrollPopup);' chrome/toolkit/content/global/elements/browser-custom-element.js popupset.appendChild(this._autoScrollPopup);' chrome/toolkit/content/global/elements/browser-custom-element.mjs
# Show Services menu on input fields # Add the dropmarker for toolbarbuttons with wantdropmarker, which Firefox no longer creates
# (JS part of https://hg.mozilla.org/mozilla-central/rev/defc298879844981e7c1817f13e3fc2caf84f148 for input boxes) replace_line 'this.appendChild\(this.constructor.fragment.cloneNode\(true\)\);' \
replace_line '<menupopup class="textbox-contextmenu">' '<menupopup class="textbox-contextmenu" showservicesmenu="true">' chrome/toolkit/content/global/elements/moz-input-box.js 'this.appendChild(this.constructor.fragment.cloneNode(true));
if (this.hasAttribute("wantdropmarker")) {
let dropmarker = document.createXULElement("dropmarker");
dropmarker.setAttribute("type", "menu");
dropmarker.className = "toolbarbutton-menu-dropmarker";
this.appendChild(dropmarker);
}' chrome/toolkit/content/global/elements/toolbarbutton.js
# Let clicks on the dropmarker pass through to the toolbarbutton, which only opens its menu
# when it's the original event target, as xul.css did when Firefox created the dropmarker
echo '.toolbarbutton-menu-dropmarker { pointer-events: none; }' >> chrome/toolkit/content/global/xul.css
# Remove non-native text input styles
remove_between 'html\|input\:where\(' '^}' chrome/toolkit/skin/classic/global/global-shared.css
# Prevent window dragging on scrollbars
echo 'scrollbar { -moz-window-dragging: no-drag; }' >> chrome/toolkit/res/scrollbars.css
# Provide a mechanism for non-UA stylesheets to override the overflow property
# of a text control's anonymous text node child
echo '::placeholder, ::-moz-text-control-editing-root, ::-moz-text-control-preview {
overflow: var(--moz-text-control-overflow);
}' >> chrome/toolkit/res/forms.css
# Size an autocomplete popup to the input that opened it, or to an ancestor that designates
# itself as the width container. Firefox sizes the popup to its content. (For creator inputs.)
replace_line '\/\/ invalidate\(\) depends on the width attribute' \
'let widthElement = aElement.closest(".autocomplete-popup-width-container") || aElement;
this.style.setProperty("--panel-width", Math.max(widthElement.getBoundingClientRect().width, 100) + "px");
\/\/ invalidate() depends on the width attribute' \
chrome/toolkit/content/global/elements/autocomplete-popup.js
zip -qr9XD omni.ja * zip -qr9XD omni.ja *
mv omni.ja .. mv omni.ja ..
@ -428,12 +601,14 @@ if [ $BUILD_MAC == 1 ]; then
echo "Using Firefox $GECKO_VERSION.app.zip" echo "Using Firefox $GECKO_VERSION.app.zip"
unzip "Firefox $GECKO_VERSION.app.zip" unzip "Firefox $GECKO_VERSION.app.zip"
else else
curl -o Firefox.dmg "$DOWNLOAD_URL/mac/en-US/Firefox%20$GECKO_VERSION.dmg" curl -fo Firefox.dmg "$DOWNLOAD_URL/mac/en-US/Firefox%20$GECKO_VERSION.dmg"
set +e set +e
hdiutil detach -quiet /Volumes/Firefox 2>/dev/null hdiutil detach -quiet /Volumes/Firefox 2>/dev/null
set -e set -e
hdiutil attach -quiet Firefox.dmg hdiutil attach -quiet Firefox.dmg
cp -a /Volumes/Firefox/Firefox.app . 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 hdiutil detach -quiet /Volumes/Firefox
fi fi
@ -442,26 +617,41 @@ if [ $BUILD_MAC == 1 ]; then
echo echo
rm -rf MacOS rm -rf MacOS
if [ -n "$custom_components_hash_mac" ]; then if [ -n "$custom_components_hash_mac" ]; then
if [ -e "Firefox $GECKO_VERSION MacOS.zip" ]; then tarfile="Firefox $GECKO_VERSION MacOS.tar.xz"
echo "Using Firefox $GECKO_VERSION MacOS.zip" if [[ "$(uname)" = "Darwin" ]]; then
unzip "Firefox $GECKO_VERSION MacOS.zip" shasum="shasum -a 256"
else
echo "Downloading custom Firefox components"
echo
curl -o MacOS.tar.xz "${custom_components_url}mac/$custom_components_hash_mac.tar.xz"
# Verify hash
if [[ "`uname`" = "Darwin" ]]; then
shasum=shasum
else else
shasum=sha256sum shasum=sha256sum
fi fi
echo
echo "$custom_components_hash_mac MacOS.tar.xz" | $shasum -c -
echo
tar xvf MacOS.tar.xz 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 fi
# Verify hash
echo
if ! verify_components_hash; then
echo "SHA hash mismatch. Redownloading custom Firefox components…"
rm -f "$tarfile"
download_components
echo
if ! verify_components_hash; then
echo "Hash still doesn't match. Aborting."
exit 1
fi
fi
echo
tar xvf "$tarfile"
echo echo
fi fi
@ -480,13 +670,8 @@ if [ $BUILD_MAC == 1 ]; then
exit 1 exit 1
fi fi
if [ ! -e "Firefox $GECKO_VERSION.app.zip" ]; then rm -f Firefox.dmg
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 echo $("$SCRIPT_DIR/xulrunner_hash" -p m) > hash-mac
fi fi
@ -494,23 +679,32 @@ if [ $BUILD_WIN == 1 ]; then
GECKO_VERSION="$GECKO_VERSION_WIN" GECKO_VERSION="$GECKO_VERSION_WIN"
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION" DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
for arch in win32 win-x64 win-arm64; do if [[ -n $arch ]]; then
arch_list=("$(get_canonical_arch w $arch)")
else
arch_list=(win-x64 win-arm64 win32)
fi
for arch in "${arch_list[@]}"; do
xdir=firefox-$arch xdir=firefox-$arch
rm -rf $xdir rm -rf $xdir
mkdir $xdir mkdir $xdir
if [ -e "Firefox Setup $GECKO_VERSION-$arch.exe" ]; then archived_file="Firefox Setup $GECKO_VERSION-$arch.exe"
echo "Using Firefox Setup $GECKO_VERSION-$arch.exe" if [ -e "$archived_file" ]; then
cp "Firefox Setup $GECKO_VERSION-$arch.exe" "Firefox%20Setup%20$GECKO_VERSION.exe" echo "Using $archived_file"
cp "$archived_file" "Firefox%20Setup%20$GECKO_VERSION.exe"
else else
if [ $arch = "win-x64" ]; then if [ $arch = "win-x64" ]; then
curl -O "$DOWNLOAD_URL/win64/en-US/Firefox%20Setup%20$GECKO_VERSION.exe" moz_arch=win64
elif [ $arch = "win-arm64" ]; then elif [ $arch = "win-arm64" ]; then
curl -O "$DOWNLOAD_URL/win64-aarch64/en-US/Firefox%20Setup%20$GECKO_VERSION.exe" moz_arch=win64-aarch64
else else
curl -O "$DOWNLOAD_URL/$arch/en-US/Firefox%20Setup%20$GECKO_VERSION.exe" moz_arch=$arch
fi 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 fi
7z x Firefox%20Setup%20$GECKO_VERSION.exe -o$xdir 'core/*' 7z x Firefox%20Setup%20$GECKO_VERSION.exe -o$xdir 'core/*'
@ -518,32 +712,88 @@ if [ $BUILD_WIN == 1 ]; then
rm -rf $xdir rm -rf $xdir
mv $xdir-core $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 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 # 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 # 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 # 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 # 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. # 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) #from=$(get_utf16_chars "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38")
perl -pe "s/$from/$to/" < xul.dll > xul.dll.new #to=$(get_utf16_chars "Zotero" 38)
mv xul.dll.new xul.dll #perl -pe "s/$from/$to/" < xul.dll > xul.dll.new
# We re-sign DLL in build.sh, but presumably this file, which is for the Widevine CDM, #mv xul.dll.new xul.dll
# isn't vaid anymore
rm xul.dll.sig
# Check for UTF-16 "Zotero" in DLL # Check for UTF-16 "Zotero" in DLL
# #
# (The macOS strings command doesn't have an encoding option, so skip on macOS. We could # (The macOS strings command doesn't have an encoding option, so skip on macOS. You need to
# require binutils, which has GNU strings, but you need to build on Windows to make an # build on Windows to make an installer, so a Windows build from macOS likely isn't being
# installer, so a Windows build from macOS likely isn't being deployed, and there's no # deployed.)
# reason the Perl command above should fail anyway.) #
if [[ "`uname`" != "Darwin" ]] && [[ -z "$(strings -e l xul.dll | grep -m 1 Zotero)" ]]; then #if [[ "`uname`" != "Darwin" ]] && [[ -z "$(strings -e l xul.dll | grep -m 1 Zotero)" ]]; then
echo '"Zotero" not found in xul.dll after replacement' # echo '"Zotero" not found in xul.dll after replacement'
exit 1 # exit 1
fi #fi
# We re-sign DLL in build.sh, but presumably this file, which is for the Widevine CDM,
# isn't vaid anymore
rm xul.dll.sig
modify_omni $arch modify_omni $arch
# Disable skeleton UI window # Disable skeleton UI window
@ -556,41 +806,49 @@ if [ $BUILD_WIN == 1 ]; then
rm "Firefox%20Setup%20$GECKO_VERSION.exe" rm "Firefox%20Setup%20$GECKO_VERSION.exe"
echo echo
echo echo
echo "$( "$SCRIPT_DIR/xulrunner_hash" -p w -a "$arch" )" > "hash-$arch"
done done
echo $("$SCRIPT_DIR/xulrunner_hash" -p w) > hash-win
fi fi
if [ $BUILD_LINUX == 1 ]; then if [ $BUILD_LINUX == 1 ]; then
GECKO_VERSION="$GECKO_VERSION_LINUX" GECKO_VERSION="$GECKO_VERSION_LINUX"
DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION" DOWNLOAD_URL="https://ftp.mozilla.org/pub/firefox/releases/$GECKO_VERSION"
if [[ -n $arch ]]; then
# Include 32-bit build if not in CI arch_list=("$arch")
if [[ "${CI:-}" = "1" ]] || [[ "${SKIP_32:-}" = "1" ]]; then
arches="x86_64"
else else
arches="i686 x86_64" arch_list=(x86_64 arm64)
fi fi
for arch in $arches; do for arch in "${arch_list[@]}"; do
xdir="firefox-$arch" xdir="firefox-linux-$arch"
rm -rf $xdir rm -rf $xdir
archived_file="firefox-$GECKO_VERSION-$arch.tar.bz2" archived_file="firefox-$GECKO_VERSION-$arch.tar.xz"
if [ -e "$archived_file" ]; then if [ -e "$archived_file" ]; then
echo "Using $archived_file" echo "Using $archived_file"
cp "$archived_file" "firefox-$GECKO_VERSION.tar.bz2" cp "$archived_file" "firefox-$GECKO_VERSION.tar.xz"
else else
curl -O "$DOWNLOAD_URL/linux-$arch/en-US/firefox-$GECKO_VERSION.tar.bz2" if [ $arch = "arm64" ]; then
moz_arch=aarch64
else
moz_arch=$arch
fi
curl -fO "$DOWNLOAD_URL/linux-$moz_arch/en-US/firefox-$GECKO_VERSION.tar.xz"
# Store local copy of unmodified app
cp "firefox-$GECKO_VERSION.tar.xz" "$archived_file"
fi fi
tar xvf firefox-$GECKO_VERSION.tar.bz2 tar xvf firefox-$GECKO_VERSION.tar.xz
mv firefox firefox-$arch mv firefox $xdir
pushd firefox-$arch pushd $xdir
modify_omni $arch modify_omni $arch
popd popd
echo $($SCRIPT_DIR/xulrunner_hash -p l) > hash-linux
rm "firefox-$GECKO_VERSION.tar.bz2" rm "firefox-$GECKO_VERSION.tar.xz"
echo "$( $SCRIPT_DIR/xulrunner_hash -p l -a "$arch" )" > "hash-linux-${arch}"
done done
fi fi

View file

@ -14,7 +14,7 @@ hash="$1"
tmpdir=`mktemp -d` tmpdir=`mktemp -d`
cd $tmpdir cd $tmpdir
wget -O build.zip "https://$S3_BUCKET.s3.amazonaws.com/$S3_CI_ZIP_PATH/$hash.zip" >&2 \ curl -sS -f -L -o build.zip "https://$S3_BUCKET.s3.amazonaws.com/$S3_CI_ZIP_PATH/$hash.zip" >&2 \
|| (echo "ZIP file not found for commit '$hash'" && exit 1) || (echo "ZIP file not found for commit '$hash'" && exit 1)
unzip build.zip >&2 unzip build.zip >&2
rm build.zip rm build.zip

322
app/scripts/incremental_update Executable file
View file

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

View file

@ -64,22 +64,19 @@ if [[ "$VERSION" ]] && [[ "$NUM_VERSIONS" ]]; then
usage usage
fi fi
INCR_FILENAME="incrementals-$CHANNEL-$PLATFORM" INCR_FILENAME="incrementals-$PLATFORM"
S3_URL="s3://$S3_BUCKET/$S3_DIST_PATH/$CHANNEL/incrementals-$PLATFORM" REMOTE_PATH="$DEPLOY_PATH/manifests/$CHANNEL/$INCR_FILENAME"
INCR_PATH="$DIST_DIR/$INCR_FILENAME" 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" mkdir -p "$DIST_DIR"
aws s3 cp $S3_URL "$INCR_PATH" >&2 scp $DEPLOY_HOST:"$REMOTE_PATH" "$INCR_PATH" >&2
# Add version to file and reupload # Add version to file and reupload
if [ "$VERSION" ]; then if [ "$VERSION" ]; then
echo "Adding $VERSION to incrementals-$PLATFORM" echo "Adding $VERSION to incrementals-$PLATFORM"
echo $VERSION >> "$INCR_PATH" echo $VERSION >> "$INCR_PATH"
aws s3 cp "$INCR_PATH" $S3_URL scp "$INCR_PATH" $DEPLOY_HOST:"$REMOTE_PATH"
# Show last n versions # Show last n versions
elif [ "$NUM_VERSIONS" -gt 0 ]; then elif [ "$NUM_VERSIONS" -gt 0 ]; then
# TEMP: Don't include 6.0 versions # TEMP: Don't include 6.0 versions

View file

@ -4,6 +4,7 @@ set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")" ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh" . "$ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage { function usage {
echo "Usage: $0 id" echo "Usage: $0 id"
@ -15,5 +16,7 @@ if [[ -z "$id" ]]; then
usage usage
fi fi
xcrun notarytool log "$id" --apple-id "$NOTARIZATION_USER" --team-id "$NOTARIZATION_TEAM_ID" --password "$NOTARIZATION_PASSWORD" notary_log.json prepare_notary_auth
xcrun notarytool log "$id" "${notary_auth[@]}" notary_log.json
cat notary_log.json cat notary_log.json

View file

@ -4,6 +4,7 @@ set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ROOT_DIR="$(dirname "$SCRIPT_DIR")" ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$ROOT_DIR/config.sh" . "$ROOT_DIR/config.sh"
. "$SCRIPT_DIR/utils.sh"
function usage { function usage {
echo "Usage: $0 file" echo "Usage: $0 file"
@ -15,5 +16,7 @@ if [[ -z "$file" ]]; then
usage usage
fi fi
prepare_notary_auth
echo "Uploading ${file##*/} to Apple for notarization" >&2 echo "Uploading ${file##*/} to Apple for notarization" >&2
xcrun notarytool submit $file --apple-id "$NOTARIZATION_USER" --team-id "$NOTARIZATION_TEAM_ID" --password="$NOTARIZATION_PASSWORD" --wait xcrun notarytool submit $file "${notary_auth[@]}" --wait

View file

@ -71,7 +71,15 @@ def main():
tmp_src_dir = os.path.join(tmp_dir, 'zotero') tmp_src_dir = os.path.join(tmp_dir, 'zotero')
# Export a clean copy of the source tree # 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([ subprocess.check_call([
'rsync', 'rsync',
'-aL', '-aL',
@ -84,21 +92,21 @@ def main():
tmp_src_dir + os.sep tmp_src_dir + os.sep
]) ])
# Make sure rsync worked # Make sure the copy worked
d = os.path.join(tmp_src_dir, 'chrome') d = os.path.join(tmp_src_dir, 'chrome')
if not os.path.isdir(d): if not os.path.isdir(d):
raise FileNotFoundError(d + " not found") raise FileNotFoundError(d + " not found")
# Delete CSL locale support files # Delete CSL locale support files
subprocess.check_call([ csl_dir = os.path.normpath(tmp_src_dir + '/chrome/content/zotero/locale/csl/')
'find', for root, dirs, files in os.walk(csl_dir, topdown=False):
os.path.normpath(tmp_src_dir + '/chrome/content/zotero/locale/csl/'), for name in files:
'-mindepth', '1', if not name.endswith('.xml') and name != 'locales.json':
'!', '-name', '*.xml', os.remove(os.path.join(root, name))
'!', '-name', 'locales.json', for name in dirs:
#'-print', d = os.path.join(root, name)
'-delete' if not os.listdir(d):
]) os.rmdir(d)
# Delete styles build script # Delete styles build script
os.remove(os.path.join(tmp_src_dir, 'styles', 'update')) os.remove(os.path.join(tmp_src_dir, 'styles', 'update'))

View file

@ -0,0 +1,46 @@
#!/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

187
app/scripts/utils.sh Normal file
View file

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

View file

@ -1,4 +1,15 @@
#!/bin/bash #!/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 set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
@ -6,39 +17,79 @@ APP_ROOT_DIR="$(dirname "$SCRIPT_DIR")"
. "$APP_ROOT_DIR/config.sh" . "$APP_ROOT_DIR/config.sh"
cd "$APP_ROOT_DIR" cd "$APP_ROOT_DIR"
platform="" platform=""; arch=""
while getopts ":p:" opt; do
usage() {
cat >&2 <<'DONE'
Usage: xulrunner_hash.sh -p <m|w|l> [-a ARCH]
-p PLATFORM m (mac), w (windows), l (linux) [required]
-a ARCH *required* for Windows & Linux builds
Windows: x64 | arm64 | win32
Linux: x64 (or x86_64) | arm64
Mac: disallowed – always universal
DONE
exit 1
}
while getopts ':p:a:' opt; do
case $opt in case $opt in
p) p) platform="$OPTARG";;
platform="$OPTARG" a) arch="$OPTARG";;
;; :) echo "Option -$OPTARG requires an argument" >&2; usage;;
\?) ? ) echo "Invalid option: -$OPTARG" >&2; echo; usage;;
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac esac
shift $((OPTIND-1)); OPTIND=1
done done
[[ -z $platform ]] && { echo "-p is required" >&2; echo; usage; }
if [ $platform == "m" ]; then case $platform in
m)
[[ -n $arch ]] && { echo "-a is not allowed for Mac (universal build)" >&2; echo; usage; }
arch="universal"
GECKO_VERSION="$GECKO_VERSION_MAC" GECKO_VERSION="$GECKO_VERSION_MAC"
elif [ $platform == "w" ]; then ;;
w)
[[ -z $arch ]] && { echo "-a is required for Windows builds" >&2; echo; usage; }
# Map CLI-friendly aliases → canonical form
case $arch in
x64) arch="win-x64" ;;
arm64) arch="win-arm64" ;;
win32|win-x64|win-arm64) ;; # already canonical
*) echo "Invalid Windows arch: $arch" >&2; echo; usage;;
esac
GECKO_VERSION="$GECKO_VERSION_WIN" GECKO_VERSION="$GECKO_VERSION_WIN"
elif [ $platform == "l" ]; then ;;
l)
[[ -z $arch ]] && { echo "-a is required for Linux builds" >&2; echo; usage; }
[[ $arch == x64 ]] && arch="x86_64"
case $arch in
x86_64|arm64) ;;
*) echo "Invalid Linux arch: $arch" >&2; echo; usage;;
esac
GECKO_VERSION="$GECKO_VERSION_LINUX" 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 else
echo "Platform parameter incorrect. Usage: -p m(mac)/w(windows)/l(linux)" hash_arch_name="${arch/-/_}"
exit 1 arch_var_name="custom_components_hash_${hash_arch_name}"
components_hash=${!arch_var_name:-}
fi fi
xulrunner_content=$(< "$SCRIPT_DIR/fetch_xulrunner") input_string="${platform}-${arch}-${components_hash}|${GECKO_VERSION}|${xulrunner_content}"
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}'

View file

@ -97,6 +97,8 @@ if [[ $BUILD_MAC == 0 ]] && [[ $BUILD_WIN == 0 ]] && [[ $BUILD_LINUX == 0 ]]; th
usage usage
fi fi
TO_MAJOR_VERSION="${TO%%.*}"
rm -rf "$UPDATE_STAGE_DIR" rm -rf "$UPDATE_STAGE_DIR"
mkdir "$UPDATE_STAGE_DIR" mkdir "$UPDATE_STAGE_DIR"
@ -136,19 +138,43 @@ for version in "$FROM" "$TO"; do
mkdir -p "$versiondir" mkdir -p "$versiondir"
cd "$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" MAC_ARCHIVE="Zotero-${version}.dmg"
WIN32_ARCHIVE="Zotero-${version}_win32.zip" WIN32_ARCHIVE="Zotero-${version}_win32.zip"
WIN_X64_ARCHIVE="Zotero-${version}_win-x64.zip" WIN_X64_ARCHIVE="Zotero-${version}_win-x64.zip"
WIN_ARM64_ARCHIVE="Zotero-${version}_win-arm64.zip" WIN_ARM64_ARCHIVE="Zotero-${version}_win-arm64.zip"
LINUX_X86_ARCHIVE="Zotero-${version}_linux-i686.tar.bz2" LINUX_X86_ARCHIVE="Zotero-${version}_linux-i686.${LINUX_EXT}"
LINUX_X86_64_ARCHIVE="Zotero-${version}_linux-x86_64.tar.bz2" LINUX_X86_64_ARCHIVE="Zotero-${version}_linux-x86_64.${LINUX_EXT}"
LINUX_ARM64_ARCHIVE="Zotero-${version}_linux-arm64.${LINUX_EXT}"
CACHE_DIR="$ROOT_DIR/cache" CACHE_DIR="$ROOT_DIR/cache"
if [ ! -e "$CACHE_DIR" ]; then if [ ! -e "$CACHE_DIR" ]; then
mkdir "$CACHE_DIR" mkdir "$CACHE_DIR"
fi fi
for archive in "$MAC_ARCHIVE" "$WIN32_ARCHIVE" "$WIN_X64_ARCHIVE" "$WIN_ARM64_ARCHIVE" "$LINUX_X86_ARCHIVE" "$LINUX_X86_64_ARCHIVE"; do # Build archive list (no Linux arm64 before Zotero 8, no Linux i686 from Zotero 11)
ARCHIVES=(
"$MAC_ARCHIVE"
"$WIN32_ARCHIVE"
"$WIN_X64_ARCHIVE"
"$WIN_ARM64_ARCHIVE"
"$LINUX_X86_64_ARCHIVE"
)
if [ "$TO_MAJOR_VERSION" -lt 11 ]; then
ARCHIVES+=("$LINUX_X86_ARCHIVE")
fi
if [ "$MAJOR_VERSION" -ge 8 ]; then
ARCHIVES+=("$LINUX_ARM64_ARCHIVE")
fi
for archive in "${ARCHIVES[@]}"; do
if [[ $archive = "$MAC_ARCHIVE" ]] && [[ $BUILD_MAC != 1 ]]; then if [[ $archive = "$MAC_ARCHIVE" ]] && [[ $BUILD_MAC != 1 ]]; then
continue continue
fi fi
@ -167,6 +193,9 @@ for version in "$FROM" "$TO"; do
if [[ $archive = "$LINUX_X86_64_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then if [[ $archive = "$LINUX_X86_64_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue continue
fi fi
if [[ $archive = "$LINUX_ARM64_ARCHIVE" ]] && [[ $BUILD_LINUX != 1 ]]; then
continue
fi
ETAG_FILE="$CACHE_DIR/$archive.etag" ETAG_FILE="$CACHE_DIR/$archive.etag"
@ -186,10 +215,12 @@ for version in "$FROM" "$TO"; do
set +e set +e
# Cached version is available # Cached version is available
if [ -n "$ETAG" ]; then if [ -n "$ETAG" ]; then
NEW_ETAG=$(wget -nv -S --header "If-None-Match: $ETAG" $URL 2>&1 | awk '/ *ETag: */ {print $2}') HTTP_CODE=$(curl -sS -L -D headers.txt -o $archive -w '%{http_code}' -H "If-None-Match: $ETAG" "$URL")
NEW_ETAG=$(awk 'tolower($1) == "etag:" {print $2}' headers.txt | tr -d '\r' | tail -n 1)
rm -f headers.txt
# If ETag didn't match, cache newly downloaded version # If ETag didn't match, cache newly downloaded version
if [ -f $archive ]; then if [ "$HTTP_CODE" = 200 ]; then
echo "ETag for $archive didn't match! -- using new version" echo "ETag for $archive didn't match! -- using new version"
rm -f "$CACHE_DIR/$archive.etag" rm -f "$CACHE_DIR/$archive.etag"
cp $archive "$CACHE_DIR/" cp $archive "$CACHE_DIR/"
@ -200,7 +231,9 @@ for version in "$FROM" "$TO"; do
cp "$CACHE_DIR/$archive" . cp "$CACHE_DIR/$archive" .
fi fi
else else
NEW_ETAG=$(wget -nv -S $URL 2>&1 | awk '/ *ETag: */ {print $2}') curl -sS -f -L -D headers.txt -o $archive "$URL"
NEW_ETAG=$(awk 'tolower($1) == "etag:" {print $2}' headers.txt | tr -d '\r' | tail -n 1)
rm -f headers.txt
# Save archive to cache # Save archive to cache
rm -f "$CACHE_DIR/$archive.etag" rm -f "$CACHE_DIR/$archive.etag"
@ -210,8 +243,13 @@ for version in "$FROM" "$TO"; do
set -e set -e
done done
# Delete cached files older than 14 days # Delete cached archives older than 14 days
find "$CACHE_DIR" -ctime +14 -delete find "$CACHE_DIR" -maxdepth 1 -type f -ctime +14 -delete
# Delete cached diffs and compressed files not created or used in 60 days
# (cache hits touch the entries)
if [ -d "$CACHE_DIR/update-packaging" ]; then
find "$CACHE_DIR/update-packaging" -type f -ctime +60 -delete
fi
# Unpack Zotero.app # Unpack Zotero.app
if [ $BUILD_MAC == 1 ]; then if [ $BUILD_MAC == 1 ]; then
@ -232,9 +270,14 @@ for version in "$FROM" "$TO"; do
# Unpack Windows zips # Unpack Windows zips
if [ $BUILD_WIN == 1 ]; then if [ $BUILD_WIN == 1 ]; then
if [[ -f "$WIN32_ARCHIVE" ]] && [[ -f "$WIN_X64_ARCHIVE" ]] && [[ -f "$WIN_ARM64_ARCHIVE" ]]; 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 for build in "$WIN32_ARCHIVE" "$WIN_X64_ARCHIVE" "$WIN_ARM64_ARCHIVE"; do
unzip -q "$build" (unzip -q "$build" && rm "$build") &
rm "$build" UNPACK_PIDS="$UNPACK_PIDS $!"
done
for pid in $UNPACK_PIDS; do
wait $pid
done done
INCREMENTALS_FOUND=1 INCREMENTALS_FOUND=1
else else
@ -244,14 +287,36 @@ for version in "$FROM" "$TO"; do
# Unpack Linux tarballs # Unpack Linux tarballs
if [ $BUILD_LINUX == 1 ]; then if [ $BUILD_LINUX == 1 ]; then
if [[ -f "$LINUX_X86_ARCHIVE" ]] && [[ -f "$LINUX_X86_64_ARCHIVE" ]]; then # Zotero 7 has no Linux arm64 build; Zotero 11 has no Linux i686 build
for build in "$LINUX_X86_ARCHIVE" "$LINUX_X86_64_ARCHIVE"; do LINUX_BUILDS_TO_UNPACK=("$LINUX_X86_64_ARCHIVE")
tar -xjf "$build" if [ "$TO_MAJOR_VERSION" -lt 11 ]; then
rm "$build" LINUX_BUILDS_TO_UNPACK+=("$LINUX_X86_ARCHIVE")
fi
if [ "$MAJOR_VERSION" -ge 8 ]; then
LINUX_BUILDS_TO_UNPACK+=("$LINUX_ARM64_ARCHIVE")
fi
MISSING=0
for build in "${LINUX_BUILDS_TO_UNPACK[@]}"; do
if [ ! -f "$build" ]; then
MISSING=1
break
fi
done
if [ "$MISSING" -eq 0 ]; then
# Unpack architectures in parallel
UNPACK_PIDS=""
for build in "${LINUX_BUILDS_TO_UNPACK[@]}"; do
(tar -xf "$build" && rm "$build") &
UNPACK_PIDS="$UNPACK_PIDS $!"
done
for pid in $UNPACK_PIDS; do
wait $pid
done done
INCREMENTALS_FOUND=1 INCREMENTALS_FOUND=1
else else
echo "$LINUX_X86_ARCHIVE/$LINUX_X86_64_ARCHIVE not found" echo "Linux tarballs not found: ${LINUX_BUILDS_TO_UNPACK[*]}"
fi fi
fi fi
@ -262,8 +327,21 @@ done
export MOZ_PRODUCT_VERSION="$TO" export MOZ_PRODUCT_VERSION="$TO"
export MAR_CHANNEL_ID="$CHANNEL" 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 CHANGES_MADE=0
for build in "mac" "win32" "win-x64" "win-arm64" "linux-i686" "linux-x86_64"; do for build in "mac" "win32" "win-x64" "win-arm64" "linux-i686" "linux-x86_64" "linux-arm64"; do
# Zotero 7 has no Linux arm64 builds
if [[ $build == "linux-arm64" ]] && [[ $FROM == 7.* ]]; then
continue
fi
# Zotero 11 has no Linux i686 builds, so there's nothing to update to
if [[ $build == "linux-i686" ]] && [[ $TO_MAJOR_VERSION -ge 11 ]]; then
continue
fi
if [[ $build == "mac" ]]; then if [[ $build == "mac" ]]; then
if [[ $BUILD_MAC == 0 ]]; then if [[ $BUILD_MAC == 0 ]]; then
continue continue
@ -273,7 +351,7 @@ for build in "mac" "win32" "win-x64" "win-arm64" "linux-i686" "linux-x86_64"; do
if ([[ $build == "win32" ]] || [[ $build == "win-x64" ]] || [[ $build == "win-arm64" ]]) && [[ $BUILD_WIN == 0 ]]; then if ([[ $build == "win32" ]] || [[ $build == "win-x64" ]] || [[ $build == "win-arm64" ]]) && [[ $BUILD_WIN == 0 ]]; then
continue continue
fi fi
if ([[ $build == "linux-i686" ]] || [[ $build == "linux-x86_64" ]]) && [[ $BUILD_LINUX == 0 ]]; then if ([[ $build == "linux-i686" ]] || [[ $build == "linux-x86_64" ]] || [[ $build == "linux-arm64" ]]) && [[ $BUILD_LINUX == 0 ]]; then
continue continue
fi fi
dir="Zotero_$build" dir="Zotero_$build"

View file

@ -9,7 +9,9 @@
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
QUIET=0 # Preserve a value set by the parent so it survives in the child processes
# spawned by run_parallel_tasks()
QUIET=${QUIET:-0}
# By default just assume that these tools exist on our path # By default just assume that these tools exist on our path
MAR=${MAR:-mar} MAR=${MAR:-mar}
@ -33,7 +35,11 @@ if [ $? -ne 0 ]; then
fi fi
fi fi
fi fi
# Ensure that we're always using the right compression settings # Ensure that we're always using the right compression settings.
# -T1 (single-threaded) is required: multi-threaded xz produces non-deterministic
# output, which would break both the reproducibility of MARs and the content-hash
# cache in process_update_task(). See get_cache_tag() for how changes here are
# accounted for in the cache.
export XZ_OPT="-T1 -7e" export XZ_OPT="-T1 -7e"
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
@ -93,16 +99,14 @@ make_add_instruction() {
check_for_add_if_not_update() { check_for_add_if_not_update() {
add_if_not_file_chk="$1" add_if_not_file_chk="$1"
# XXX unconditional add-if for channel-prefs.js to fix mac signature on old installs (bug 1804303) if [[ "$(basename "$add_if_not_file_chk")" = "channel-prefs.js" || \
if [ "$add_if_not_file_chk" = "Contents/Resources/defaults/pref/channel-prefs.js" ]; then "$add_if_not_file_chk" =~ (^|/)ChannelPrefs\.framework/ || \
return 1 "$(basename "$add_if_not_file_chk")" = "update-settings.ini" || \
fi "$add_if_not_file_chk" =~ (^|/)UpdateSettings\.framework/ ]]; then
if [ `basename $add_if_not_file_chk` = "channel-prefs.js" -o \ ## "true"
`basename $add_if_not_file_chk` = "update-settings.ini" ]; then
## "true" *giggle*
return 0; return 0;
fi fi
## 'false'... because this is bash. Oh yay! ## "false"
return 1; return 1;
} }
@ -158,10 +162,10 @@ append_remove_instructions() {
if [ -n "$f" ]; then if [ -n "$f" ]; then
# Exclude comments # Exclude comments
if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then 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\"" verbose_notice " rmdir \"$f\""
echo "rmdir \"$f\"" >> "$filev3" echo "rmdir \"$f\"" >> "$filev3"
elif [ $(echo "$f" | grep -c '\/\*$') = 1 ]; then elif [ $(echo "$f" | grep -c '/\*$') = 1 ]; then
# Remove the * # Remove the *
f=$(echo "$f" | sed -e 's:\*$::') f=$(echo "$f" | sed -e 's:\*$::')
verbose_notice " rmrfdir \"$f\"" verbose_notice " rmrfdir \"$f\""
@ -177,6 +181,236 @@ append_remove_instructions() {
fi 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 "./" # List all files in the current directory, stripping leading "./"
# Pass a variable name and it will be filled as an array. # Pass a variable name and it will be filled as an array.
list_files() { list_files() {

View file

@ -13,6 +13,12 @@ set -eo pipefail
. $(dirname "$0")/common.sh . $(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() { print_usage() {
@ -50,7 +56,7 @@ archive="$1"
targetdir="$2" targetdir="$2"
# Prevent the workdir from being inside the targetdir so it isn't included in # Prevent the workdir from being inside the targetdir so it isn't included in
# the update mar. # the update mar.
if [ $(echo "$targetdir" | grep -c '\/$') = 1 ]; then if [ $(echo "$targetdir" | grep -c '/$') = 1 ]; then
# Remove the / # Remove the /
targetdir=$(echo "$targetdir" | sed -e 's:\/$::') targetdir=$(echo "$targetdir" | sed -e 's:\/$::')
fi fi
@ -88,6 +94,23 @@ notice ""
notice "Adding file add instructions to update manifests" notice "Adding file add instructions to update manifests"
num_files=${#files[*]} 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 for ((i=0; $i<$num_files; i=$i+1)); do
f="${files[$i]}" f="${files[$i]}"
@ -97,11 +120,6 @@ for ((i=0; $i<$num_files; i=$i+1)); do
make_add_instruction "$f" "$updatemanifestv3" make_add_instruction "$f" "$updatemanifestv3"
fi 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\"" targetfiles="$targetfiles \"$f\""
done done

View file

@ -13,6 +13,12 @@ set -eo pipefail
. $(dirname "$0")/common.sh . $(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() { print_usage() {
@ -81,7 +87,6 @@ if [ $# = 0 ]; then
exit 1 exit 1
fi fi
# channel-prefs.js removed for Zotero
requested_forced_updates='Contents/MacOS/firefox' requested_forced_updates='Contents/MacOS/firefox'
while getopts "hqf:" flag while getopts "hqf:" flag
@ -113,7 +118,7 @@ olddir="$2"
newdir="$3" newdir="$3"
# Prevent the workdir from being inside the targetdir so it isn't included in # Prevent the workdir from being inside the targetdir so it isn't included in
# the update mar. # the update mar.
if [ $(echo "$newdir" | grep -c '\/$') = 1 ]; then if [ $(echo "$newdir" | grep -c '/$') = 1 ]; then
# Remove the / # Remove the /
newdir=$(echo "$newdir" | sed -e 's:\/$::') newdir=$(echo "$newdir" | sed -e 's:\/$::')
fi fi
@ -165,94 +170,68 @@ num_oldfiles=${#oldfiles[*]}
remove_array= remove_array=
num_removes=0 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 for ((i=0; $i<$num_oldfiles; i=$i+1)); do
f="${oldfiles[$i]}" f="${oldfiles[$i]}"
# If this file exists in the new directory as well, then check if it differs. # If this file exists in the new directory as well, then check if it differs.
if [ -f "$newdir/$f" ]; then if [ -f "$newdir/$f" ]; then
if check_for_add_if_not_update "$f"; then if check_for_add_if_not_update "$f"; then
# The full workdir may not exist yet, so create it if necessary. echo "full$TAB$f" >> "$taskfile"
mkdir -p `dirname "$workdir/$f"` elif check_for_forced_update "$requested_forced_updates" "$f"; then
$XZ $XZ_OPT --compress $BCJ_OPTIONS --lzma2 --format=xz --check=crc64 --force --stdout "$newdir/$f" > "$workdir/$f" echo "full$TAB$f" >> "$taskfile"
copy_perm "$newdir/$f" "$workdir/$f" else
echo "diff$TAB$f" >> "$taskfile"
fi
fi
done
# Newly added files -- in the new directory but not the old one. (This replaces
# the O(n^2) nested skip loop from the upstream script.)
printf '%s\n' "${oldfiles[@]}" | sort > "$tmpdir/oldlist"
printf '%s\n' "${newfiles[@]}" | sort > "$tmpdir/newlist"
comm -13 "$tmpdir/oldlist" "$tmpdir/newlist" | sed '/^$/d' | sort -r > "$tmpdir/newonly"
while IFS= read -r f; do
echo "full$TAB$f" >> "$taskfile"
done < "$tmpdir/newonly"
export olddir newdir workdir MBSDIFF BCJ_OPTIONS QUIET
run_parallel_tasks "$taskfile" "$newdir" "$0"
# Assemble the manifest and archive list in the original order. A diffed file
# left $f.patch in the work directory if the patch was smaller, $f if the
# compressed file was smaller, and neither if the file was unchanged.
for ((i=0; $i<$num_oldfiles; i=$i+1)); do
f="${oldfiles[$i]}"
if [ -f "$newdir/$f" ]; then
if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3" make_add_if_not_instruction "$f" "$updatemanifestv3"
archivefiles="$archivefiles \"$f\"" archivefiles="$archivefiles \"$f\""
continue 1 elif check_for_forced_update "$requested_forced_updates" "$f"; then
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 make_add_instruction "$f" "$updatemanifestv3" 1
archivefiles="$archivefiles \"$f\"" archivefiles="$archivefiles \"$f\""
continue 1 elif [ -f "$workdir/$f.patch" ]; then
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" make_patch_instruction "$f" "$updatemanifestv3"
mv -f "$patchfile" "$workdir/$f.patch"
rm -f "$workdir/$f"
archivefiles="$archivefiles \"$f.patch\"" archivefiles="$archivefiles \"$f.patch\""
else elif [ -f "$workdir/$f" ]; then
make_add_instruction "$f" "$updatemanifestv3" make_add_instruction "$f" "$updatemanifestv3"
rm -f "$patchfile"
archivefiles="$archivefiles \"$f\"" archivefiles="$archivefiles \"$f\""
fi fi
fi
else else
# remove instructions are added after add / patch instructions for # remove instructions are added after add / patch instructions for
# consistency with make_incremental_updates.py # consistency with make_incremental_updates.py
@ -266,33 +245,18 @@ done
# Newly added files # Newly added files
notice "" notice ""
notice "Adding file add instructions to update manifests" 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 if check_for_add_if_not_update "$f"; then
make_add_if_not_instruction "$f" "$updatemanifestv3" make_add_if_not_instruction "$f" "$updatemanifestv3"
else else
make_add_instruction "$f" "$updatemanifestv3" make_add_instruction "$f" "$updatemanifestv3"
fi fi
archivefiles="$archivefiles \"$f\"" archivefiles="$archivefiles \"$f\""
done done < "$tmpdir/newonly"
rm -rf "$tmpdir"
notice "" notice ""
notice "Adding file remove instructions to update manifests" notice "Adding file remove instructions to update manifests"

View file

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

View file

@ -1,4 +1,4 @@
Contents/Frameworks/* Contents/Frameworks/XUL.framework/
Contents/MacOS/Info.plist Contents/MacOS/Info.plist
Contents/MacOS/LICENSE Contents/MacOS/LICENSE
Contents/MacOS/README.xulrunner Contents/MacOS/README.xulrunner
@ -38,12 +38,6 @@ Contents/MacOS/updater.app/Contents/MacOS/updater-bin
Contents/MacOS/updates.xml Contents/MacOS/updates.xml
Contents/MacOS/updates/* Contents/MacOS/updates/*
Contents/MacOS/zotero-bin 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/CONTRIBUTING.md
Contents/Resources/COPYING Contents/Resources/COPYING
Contents/Resources/README.md Contents/Resources/README.md

View file

@ -61,6 +61,6 @@ echo "MAR channel name: $channel"
echo "Product version: $version" echo "Product version: $version"
echo "Creating new MAR" echo "Creating new MAR"
$mar -V $version -H $channel -c "$outfile" `find . -type f | sed 's/^\.\///'` find . -type f -print0 | xargs -0 $mar -V $version -H $channel -c "$outfile"
echo echo
$mar -T "$outfile" $mar -T "$outfile"

View file

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

View file

@ -20,7 +20,7 @@ ROOT_DIR="$(dirname "$SCRIPT_DIR")"
#mkdir -p nsis #mkdir -p nsis
#cd nsis #cd nsis
#for plugin in $plugins; do #for plugin in $plugins; do
# wget $plugin # curl -fsSLO $plugin
#done #done
# #
#unzip_cmd="unzip -d Plugins -j" #unzip_cmd="unzip -d Plugins -j"

84
app/win/extract_xul_dll Executable file
View file

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

View file

@ -1,9 +1,9 @@
# Set Z_ARCH=win32 on command line to build and package win32 # Set Z_ARCH=win32 or Z_ARCH=aarch64 on command line
if [ "$Z_ARCH" == "win32" ]; then if [ "$Z_ARCH" == "win32" ]; then
ac_add_options --target=i686-pc-windows-msvc ac_add_options --target=i686-pc-windows-msvc
# https://bugzilla.mozilla.org/show_bug.cgi?id=1804548 # https://bugzilla.mozilla.org/show_bug.cgi?id=1804548
ac_add_options --without-wasm-sandboxed-libraries ac_add_options --without-wasm-sandboxed-libraries
elif [ "$Z_ARCH" == "aarch64" ]; then elif [ "$Z_ARCH" == "arm64" ]; then
ac_add_options --target=aarch64-pc-windows-msvc ac_add_options --target=aarch64-pc-windows-msvc
fi fi
@ -23,8 +23,17 @@ ac_add_options --disable-webrtc
ac_add_options --disable-eme ac_add_options --disable-eme
ac_add_options --enable-official-branding 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 MOZILLA_OFFICIAL=1
export RELEASE_OR_BETA=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= MOZ_REQUIRE_SIGNING=
# Build updater without MAR signature verification # Build updater without MAR signature verification

View file

@ -1,118 +0,0 @@
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];

179
app/win/mozilla-153.patch Normal file
View file

@ -0,0 +1,179 @@
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;

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:aa342dc2f3cb260505154fc823710955026f882ee8153bda8f4e592912c957de oid sha256:f1cb0ea50ccb7a609358131ae2a06e040761f012cc196ff0c436f4939895b156
size 363688 size 876988

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b676ce06f149e296b294cfea72003797eb53f874bcf3786e496191f38e5688e9 oid sha256:94ac6f2b12672e5dfe166287fce4d7bf357a77fac231b6c2818ad39d6a9d3f15
size 499708 size 614252

View file

@ -7,7 +7,6 @@ content zotero-platform chrome/content/zotero-platform/unix/ os=FreeBSD
content zotero-platform chrome/content/zotero-platform/unix/ os=OpenBSD 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/default-version/
content zotero-platform-version chrome/content/zotero-platform/mac-big-sur/ os=Darwin osversion>=11
resource zotero resource/ resource zotero resource/

View file

@ -23,7 +23,6 @@
***** END LICENSE BLOCK ***** ***** 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/include.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this); Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);

View file

@ -13,6 +13,17 @@
padding: 0; padding: 0;
overflow: hidden; 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> </style>
</head> </head>
<body> <body>
@ -23,6 +34,7 @@
<script> <script>
/** /**
* @param {Object} [params = {}] * @param {Object} [params = {}]
* @param {string} [params.emptyOverlayMessage] Optional content for the empty overlay
* @return {Promise<void>} * @return {Promise<void>}
*/ */
function loadMonaco(params = {}) { function loadMonaco(params = {}) {
@ -47,7 +59,6 @@
globalEditor = monaco; globalEditor = monaco;
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({ monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES6,
allowNonTsExtensions: true // needed for peeking definitions from in-memory models to work allowNonTsExtensions: true // needed for peeking definitions from in-memory models to work
}); });
@ -65,6 +76,30 @@
editor.getModel().setEOL(monaco.editor.EndOfLineSequence.LF); 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 () { window.onresize = function () {
editor.layout(); editor.layout();
}; };

File diff suppressed because it is too large Load diff

View file

@ -43,6 +43,7 @@
title="Scaffold" title="Scaffold"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml"
windowtype="zotero:scaffold"
style="display: flex;" style="display: flex;"
drawintitlebar-platforms="win, linux"> drawintitlebar-platforms="win, linux">
@ -55,15 +56,11 @@
</linkset> </linkset>
<script> <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/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/titlebar.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/platformKeys.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/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/translators.js", this);
Services.scriptloader.loadSubScript("chrome://scaffold/content/scaffold.js", this); Services.scriptloader.loadSubScript("chrome://scaffold/content/scaffold.js", this);
@ -76,6 +73,8 @@
// Custom elements // Custom elements
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this); 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); Services.scriptloader.loadSubScript("chrome://zotero/content/menuAccessKey.js", this);
</script> </script>
@ -139,15 +138,19 @@
</keyset> </keyset>
<popupset> <popupset>
<menupopup id="testing-context-menu"> <menupopup id="testing-context-menu" onpopupshowing="Scaffold.handleTestingContextMenuShowing()">
<menuitem label="&scaffold.testing.copyToClipboard;" tooltiptext="Copy the URL or data for the current test to the clipboard" oncommand="Scaffold.copyToClipboard()"/> <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()"/> <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;"> <menu id="testing-openURL" label="&scaffold.testing.openUrl;">
<menupopup> <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.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)"/> <menuitem label="&scaffold.testing.openUrl.externally;" tooltiptext="Open the URL for the current test in your default browser" oncommand="Scaffold.openURL(true)"/>
</menupopup> </menupopup>
</menu> </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> </menupopup>
</popupset> </popupset>
@ -290,11 +293,16 @@
</keyset> </keyset>
<!-- nsMenuBarX hides these and uses them to build the Application menu. --> <!-- 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"> <menupopup id="menu_FilePopup">
<menuitem id="menu_preferences" <menuitem id="menu_preferences"
label="&preferencesCmdMac.label;" label="&preferencesCmdMac.label;"
key="key_preferencesCmdMac" key="key_preferencesCmdMac"
oncommand="Zotero.Utilities.Internal.openPreferences()"/> 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" <menuitem id="menu_mac_services"
label="&servicesMenuMac.label;"/> label="&servicesMenuMac.label;"/>
<menuitem id="menu_mac_hide_app" <menuitem id="menu_mac_hide_app"
@ -479,26 +487,22 @@
</keyset> </keyset>
<listheader> <listheader>
<treecol label="&scaffold.testing.input.label;" flex="1"/> <treecol label="&scaffold.testing.input.label;" class="col-input"/>
<treecol label="&scaffold.testing.status.label;" width="150"/> <treecol label="&scaffold.testing.status.label;" class="col-status"/>
<treecol label="&scaffold.testing.defer.label;" width="75"/> <treecol label="&scaffold.testing.defer.label;" class="col-defer"/>
</listheader> </listheader>
<richlistbox <richlistbox
id="testing-listbox" id="testing-listbox"
observes="validate-tests" observes="validate-tests"
flex="1" flex="1"
seltype="multiple" seltype="multiple"
onselect="Scaffold.handleTestSelect(event)"
context="testing-context-menu" context="testing-context-menu"
focus-on-tab-select="true" focus-on-tab-select="true"
onselect="Scaffold.handleTestingListboxSelect()"
ondblclick="Scaffold.handleTestingListboxDblClick()"
/> />
</vbox> </vbox>
<hbox id="testing-buttons"> <hbox pack="end">
<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 <checkbox
id="checkbox-remember-cookies" id="checkbox-remember-cookies"
label="&scaffold.testing.rememberCookies;" label="&scaffold.testing.rememberCookies;"
@ -506,7 +510,6 @@
checked="true" checked="true"
/> />
</hbox> </hbox>
</hbox>
<iframe src="monaco/monaco.html" id="editor-tests" onmousedown="this.focus()"/> <iframe src="monaco/monaco.html" id="editor-tests" onmousedown="this.focus()"/>
</vbox> </vbox>
</tabpanel> </tabpanel>
@ -515,7 +518,9 @@
<hbox id="browser-url-container" align="center"> <hbox id="browser-url-container" align="center">
<label control="browser-url" value="&scaffold.tabUrl.label;"/> <label control="browser-url" value="&scaffold.tabUrl.label;"/>
<html:input id="browser-url" style="flex: 1;"/> <html:input id="browser-url" style="flex: 1;"/>
<button observes="validate-tests" label="&scaffold.testing.create.web;" tooltiptext="Create a new test from the current page" oncommand="Scaffold.saveTestFromCurrent('web')"/> <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')"/>
</hbox> </hbox>
<browser <browser
@ -542,6 +547,7 @@
<splitter id="pane-splitter" resizeafter="farthest" /> <splitter id="pane-splitter" resizeafter="farthest" />
<vbox id="right-pane"> <vbox id="right-pane">
<html:textarea id="output" readonly="true" data-l10n-id="scaffold-translator-output"/> <html:textarea id="output" readonly="true" data-l10n-id="scaffold-translator-output"/>
<scaffold-item-previews id="item-previews" hidden="true"/>
</vbox> </vbox>
</hbox> </hbox>

View file

@ -0,0 +1,219 @@
/*
***** 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);
}

View file

@ -0,0 +1,146 @@
/*
***** 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="&lt;" class="previous"/>
<html:div><html:span class="current"/>/<html:span class="max"/></html:div>
<button label="&gt;" 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);
}

View file

@ -43,8 +43,8 @@ Zotero_Ingester_Interface_SelectItems.init = function () {
() => Zotero_Ingester_Interface_SelectItems.acceptSelection()); () => Zotero_Ingester_Interface_SelectItems.acceptSelection());
// Set font size from pref // Set font size from pref
var sbc = document.getElementById('zotero-select-items-container'); var dialog = document.querySelector('dialog');
Zotero.UIProperties.registerRoot(sbc); Zotero.UIProperties.registerRoot(dialog);
this.io = window.arguments[0]; this.io = window.arguments[0];
var listbox = document.getElementById("zotero-selectitems-links"); var listbox = document.getElementById("zotero-selectitems-links");
@ -61,11 +61,13 @@ Zotero_Ingester_Interface_SelectItems.init = function () {
title = item; title = item;
} }
let itemNode = document.createXULElement("richlistitem");
let checkbox = document.createXULElement("checkbox"); let checkbox = document.createXULElement("checkbox");
checkbox.checked = checked; checkbox.checked = checked;
checkbox.label = title; checkbox.label = title;
itemNode.setAttribute("value", i); checkbox.setAttribute('native', 'true');
let itemNode = document.createXULElement("richlistitem");
itemNode.value = i;
itemNode.append(checkbox); itemNode.append(checkbox);
itemNode.addEventListener('click', (event) => { itemNode.addEventListener('click', (event) => {
if (event.target == itemNode) { if (event.target == itemNode) {

View file

@ -42,21 +42,16 @@
id="zotero-selectitems"> id="zotero-selectitems">
<script> <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/include.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/titlebar.js", this); Services.scriptloader.loadSubScript("chrome://zotero/content/titlebar.js", this);
Services.scriptloader.loadSubScript("chrome://scaffold/content/select.js", this); Services.scriptloader.loadSubScript("chrome://scaffold/content/select.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this); Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);
</script> </script>
<vbox id="zotero-select-items-container" flex="1"> <richlistbox id="zotero-selectitems-links"></richlistbox>
<box flex="1">
<richlistbox id="zotero-selectitems-links" flex="1"></richlistbox>
</box>
<hbox id="zotero-select-items-buttons"> <hbox id="zotero-select-items-buttons">
<button label="Select All" oncommand="Zotero_Ingester_Interface_SelectItems.selectAll()"/> <button label="Select All" oncommand="Zotero_Ingester_Interface_SelectItems.selectAll()"/>
<button label="Deselect All" oncommand="Zotero_Ingester_Interface_SelectItems.selectAll(true)"/> <button label="Deselect All" oncommand="Zotero_Ingester_Interface_SelectItems.selectAll(true)"/>
</hbox> </hbox>
</vbox>
</dialog> </dialog>
</window> </window>

View file

@ -0,0 +1,77 @@
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();
}
}

View file

@ -1,4 +0,0 @@
/* Quick Format dialog, which is based on window corners, which are different on Big Sur */
#quick-format-iframe {
margin-top: 2px;
}

View file

@ -9,11 +9,6 @@ window.citation-dialog {
padding: 0; padding: 0;
} }
.citation-dialog.progress-bar .citation-dialog.deck {
height: 37px;
}
.citation-dialog.entry { .citation-dialog.entry {
background: -moz-linear-gradient(-90deg, rgb(243,123,119) 0, rgb(180,47,38) 50%, rgb(156,36,27) 50%); background: -moz-linear-gradient(-90deg, rgb(243,123,119) 0, rgb(180,47,38) 50%, rgb(156,36,27) 50%);

View file

@ -10,6 +10,11 @@ tab {
border-right: 1px solid hsla(0, 0%, 0%, 0.2); border-right: 1px solid hsla(0, 0%, 0%, 0.2);
border-left: 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. */ /* Fixes misc Ubuntu 16.10 rendering issue fixes. */
#zotero-prefs menulist dropmarker{ #zotero-prefs menulist dropmarker{
width: 30px; width: 30px;
@ -17,13 +22,3 @@ tab {
#zotero-prefs .numberbox-input-box{ #zotero-prefs .numberbox-input-box{
-moz-appearance: textfield; -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);
}

View file

@ -23,15 +23,11 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*/ */
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.defineESModuleGetters(globalThis, {
ChromeUtils.defineESModuleGetters(this, {
Zotero: "chrome://zotero/content/zotero.mjs", Zotero: "chrome://zotero/content/zotero.mjs",
}); });
var EXPORTED_SYMBOLS = ["BlockingObserver"]; export class BlockingObserver {
class BlockingObserver {
shouldBlock; shouldBlock;
_observerAdded = false; _observerAdded = false;

View file

@ -23,9 +23,7 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*/ */
var EXPORTED_SYMBOLS = ["EPUB"]; ChromeUtils.defineESModuleGetters(globalThis, {
ChromeUtils.defineESModuleGetters(this, {
Zotero: "chrome://zotero/content/zotero.mjs", Zotero: "chrome://zotero/content/zotero.mjs",
}); });
@ -38,7 +36,7 @@ const ZipReader = Components.Constructor(
const DC_NS = 'http://purl.org/dc/elements/1.1/'; const DC_NS = 'http://purl.org/dc/elements/1.1/';
const OPF_NS = 'http://www.idpf.org/2007/opf'; const OPF_NS = 'http://www.idpf.org/2007/opf';
class EPUB { export class EPUB {
_zipReader; _zipReader;
_contentOPF = null; _contentOPF = null;

View file

@ -24,30 +24,27 @@
*/ */
var EXPORTED_SYMBOLS = ["HiddenBrowser"]; import { BlockingObserver } from "chrome://zotero/content/BlockingObserver.mjs";
import "chrome://zotero/content/actors/ActorManager.mjs";
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); /* global HiddenFrame, E10SUtils */
const { BlockingObserver } = ChromeUtils.import("chrome://zotero/content/BlockingObserver.jsm"); ChromeUtils.defineESModuleGetters(globalThis, {
E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
ChromeUtils.import("chrome://zotero/content/actors/ActorManager.jsm"); HiddenFrame: "resource://gre/modules/HiddenFrame.sys.mjs",
Zotero: "chrome://zotero/content/zotero.mjs",
/* global HiddenFrame, E10SUtils, this */ setTimeout: "resource://gre/modules/Timer.sys.mjs"
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(); const progressListeners = new Set();
// From nsSandboxFlags.h
const SANDBOXED_ORIGIN = 0x10;
const SANDBOXED_SCRIPTS = 0x80;
/** /**
* Functions for creating and destroying hidden browser objects * Functions for creating and destroying hidden browser objects
**/ **/
class HiddenBrowser { export class HiddenBrowser {
/** /**
* @param {Object} options * @param {Object} options
* @param {Boolean} [options.allowJavaScript] * @param {Boolean} [options.allowJavaScript]
@ -55,10 +52,13 @@ class HiddenBrowser {
* @param {Boolean} [options.blockRemoteResources] Block all remote (non-file:) resources * @param {Boolean} [options.blockRemoteResources] Block all remote (non-file:) resources
* @param {Boolean} [options.useHiddenFrame=true] Use a hidden frame to create the browser. * @param {Boolean} [options.useHiddenFrame=true] Use a hidden frame to create the browser.
* Must be set to false if intending to call print(). * Must be set to false if intending to call print().
* @param {Zotero.CookieSandbox} [options.cookieSandbox] * @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
*/ */
constructor(options = {}) { constructor(options = {}) {
this._destroyed = false; this._destroyed = false;
this._allowJavaScript = options.allowJavaScript !== false;
this._createdPromise = (async () => { this._createdPromise = (async () => {
let doc; let doc;
if (options.useHiddenFrame !== false) { if (options.useHiddenFrame !== false) {
@ -66,7 +66,6 @@ class HiddenBrowser {
this._frame = frame; this._frame = frame;
var windowlessBrowser = await frame.get(); var windowlessBrowser = await frame.get();
windowlessBrowser.browsingContext.allowJavascript = options.allowJavaScript !== false;
windowlessBrowser.docShell.allowImages = false; windowlessBrowser.docShell.allowImages = false;
if (options.docShell) { if (options.docShell) {
Object.assign(windowlessBrowser.docShell, options.docShell); Object.assign(windowlessBrowser.docShell, options.docShell);
@ -87,17 +86,12 @@ class HiddenBrowser {
browser.setAttribute("remote", "true"); browser.setAttribute("remote", "true");
browser.setAttribute('maychangeremoteness', 'true'); browser.setAttribute('maychangeremoteness', 'true');
browser.setAttribute("disableglobalhistory", "true"); browser.setAttribute("disableglobalhistory", "true");
if (options.userContextId) {
browser.setAttribute("usercontextid", String(options.userContextId));
}
browser.style.display = "none"; browser.style.display = "none";
doc.documentElement.appendChild(browser); doc.documentElement.appendChild(browser);
if (options.cookieSandbox) {
options.cookieSandbox.attachToBrowser(browser);
}
if (Zotero.Debug.enabled) {
startLeakWarningTimer(browser);
}
if (options.blockRemoteResources) { if (options.blockRemoteResources) {
this._blockingObserver = new BlockingObserver({ this._blockingObserver = new BlockingObserver({
shouldBlock(uri) { shouldBlock(uri) {
@ -107,6 +101,17 @@ class HiddenBrowser {
this._blockingObserver.register(browser); 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; this._browser = browser;
})(); })();
@ -121,15 +126,16 @@ class HiddenBrowser {
set(target, prop, val) { set(target, prop, val) {
if (prop in target) { if (prop in target) {
target[prop] = val; target[prop] = val;
}
Reflect.set(target._browser, prop, val)
return true; return true;
} }
else {
return Reflect.set(target._browser, prop, val);
}
}
}); });
} }
/** /**
*
* @param {String} source - HTTP URL, file: URL, or file path * @param {String} source - HTTP URL, file: URL, or file path
* @param {Object} [options] * @param {Object} [options]
* @param {Boolean} [options.requireSuccessfulStatus] * @param {Boolean} [options.requireSuccessfulStatus]
@ -149,18 +155,12 @@ class HiddenBrowser {
Zotero.debug(`Loading ${uri} in hidden browser`); Zotero.debug(`Loading ${uri} in hidden browser`);
// Next bit adapted from Mozilla's HeadlessShell.jsm // Next bit adapted from Mozilla's HeadlessShell.jsm
try { try {
// Figure out whether the browser should be remote. We actually // Figure out whether the browser should be remote
// perform the load in PageDataChild, but remoteness changes let remoteType = ChromeUtils.predictRemoteTypeForURI(uri, {
// need to happen here. preferredRemoteType: E10SUtils.DEFAULT_REMOTE_TYPE,
let oa = E10SUtils.predictOriginAttributes({ browser: this }); useRemoteTabs: true,
let remoteType = E10SUtils.getRemoteTypeForURI( useRemoteSubframes: false
uri, });
true,
false,
E10SUtils.DEFAULT_REMOTE_TYPE,
null,
oa
);
if (this.remoteType !== remoteType) { if (this.remoteType !== remoteType) {
// The following functions need to be called on the <browser> directly, // The following functions need to be called on the <browser> directly,
// not through our proxy (aka 'this') // not through our proxy (aka 'this')
@ -215,12 +215,18 @@ class HiddenBrowser {
); );
}); });
let loadURISuccess = await this.browsingContext.currentWindowGlobal.getActor("PageData") let prepared = await this.browsingContext.currentWindowGlobal.getActor("PageData")
.sendQuery("loadURI", { uri }); .sendQuery("prepareLoad");
if (!loadURISuccess) { if (!prepared) {
Zotero.logError(new Error("Load failed")); Zotero.logError(new Error("Load failed"));
return false; 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; await loadCompletePromise;
} }
catch (e) { catch (e) {
@ -234,7 +240,7 @@ class HiddenBrowser {
let response = `${channelInfo.responseStatus} ${channelInfo.responseStatusText}`; let response = `${channelInfo.responseStatus} ${channelInfo.responseStatusText}`;
Zotero.debug(`HiddenBrowser.load: ${uri} failed with ${response}`, 2); Zotero.debug(`HiddenBrowser.load: ${uri} failed with ${response}`, 2);
// HiddenBrowser will never get returned so we need to clean it up here // HiddenBrowser will never get returned so we need to clean it up here
this.destroy() this.destroy();
throw new Zotero.HTTP.UnexpectedStatusException( throw new Zotero.HTTP.UnexpectedStatusException(
{ {
status: channelInfo.responseStatus status: channelInfo.responseStatus
@ -244,16 +250,38 @@ 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 {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) { async getPageData(props, { timeout = 30000 } = {}) {
var actor = this.browsingContext.currentWindowGlobal.getActor("PageData"); var actor = this.browsingContext.currentWindowGlobal.getActor("PageData");
var data = {}; var data = {};
for (let prop of props) { for (let prop of props) {
data[prop] = await actor.sendQuery(prop); 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]);
} }
return data; return data;
} }
@ -313,21 +341,3 @@ 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);
}

View file

@ -23,20 +23,14 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*/ */
var EXPORTED_SYMBOLS = ["RemoteTranslate"]; ChromeUtils.defineESModuleGetters(globalThis, {
ChromeUtils.import("chrome://zotero/content/actors/ActorManager.jsm");
ChromeUtils.defineESModuleGetters(this, {
Zotero: "chrome://zotero/content/zotero.mjs", 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",
}); });
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); export class RemoteTranslate {
XPCOMUtils.defineLazyModuleGetters(this, {
TranslationManager: "chrome://zotero/content/actors/TranslationParent.jsm",
ZOTERO_CONFIG: "resource://zotero/config.js",
});
class RemoteTranslate {
_browser = null; _browser = null;
_id = Zotero.Utilities.randomString(); _id = Zotero.Utilities.randomString();
@ -86,8 +80,7 @@ class RemoteTranslate {
* Set a handler on the proxied Zotero.Translate instance. * Set a handler on the proxied Zotero.Translate instance.
* The handler function is passed this RemoteTranslate as its first argument. * The handler function is passed this RemoteTranslate as its first argument.
* *
* Supports all Zotero.Translate handlers in addition to newTestDetectionFailed, which can be called from * Supports all Zotero.Translate handlers.
* {@link newTest} if detection fails to confirm the creation of an expected-fail test.
* *
* @param {String} name * @param {String} name
* @param {Function} handler * @param {Function} handler
@ -248,11 +241,13 @@ class RemoteTranslate {
/** /**
* Create a test on the browser's current page. * 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) * @return {Promise<Object | null>} Resolves to the created test object (null on error)
*/ */
newTest() { newTest(testInit) {
let actor = this._browser.browsingContext.currentWindowGlobal.getActor("Translation"); let actor = this._browser.browsingContext.currentWindowGlobal.getActor("Translation");
return actor.sendQuery("newTest", { translator: this._translator, id: this._id }); return actor.sendQuery("newTest", { translator: this._translator, testInit, id: this._id });
} }
/** /**

View file

@ -27,16 +27,12 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*/ */
const EXPORTED_SYMBOLS = ['ZoteroProtocolHandler'];
const ZOTERO_SCHEME = "zotero"; const ZOTERO_SCHEME = "zotero";
const ZOTERO_PROTOCOL_CID = Components.ID("{9BC3D762-9038-486A-9D70-C997AF848A7C}"); 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_CONTRACTID = "@mozilla.org/network/protocol;1?name=" + ZOTERO_SCHEME;
const ZOTERO_PROTOCOL_NAME = "Zotero Chrome Extension Protocol"; const ZOTERO_PROTOCOL_NAME = "Zotero Chrome Extension Protocol";
Components.utils.import("resource://gre/modules/Services.jsm"); import { NetUtil } from "resource://gre/modules/NetUtil.sys.mjs";
Components.utils.import("resource://gre/modules/ComponentUtils.jsm");
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
const Cc = Components.classes; const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;
@ -46,9 +42,9 @@ const ios = Services.io;
// Dummy chrome URL used to obtain a valid chrome channel // Dummy chrome URL used to obtain a valid chrome channel
const DUMMY_CHROME_URL = "chrome://zotero/content/zoteroPane.xul"; const DUMMY_CHROME_URL = "chrome://zotero/content/zoteroPane.xul";
var { Zotero } = ChromeUtils.importESModule("chrome://zotero/content/zotero.mjs"); import { Zotero } from "chrome://zotero/content/zotero.mjs";
function ZoteroProtocolHandler() { export function ZoteroProtocolHandler() {
this.wrappedJSObject = this; this.wrappedJSObject = this;
this._principal = null; this._principal = null;
this._extensions = {}; this._extensions = {};
@ -67,13 +63,13 @@ function ZoteroProtocolHandler() {
loadAsChrome: false, loadAsChrome: false,
newChannel: function (uri, loadInfo) { newChannel: function (uri, loadInfo) {
return new AsyncChannel(uri, loadInfo, function* () { return new AsyncChannel(uri, loadInfo, async function () {
try { try {
var uriPath = uri.pathQueryRef; var uriPath = uri.pathQueryRef;
if (!uriPath) { if (!uriPath) {
return this._errorChannel('Invalid URL'); return this._errorChannel('Invalid URL');
} }
uriPath = uriPath.substr('//attachment/'.length); uriPath = uriPath.substring(1);
var params = {}; var params = {};
var router = new Zotero.Router(params); var router = new Zotero.Router(params);
@ -89,7 +85,7 @@ function ZoteroProtocolHandler() {
if (!params.itemKey) { if (!params.itemKey) {
return this._errorChannel("Item key not provided"); return this._errorChannel("Item key not provided");
} }
var item = yield Zotero.Items.getByLibraryAndKeyAsync(params.libraryID, params.itemKey); var item = await Zotero.Items.getByLibraryAndKeyAsync(params.libraryID, params.itemKey);
if (!item) { if (!item) {
return this._errorChannel(`No item found for ${uriPath}`); return this._errorChannel(`No item found for ${uriPath}`);
@ -98,7 +94,7 @@ function ZoteroProtocolHandler() {
return this._errorChannel(`Item for ${uriPath} is not a file attachment`); return this._errorChannel(`Item for ${uriPath} is not a file attachment`);
} }
var path = yield item.getFilePathAsync(); var path = await item.getFilePathAsync();
if (!path) { if (!path) {
return this._errorChannel(`${path} not found`); return this._errorChannel(`${path} not found`);
} }
@ -118,7 +114,7 @@ function ZoteroProtocolHandler() {
Zotero.logError(e); Zotero.logError(e);
return this._errorChannel(`Resource ${resourcePathParts.join('/')} not found`); return this._errorChannel(`Resource ${resourcePathParts.join('/')} not found`);
} }
if (!(yield IOUtils.exists(path))) { if (!(await IOUtils.exists(path))) {
return this._errorChannel(`Resource ${resourcePathParts.join('/')} not found`); return this._errorChannel(`Resource ${resourcePathParts.join('/')} not found`);
} }
} }
@ -155,7 +151,7 @@ function ZoteroProtocolHandler() {
loadAsChrome: false, loadAsChrome: false,
newChannel: function (uri, loadInfo) { newChannel: function (uri, loadInfo) {
return new AsyncChannel(uri, loadInfo, function* () { return new AsyncChannel(uri, loadInfo, async function () {
this.contentType = 'text/plain'; this.contentType = 'text/plain';
var path = uri.spec.match(/zotero:\/\/[^/]+(.*)/)[1]; var path = uri.spec.match(/zotero:\/\/[^/]+(.*)/)[1];
@ -182,14 +178,14 @@ function ZoteroProtocolHandler() {
loadAsChrome: true, loadAsChrome: true,
newChannel: function (uri, loadInfo) { newChannel: function (uri, loadInfo) {
return new AsyncChannel(uri, loadInfo, function* () { return new AsyncChannel(uri, loadInfo, async function () {
var userLibraryID = Zotero.Libraries.userLibraryID; var userLibraryID = Zotero.Libraries.userLibraryID;
var path = uri.pathQueryRef; var path = uri.pathQueryRef;
if (!path) { if (!path) {
return 'Invalid URL'; return 'Invalid URL';
} }
path = path.substr('//report/'.length); path = path.substring(1);
// Proxy CSS files // Proxy CSS files
if (path.endsWith('.css')) { if (path.endsWith('.css')) {
@ -270,7 +266,7 @@ function ZoteroProtocolHandler() {
try { try {
Zotero.API.parseParams(params); Zotero.API.parseParams(params);
var results = yield Zotero.API.getResultsFromParams(params); var results = await Zotero.API.getResultsFromParams(params);
} }
catch (e) { catch (e) {
Zotero.debug(e, 1); Zotero.debug(e, 1);
@ -320,7 +316,7 @@ function ZoteroProtocolHandler() {
for (let id of searchItemIDs) { for (let id of searchItemIDs) {
if (!searchChildIDs.has(id)) { if (!searchChildIDs.has(id)) {
var children = []; var children = [];
var item = yield Zotero.Items.getAsync(id); var item = await Zotero.Items.getAsync(id);
if (!item.isRegularItem()) { if (!item.isRegularItem()) {
continue; continue;
} }
@ -351,7 +347,7 @@ function ZoteroProtocolHandler() {
// Add parents of matches if parents aren't matches themselves // Add parents of matches if parents aren't matches themselves
for (let id of searchParentIDs) { for (let id of searchParentIDs) {
if (!searchItemIDs.has(id) && !itemsHash[id]) { if (!searchItemIDs.has(id) && !itemsHash[id]) {
var item = yield Zotero.Items.getAsync(id); var item = await Zotero.Items.getAsync(id);
itemsHash[id] = items.length; itemsHash[id] = items.length;
items.push(item.toJSON({ mode: 'full' })); items.push(item.toJSON({ mode: 'full' }));
} }
@ -359,7 +355,7 @@ function ZoteroProtocolHandler() {
// Add children to reportChildren property of parents // Add children to reportChildren property of parents
for (let id of searchChildIDs) { for (let id of searchChildIDs) {
let item = yield Zotero.Items.getAsync(id); let item = await Zotero.Items.getAsync(id);
var parentID = item.parentID; var parentID = item.parentID;
if (!items[itemsHash[parentID]].reportChildren) { if (!items[itemsHash[parentID]].reportChildren) {
items[itemsHash[parentID]].reportChildren = { items[itemsHash[parentID]].reportChildren = {
@ -379,7 +375,7 @@ function ZoteroProtocolHandler() {
// for each matching child // for each matching child
else { else {
for (let id of searchChildIDs) { for (let id of searchChildIDs) {
var item = yield Zotero.Items.getAsync(id); var item = await Zotero.Items.getAsync(id);
var parentID = item.parentID; var parentID = item.parentID;
var parentItem = Zotero.Items.get(parentID); var parentItem = Zotero.Items.get(parentID);
@ -423,7 +419,7 @@ function ZoteroProtocolHandler() {
var collation = Zotero.getLocaleCollation(); var collation = Zotero.getLocaleCollation();
var compareFunction = function(a, b) { var compareFunction = function (a, b) {
var index = 0; var index = 0;
// Multidimensional sort // Multidimensional sort
@ -544,267 +540,6 @@ 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 * Select an item
@ -820,15 +555,14 @@ function ZoteroProtocolHandler() {
var SelectExtension = { var SelectExtension = {
noContent: true, noContent: true,
doAction: Zotero.Promise.coroutine(function* (uri) { doAction: async function (uri) {
var userLibraryID = Zotero.Libraries.userLibraryID; var userLibraryID = Zotero.Libraries.userLibraryID;
var path = uri.pathQueryRef; var path = uri.pathQueryRef;
if (!path) { if (!path) {
return 'Invalid URL'; return 'Invalid URL';
} }
path = path.substr('//select/'.length); path = path.substring(1);
var mimeType, content = '';
var params = { var params = {
objectType: 'item' objectType: 'item'
@ -886,7 +620,7 @@ function ZoteroProtocolHandler() {
return; return;
} }
var results = yield Zotero.API.getResultsFromParams(params); var results = await Zotero.API.getResultsFromParams(params);
if (!results.length) { if (!results.length) {
var msg = "Objects not found"; var msg = "Objects not found";
@ -921,7 +655,7 @@ function ZoteroProtocolHandler() {
else { else {
col = Zotero.Collections.get(params.scopeObjectID); col = Zotero.Collections.get(params.scopeObjectID);
} }
yield zp.collectionsView.selectCollection(col.id); await zp.collectionsView.selectCollection(col.id);
} }
else if (params.scopeObject == 'searches') { else if (params.scopeObject == 'searches') {
let s; let s;
@ -933,15 +667,15 @@ function ZoteroProtocolHandler() {
else { else {
s = Zotero.Searches.get(params.scopeObjectID); s = Zotero.Searches.get(params.scopeObjectID);
} }
yield zp.collectionsView.selectSearch(s.id); await zp.collectionsView.selectSearch(s.id);
} }
// If collection not specified, select library root // If collection not specified, select library root
else { else {
yield zp.collectionsView.selectLibrary(params.libraryID); await zp.collectionsView.selectLibrary(params.libraryID);
} }
return zp.selectItems(results.map(x => x.id)); return zp.selectItems(results.map(x => x.id));
} }
}), },
newChannel: function (uri) { newChannel: function (uri) {
this.doAction(uri); this.doAction(uri);
@ -955,7 +689,7 @@ function ZoteroProtocolHandler() {
loadAsChrome: false, loadAsChrome: false,
newChannel: function (uri, loadInfo) { newChannel: function (uri, loadInfo) {
return new AsyncChannel(uri, loadInfo, function* () { return new AsyncChannel(uri, loadInfo, async function () {
this.contentType = "text/plain"; this.contentType = "text/plain";
try { try {
@ -969,99 +703,6 @@ 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/viewer.html
zotero://pdf.js/pdf/1/ABCD5678 zotero://pdf.js/pdf/1/ABCD5678
@ -1070,7 +711,7 @@ function ZoteroProtocolHandler() {
loadAsChrome: true, loadAsChrome: true,
newChannel: function (uri) { newChannel: function (uri) {
return new AsyncChannel(uri, function* () { return new AsyncChannel(uri, async function () {
try { try {
uri = uri.spec; uri = uri.spec;
// Proxy PDF.js files // Proxy PDF.js files
@ -1088,11 +729,11 @@ function ZoteroProtocolHandler() {
var [libraryID, key] = uri.substr(pdfPrefix.length).split('/'); var [libraryID, key] = uri.substr(pdfPrefix.length).split('/');
libraryID = parseInt(libraryID); libraryID = parseInt(libraryID);
var item = yield Zotero.Items.getByLibraryAndKeyAsync(libraryID, key); var item = await Zotero.Items.getByLibraryAndKeyAsync(libraryID, key);
if (!item) { if (!item) {
return this._errorChannel("Item not found"); return this._errorChannel("Item not found");
} }
var path = yield item.getFilePathAsync(); var path = await item.getFilePathAsync();
if (!path) { if (!path) {
return this._errorChannel("File not found"); return this._errorChannel("File not found");
} }
@ -1146,7 +787,7 @@ function ZoteroProtocolHandler() {
if (!uriPath) { if (!uriPath) {
return 'Invalid URL'; return 'Invalid URL';
} }
uriPath = uriPath.replace(/^\/\/open(-pdf)?\//, ''); uriPath = uriPath.substring(1);
var params = { var params = {
objectType: 'item' objectType: 'item'
@ -1246,10 +887,8 @@ function ZoteroProtocolHandler() {
this._extensions[ZOTERO_SCHEME + "://attachment"] = AttachmentExtension; this._extensions[ZOTERO_SCHEME + "://attachment"] = AttachmentExtension;
this._extensions[ZOTERO_SCHEME + "://data"] = DataExtension; this._extensions[ZOTERO_SCHEME + "://data"] = DataExtension;
this._extensions[ZOTERO_SCHEME + "://report"] = ReportExtension; this._extensions[ZOTERO_SCHEME + "://report"] = ReportExtension;
this._extensions[ZOTERO_SCHEME + "://timeline"] = TimelineExtension;
this._extensions[ZOTERO_SCHEME + "://select"] = SelectExtension; this._extensions[ZOTERO_SCHEME + "://select"] = SelectExtension;
this._extensions[ZOTERO_SCHEME + "://debug"] = DebugExtension; this._extensions[ZOTERO_SCHEME + "://debug"] = DebugExtension;
this._extensions[ZOTERO_SCHEME + "://connector"] = ConnectorExtension;
this._extensions[ZOTERO_SCHEME + "://pdf.js"] = PDFJSExtension; this._extensions[ZOTERO_SCHEME + "://pdf.js"] = PDFJSExtension;
this._extensions[ZOTERO_SCHEME + "://open"] = OpenExtension; this._extensions[ZOTERO_SCHEME + "://open"] = OpenExtension;
this._extensions[ZOTERO_SCHEME + "://open-pdf"] = OpenExtension; this._extensions[ZOTERO_SCHEME + "://open-pdf"] = OpenExtension;
@ -1382,14 +1021,14 @@ ZoteroProtocolHandler.init = function () {
/** /**
* nsIChannel implementation that takes a promise-yielding generator that returns a * nsIChannel implementation that takes an async function that returns a
* string, nsIAsyncInputStream, or file * string, nsIAsyncInputStream, or file
*/ */
function AsyncChannel(uri, loadInfo, gen) { function AsyncChannel(uri, loadInfo, func) {
this.URI = this.originalURI = uri; this.URI = this.originalURI = uri;
this.loadInfo = loadInfo; this.loadInfo = loadInfo;
this._generator = gen; this._function = func;
this._isPending = true; this._isPending = true;
// nsIRequest // nsIRequest
@ -1411,7 +1050,7 @@ function AsyncChannel(uri, loadInfo, gen) {
} }
AsyncChannel.prototype = { AsyncChannel.prototype = {
asyncOpen: Zotero.Promise.coroutine(function* (streamListener) { asyncOpen: async function (streamListener) {
if (this.loadGroup) this.loadGroup.addRequest(this, null); if (this.loadGroup) this.loadGroup.addRequest(this, null);
var channel = this; var channel = this;
@ -1456,17 +1095,17 @@ AsyncChannel.prototype = {
var data; var data;
try { try {
if (!data) { if (!data) {
data = yield Zotero.spawn(channel._generator, channel) data = await channel._function();
} }
if (typeof data == 'string') { if (typeof data == 'string') {
//Zotero.debug("AsyncChannel: Got string from generator"); //Zotero.debug("AsyncChannel: Got string from generator");
listenerWrapper.onStartRequest(this); listenerWrapper.onStartRequest(this);
let converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] let inputStream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter); .createInstance(Ci.nsIStringInputStream);
converter.charset = "UTF-8"; inputStream.setUTF8Data(data);
let inputStream = converter.convertToInputStream(data);
listenerWrapper.onDataAvailable(this, inputStream, 0, inputStream.available()); listenerWrapper.onDataAvailable(this, inputStream, 0, inputStream.available());
listenerWrapper.onStopRequest(this, this.status); listenerWrapper.onStopRequest(this, this.status);
@ -1497,11 +1136,10 @@ AsyncChannel.prototype = {
uri.QueryInterface(Ci.nsIURL); uri.QueryInterface(Ci.nsIURL);
this.contentType = Zotero.MIME.getMIMETypeFromExtension(uri.fileExtension); this.contentType = Zotero.MIME.getMIMETypeFromExtension(uri.fileExtension);
if (!this.contentType) { if (!this.contentType) {
let sample = yield Zotero.File.getSample(uri.spec); let sample = await Zotero.File.getSample(uri.spec);
this.contentType = Zotero.MIME.getMIMETypeFromData(sample); this.contentType = Zotero.MIME.getMIMETypeFromData(sample);
} }
Components.utils.import("resource://gre/modules/NetUtil.jsm");
NetUtil.asyncFetch({ uri: data, loadUsingSystemPrincipal: true }, function (inputStream, status) { NetUtil.asyncFetch({ uri: data, loadUsingSystemPrincipal: true }, function (inputStream, status) {
if (!Components.isSuccessCode(status)) { if (!Components.isSuccessCode(status)) {
reject(); reject();
@ -1544,7 +1182,7 @@ AsyncChannel.prototype = {
} }
catch (e) {} catch (e) {}
} }
}), },
// nsIRequest // nsIRequest
isPending: function () { isPending: function () {

View file

@ -10,7 +10,6 @@
onload="moveToAlertPosition(); sizeToContent(); document.getElementById('zotero-about').getButton('accept').focus();" onload="moveToAlertPosition(); sizeToContent(); document.getElementById('zotero-about').getButton('accept').focus();"
inwindowmenu="false"> inwindowmenu="false">
<script> <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/include.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this); Services.scriptloader.loadSubScript("chrome://zotero/content/customElements.js", this);
</script> </script>
@ -42,7 +41,7 @@
<p id="third-party-software"></p> <p id="third-party-software"></p>
<p id="get-involved"></p> <p id="get-involved"></p>
<script><![CDATA[ <script><![CDATA[
Components.utils.import("resource://zotero/config.js"); const { ZOTERO_CONFIG } = ChromeUtils.importESModule('resource://zotero/config.mjs');
document.getElementById('changelog').href = ZOTERO_CONFIG.CHANGELOG_URL; document.getElementById('changelog').href = ZOTERO_CONFIG.CHANGELOG_URL;

View file

@ -1,88 +0,0 @@
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"]
});
// On macOS only, register the Ctrl-Enter handler actor
// (No access to Zotero object here)
if (AppConstants.platform === "macosx") {
ChromeUtils.registerWindowActor("SequoiaContextMenu", {
parent: {
moduleURI: "chrome://zotero/content/actors/SequoiaContextMenuParent.jsm",
},
child: {
moduleURI: "chrome://zotero/content/actors/SequoiaContextMenuChild.jsm",
},
allFrames: true,
includeChrome: true
});
}
ChromeUtils.registerWindowActor("MendeleyAuth", {
parent: {
moduleURI: "chrome://zotero/content/actors/MendeleyAuthParent.jsm"
},
child: {
moduleURI: "chrome://zotero/content/actors/MendeleyAuthChild.jsm"
}
});

View file

@ -0,0 +1,92 @@
// 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"
}
});

View file

@ -0,0 +1,12 @@
import { documentIsReady } from "chrome://zotero/content/actors/actorUtils.mjs";
export class DocumentIsReadyChild extends JSWindowActorChild {
async receiveMessage({ name, data }) {
if (name !== "waitForDocument") {
return null;
}
let { allowInteractiveAfter } = data;
await documentIsReady(this.document, { allowInteractiveAfter });
}
}

View file

@ -1,7 +1,4 @@
var EXPORTED_SYMBOLS = ["ExternalLinkHandlerChild"]; export class ExternalLinkHandlerChild extends JSWindowActorChild {
class ExternalLinkHandlerChild extends JSWindowActorChild {
async handleEvent(event) { async handleEvent(event) {
switch (event.type) { switch (event.type) {
case "click": { case "click": {
@ -41,7 +38,7 @@ class ExternalLinkHandlerChild extends JSWindowActorChild {
} }
let currentURL = this.contentWindow.location; let currentURL = this.contentWindow.location;
// eslint-disable-next-line no-script-url
if (hrefURL.protocol === 'javascript:') { if (hrefURL.protocol === 'javascript:') {
// Link executes a script: open internally // Link executes a script: open internally
return true; return true;

View file

@ -1,10 +1,8 @@
var EXPORTED_SYMBOLS = ["ExternalLinkHandlerParent"]; ChromeUtils.defineESModuleGetters(globalThis, {
ChromeUtils.defineESModuleGetters(this, {
Zotero: "chrome://zotero/content/zotero.mjs" Zotero: "chrome://zotero/content/zotero.mjs"
}); });
class ExternalLinkHandlerParent extends JSWindowActorParent { export class ExternalLinkHandlerParent extends JSWindowActorParent {
async receiveMessage({ name, data }) { async receiveMessage({ name, data }) {
switch (name) { switch (name) {
case "launchURL": { case "launchURL": {

View file

@ -1,8 +1,4 @@
var EXPORTED_SYMBOLS = ["FeedAbstractChild"]; export class FeedAbstractChild extends JSWindowActorChild {
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
class FeedAbstractChild extends JSWindowActorChild {
_stylesheet; _stylesheet;
_stylesheetPromise; _stylesheetPromise;

View file

@ -1,10 +1,8 @@
var EXPORTED_SYMBOLS = ["FeedAbstractParent"]; ChromeUtils.defineESModuleGetters(globalThis, {
ChromeUtils.defineESModuleGetters(this, {
Zotero: "chrome://zotero/content/zotero.mjs" Zotero: "chrome://zotero/content/zotero.mjs"
}); });
class FeedAbstractParent extends JSWindowActorParent { export class FeedAbstractParent extends JSWindowActorParent {
async receiveMessage({ name, data }) { async receiveMessage({ name, data }) {
switch (name) { switch (name) {
case "getStylesheet": { case "getStylesheet": {

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