Zotero 7 for Developers
Zotero 7, which will be released in 2023, includes a major internal upgrade of the Mozilla platform on which Zotero is based, incorporating changes from Firefox 60 through Firefox 102. This upgrade brings major performance gains, new JavaScript and HTML features, better OS compatibility and platform integration, and native support for Apple Silicon Macs.
While this upgrade required a massive rewrite of the Zotero code base and will require many plugin changes due to technical changes in the Mozilla platform, going forward we expect to keep Zotero current with Firefox Extended Support Release (ESR) versions, with comparatively fewer technical changes between versions.
Feedback
If you have questions about anything on this page or encounter other problems while updating your plugin, let us know on the dev list. Please don't post to the Zotero Forums about Zotero 7 at this time.
Dev Builds
Sample Plugin
We've created a very simple plugin, Make It Red, to demonstrate some of the concepts discussed in this document. It makes things red.
We'll update the plugin as we continue developing the Zotero 7 plugin framework and address issues from the dev list.
Using the Firefox Developer Tools
Since Zotero 7 is based on Firefox 102, it's compatible with the Firefox 102 ESR Developer Tools (rather than the Firefox 60 DevTools as before).
To start a Zotero dev build with the DevTools server, pass the -debugger
flag on the command line:
$ /Applications/Zotero\ Dev.app/Contents/MacOS/zotero -ZoteroDebugText -jsconsole -debugger
In Firefox 102 ESR, you can then go to about:debugging, add localhost:6100
as a network location, and connect to Zotero. (If you haven't yet, you'll first need to enable the “Enable browser chrome and add-on debugging toolboxes” and “Enable remote debugging” options in the settings of Firefox's Web Developer Tools.)
You should use a separate Firefox profile for 102 ESR and disable automatic updates to prevent Firefox from being automatically updated to an incompatible version.
Plugin Changes
All Zotero plugins will need to be updated for Zotero 7.
Zotero 7 plugins continue to provide full access to platform internals (XPCOM, file access, etc.), but the Mozilla platform itself no longer supports similar extensions. All Firefox extensions are now based on the much more limited WebExtensions API shared with Chrome and other browsers, which provides sandboxed APIs for common integration points.
We have no plans to make similar restrictions in Zotero. However, due to the Mozilla platform changes, some integration techniques are no longer available, and all plugins will need to change the way they register themselves in Zotero.
install.rdf → manifest.json
The legacy install.rdf manifest must be replaced with a WebExtension-style manifest.json file. Most WebExtension manifest.json keys are not relevant in Zotero, but you should transfer the main metadata from install.rdf.
{ "manifest_version": 2, "name": "Make It Red", "version": "1.1", "description": "Makes everything red", "author": "Zotero", "icons": { "48": "icon.png", "96": "icon@2x.png" }, "applications": { "zotero": { "id": "make-it-red@zotero.org", "update_url": "https://www.zotero.org/download/plugins/make-it-red/updates.json", "strict_min_version": "6.999", "strict_max_version": "7.0.*" } } }
applications.zotero
is based on browser_specific_settings.gecko
and must be present for Zotero to install your plugin. You should set strict_max_version
to x.x.*
of the latest minor version that you have tested your plugin with. (You can later update compatibility via your update manifest without distributing a new version if no changes are required.)
Use "strict_min_version": "6.999"
to allow your plugin to be installed on Zotero 7 betas.
Transition Process
Plugins can be made to work in both Zotero 6 and Zotero 7 by including both install.rdf and manifest.json files. Zotero 6 will use install.rdf, while Zotero 7 will use manifest.json.
You can load overlay code for Zotero 6 and bootstrap code (as described below) for Zotero 7, or you can create a single bootstrapped version by adding <em:bootstrap>true</em:bootstrap>
to install.rdf for Zotero 6.
update.rdf → updates.json
The legacy RDF update manifest for specifying updates must be replaced with a Mozilla-style JSON update manifest:
{ "addons": { "make-it-red@zotero.org": { "updates": [ { "version": "2.0", "update_link": "https://download.zotero.org/plugins/make-it-red/make-it-red-2.0.xpi", "update_hash": "sha256:4a6dd04c197629a02a9c6beaa9ebd52a69bb683f8400243bcdf95847f0ee254a", "applications": { "zotero": { "strict_min_version": "6.999" } } } ] } } }
Zotero 6 also supports this manifest format with a slight variation: you must specify minimum and maximum versions using applications.gecko
instead of applications.zotero
, and you must use the Firefox platform version instead of the Zotero app version. Since Zotero 6 is based on Firefox 60.9.0 ESR, you can use 60.9
for strict_min_version
and strict_max_version
.
{ "addons": { "make-it-red@zotero.org": { "updates": [ { "version": "1.0", "update_link": "https://download.zotero.org/plugins/make-it-red/make-it-red-1.0.xpi", "update_hash": "sha256:8f383546844b17eb43bd7f95423d7f9a65dfbc0b4eb5cb2e7712fb88a41d02e3", "applications": { "gecko": { "strict_min_version": "60.9", "strict_max_version": "60.9" } } } ] } } }
In this example, version 1.2 is compatible with both Zotero 6 and 7, and version 2.0 is compatible only with Zotero 7:
{ "addons": { "make-it-red@zotero.org": { "updates": [ { "version": "1.2", "update_link": "https://download.zotero.org/plugins/make-it-red/make-it-red-1.2.xpi", "update_hash": "sha256:9b0546d5cf304adcabf39dd13c9399e2702ace8d76882b0b37379ef283c7db13", "applications": { "gecko": { "strict_min_version": "60.9", "strict_max_version": "60.9" }, "zotero": { "strict_min_version": "6.999", "strict_max_version": "7.0.*" } } }, { "version": "2.0", "update_link": "https://download.zotero.org/plugins/make-it-red/make-it-red-2.0.xpi", "update_hash": "sha256:4a6dd04c197629a02a9c6beaa9ebd52a69bb683f8400243bcdf95847f0ee254a", "applications": { "zotero": { "strict_min_version": "6.999", "strict_max_version": "7.0.*" } } } ] } } }
Transition Process
Since Zotero 6 already supports the new JSON update manifest, we recommend creating a JSON manifest now and pointing new versions of your plugin at its URL in install.rdf, even before you've updated your plugin for Zotero 7. As described above, you can serve a manifest that offers a version that supports only Zotero 6 now and later add a version that supports both Zotero 6 and 7 and/or a version that supports only Zotero 7, all from the same file.
However, since you can't be sure that all of your users will upgrade to your new version that points to a JSON URL before they upgrade to Zotero 7, and since Zotero 7 will no longer be able to parse RDF update manifests, there's a risk of users getting stranded on an old version. To avoid this, you can simply make the new JSON manifest available from the old RDF URL as well. Even with the .rdf extension, Zotero will detect that it's a JSON manifest and process it properly.
XUL Overlays → bootstrap.js
This will likely be the biggest change for most plugin developers.
Zotero 6 and earlier supported two types of plugins:
- Overlay plugins, which use XUL overlays to inject elements — including
<script>
elements — into the DOM of existing windows - Bootstrapped plugins, which programmatically insert themselves into the app and modify the DOM as necessary, and which can be enabled and disabled without restarting Zotero
These correspond to the two types of legacy Firefox extensions up through Firefox 56.
The Mozilla platform no longer supports either type of extension — instead supporting WebExtensions (or MailExtensions in Thunderbird) — and no longer supports XUL overlays. We don't feel that restrictive WebExtension-style APIs are a good fit for the Zotero plugin ecosystem, so we've reimplemented support for bootstrapped extensions for Zotero plugins to use going forward.
Most existing Zotero plugins use overlays and will need to be rewritten to work as bootstrapped plugins.
Bootstrapped Zotero plugins in Zotero 7 require two components:
- A WebExtension-style manifest.json file, as described above
- A bootstrapped-extension-style bootstrap.js, with functions
startup()
,shutdown()
,install()
, anduninstall()
The bootstrap.js functions are passed two parameters:
- An object with these properties:
id
, the plugin idversion
, the plugin versionrootURI
, a string URL pointing to the plugin's files. For XPIs, this will be ajar:file:///
URL. This value will always end in a slash, so you can append a relative path to get a URL for a file bundled with your plugin (e.g.,rootURI + 'style.css'
).
- A number representing the reason for the event, which can be checked against the following constants:
APP_STARTUP
,APP_SHUTDOWN
,ADDON_ENABLE
,ADDON_DISABLE
,ADDON_INSTALL
,ADDON_UNINSTALL
,ADDON_UPGRADE
(not yet implemented),ADDON_DOWNGRADE
(not yet implemented)
Note that Zotero 6 provides a resourceURI
nsIURI object instead of a rootURI
string, so you'll want to assign resourceURI.spec
to rootURI
if rootURI
isn't provided.
Bootstrapped plugins can be disabled or uninstalled without restarting Zotero, so you'll need to make sure you remove all elements and functionality in the shutdown()
function.
In Zotero 7, the install()
and startup()
bootstrap methods are called only after Zotero has initialized, and the Zotero
object is automatically made available in the bootstrap scope, along with Services
, Cc
, Ci
, and other Mozilla and browser objects. This isn't the case in Zotero 6, in which these functions can load before the Zotero
object is available and won't automatically get window
properties such as URL
. (Zotero
also isn't available in uninstall()
in Zotero 6.) The sample plugin provides an example of waiting for availability of the ''Zotero'' object and importing global properties in a bootstrapped plugin that works in both Zotero 6 and 7.
Some plugins may require additional hooks in Zotero itself to work well as bootstrapped plugins. If you're having trouble accomplishing something you were doing previously via XUL overlays, let us know on zotero-dev.
chrome.manifest → runtime chrome registration
The Mozilla platform no longer supports chrome.manifest files for registration of resources.
In many cases, you may no longer need to register chrome://
URLs, as resources can be loaded by using a relative path directly or by appending a relative path to the rootURI
string passed to your plugin's startup()
function. However, some functions, such as ChromeUtils.import()
for JSMs (or, post–Firefox 102, ESMs), only accept chrome://
content URLs. .prop
and .dtd
locale files also still need to be registered as chrome files.
You can register content
and locale
resources in your plugin's startup
function:
var chromeHandle; function startup({ id, version, rootURI }, reason) { var aomStartup = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup); var manifestURI = Services.io.newURI(rootURI + "manifest.json"); chromeHandle = aomStartup.registerChrome(manifestURI, [ ["content", "make-it-red", "chrome/content/"], ["locale", "make-it-red", "en-US", "chrome/locale/en-US/"], ["locale", "make-it-red", "fr", "chrome/locale/fr/"] ]);
Deregister the files in shutdown()
in case your plugin is disabled, remove, or upgraded:
chromeHandle.destruct(); chromeHandle = null;
Default Preferences
In Zotero 6, default preferences could be set by creating .js files in your plugin's defaults/preferences/
folder:
pref("extensions.make-it-red.intensity", 100);
These default preferences are loaded automatically at startup. While this works fine for overlay plugins, which require a restart, bootstrapped plugins can be installed or enabled at any time, and their default preferences are not read until Zotero is restarted.
In Zotero 7, default preferences should be placed in a prefs.js
file in the plugin root, following the same format as above. These preferences will be read when plugins are installed or enabled and then on every startup. While files in defaults/preferences
still work for now in Zotero 7, they won't be read when plugins are installed or enabled, so we don't recommend relying on that location.
Transition Process
If your plugin will work in overlay mode in Zotero 6 and bootstrap mode in Zotero 7, create defaults/preferences/prefs.js
and add a step to your build process to copy it to prefs.js
in the plugin root. Zotero 7 will load both at startup, but as long as the contents are the same, there won't be any difference in the created preferences.
If your plugin is a bootstrapped plugin for both Zotero 6 and 7, create only prefs.js
in your plugin root. You can then force Zotero 6 to read preferences from that file every time it starts up. See setDefaultPrefs() and the conditional call in startup() from Make It Red 1.2 for an example.
Preference Panes
Zotero now includes a built-in function to register a preference pane. In your plugin's startup
function:
Zotero.PreferencePanes.register({ pluginID: 'make-it-red@zotero.org', src: rootURI + 'prefs.xhtml', scripts: [rootURI + 'prefs.js'] });
More advanced options are supported.
The pane's src
should point to a file containing an XHTML fragment. Fragments cannot have a <!DOCTYPE
. The default namespace is XUL, and HTML tags are accessible under html:
. A simple pane could look like:
<vbox onload="MakeItRed_Preferences.init()"> <groupbox> <label><html:h2>Colors</html:h2></label> <!-- [...] --> </groupbox> </vbox>
Organizing your pane as a sequence of top-level <groupbox>
es inside a <vbox>
will optimize it for the new preferences search mechanism. By default, all text in the DOM is searchable. If you want to manually add keywords to an element (for example, a button that opens a dialog), set its data-search-strings-raw
property to a comma-separated list.
<preference> Tags and Preference Binding
Zotero 6 preference panes use <preference>
tags to assign pane-local IDs to preference keys and bind form fields to them. For example, a Zotero 6 pane might have included:
<preferences> <preference id="pref-makeItRed-color" name="extensions.zotero.makeItRed.color" type="string"/> </preferences> <!-- ... --> <textbox preference="pref-makeItRed-color"/>
That markup would create a textbox whose value is bound to extensions.zotero.makeItRed.color
.
Zotero 7 supports binding fields to preferences in almost the same way, without the <preference>
tag. Instead, the field is bound directly to the preference key. For example:
<textbox preference="extensions.zotero.makeItRed.color"/>
Zotero 6 also supported getting and setting preferences through <preferences>
tags. For example, in the Zotero 6 example above, JavaScript code could get the value of extensions.zotero.makeItRed.color
by calling document.getElementById('pref-makeItRed-color').value
. This is no longer supported — call Zotero.Prefs.get()
directly.
Other Platform Changes
Mozilla made many other changes between Firefox 60 and Firefox 102 that affect both Zotero code and plugin code, and Zotero 7 includes other API changes as well.
Mozilla Platform
The following list includes nearly all Mozilla changes that affected Zotero code. You may encounter other breaking changes if you use APIs not used in Zotero. Searchfox is the best resource for identifying current correct usage in Mozilla code and changes between Firefox 60 and Firefox 102.
- All
.xul
files must be renamed to.xhtml
- Some XUL elements were removed; others were converted to Custom Elements that can be used more or less the same way
- Top-level
<dialog>…</dialog>
→<window><dialog>…</dialog></window>
and dialog button/event changes (example) - XBL support removed — convert to Custom Elements (example)
openPopup(anchor, position, clientX, clientY, isContextMenu)
→openPopupAtScreen(screenX, screenY, isContextMenu)
(example).boxObject
removed (example)nsIWebNavigation::loadURI()
signature change (example)Components.interfaces.nsIDOMXULElement
→XULElement
Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURL)
→Services.io.newURI(url).QueryInterface(Ci.nsIURL)
(example)- Use
native="true"
for native form elements (example) nsIWebNavigation.loadURI()
signature change (example)nsILoginManager::findLogins()
signature change (example)nsIURI.clone()
was removedAddonManager.getAllAddons()
now returns a promise (example)- Custom protocol handler changes (example)
Services.console.getMessageArray()
returns an actual array (example)OS.Path.join()
silently drops Number arguments (example)
Note that Mozilla has largely switch to Fluent for localization, but Zotero 7 is currently still using .properties/.dtd files. Plugin authors may wish to investigate Fluent for localization of their plugins.
Zotero Platform
tooltiptext
→title
— works automatically in existing windows; in new windows, requires a XUL <tooltip id="html-tooltip"> element and atooltip="html-tooltip"
attribute on the<window>
(example)Zotero.Browser
→HiddenBrowser.jsm
— post on dev list for further discussion if you're currently relying on a hidden browser (details)