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

Developer builds of Zotero 7 are currently available for macOS and Linux. We hope to make a Windows build available soon.

These are feature-incomplete test builds intended solely for use by Zotero plugin developers and should not be used in production. While we're not aware of any problems switching between Zotero 6 and 7, we nevertheless recommend using a separate profile and data directory for development.

Known Issues

  • Dragging an XPI to the Add-ons window does not currently work. To install an XPI, click the gear icon and select “Install Add-on from File…”, or see Setting Up a Plugin Development Environment to install from source.
  • Dev builds may not include new strings for all locales, so you may need to use en-US to avoid interface errors.
  • Many, many other things!

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 current Firefox 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, 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.)

Plugin Changes

All Zotero plugins will need to be updated for Zotero 7.

Zotero 7 plugins continue to be based on the legacy XUL/XPCOM Mozilla extension framework that provides full access to platform internals, but the Mozilla platform itself no longer supports this framework. 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.

Currently, a bootstrapped plugin installed in Zotero 6 will be disabled when upgrading to Zotero 7. We hope to fix this before Zotero 7 is released, but for now you can create an update manifest entry specifying a version that only works in Zotero 7, which Zotero 7 will automatically install when it first checks for updates, causing the plugin to be re-enabled.

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:

  1. Overlay plugins, which use XUL overlays to inject elements — including <script> elements — into the DOM of existing windows
  2. 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:

  1. A WebExtension-style manifest.json file, as described above
  2. A bootstrapped-extension-style bootstrap.js, with functions startup(), shutdown(), install(), and uninstall()

Unlike in Zotero 6, install() and startup() 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). The sample plugin provides an example of testing for availability of the Zotero object in a plugin that works in both Zotero 6 and 7.

The bootstrap.js functions are passed two parameters:

  • An object with these properties:
    • id, the plugin id
    • version, the plugin version
    • rootURI, a string URL pointing to the plugin' files. For XPIs this will be a jar:file:/// URL. This value will 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 reason code for the event, which can be tested against Zotero.Plugins.REASONS (e.g., Zotero.Plugins.REASONS.ADDON_ENABLE).

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.

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;

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 HTML and XUL tags are accessible under xul:. A simple pane could look like:

<xul:vbox onload="MakeItRed_Preferences.init()">
	<xul:groupbox>
		<label><h2>Colors</h2></label>
		<!-- [...] -->
	</xul:groupbox>
</xul: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.

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
  • createElement()/createElementNS()createXULElement() for remaining XUL elements (example)
  • Top-level <dialog>…</dialog><window><dialog>…</dialog></window> and dialog button/event changes (example)
  • XBL support removed — convert to Custom Elements (example)
  • nsIDOMParsernew DOMParser() (example)
  • XUL document.getElementsByAttribute()document.querySelectorAll() (example)
  • openPopup(anchor, position, clientX, clientY, isContextMenu)openPopupAtScreen(screenX, screenY, isContextMenu) (example)
  • .boxObject removed (example)
  • <browser><browser maychangeremoteness="true"> when loading HTML content (example)
  • nsIIdleServicensIUserIdleService (example)
  • XPCOMUtils.generateQI()ChromeUtils.generateQI() (example)
  • nsIWebNavigation::loadURI() signature change (example)
  • <textbox type="search"><search-textbox> (example)
  • XUL textbox → HTML <input type="text"> (example)
  • XUL <progressmeter> → HTML <progress> (example)
  • <label><label><html:h2> within <groupbox> (example)
  • XULDocumentXMLDocument (example)
  • Components.interfaces.nsIDOMXULElementXULElement
  • XUL tree: tree.treeBoxObject removal and getCellAt() signature change (example)
  • Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURL)
    Services.io.newURI(url).QueryInterface(Ci.nsIURL) (example)
  • getURLSpecFromFile()Zotero.File.pathToFileURI() (example)
  • initKeyEvent()new KeyboardEvent() (example)
  • context removed from onStartRequest()/onDataAvailable()/onStopRequest()/asyncRead() (example)
  • Use native="true" for native form elements (example)
  • nsIWebNavigation.loadURI() signature change (example)
  • XPCOMUtilsComponentUtils for generateNSGetFactory (example)
  • nsILoginManager::findLogins() signature change (example)
  • nsIURI.clone() was removed
  • AddonManager.getAllAddons() now returns a promise (example)
  • Custom protocol handler changes (example)
  • goQuitApplication() now takes an event argument (example)
  • Services.console.getMessageArray() returns an actual array (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

  • DB.executeTransaction() no longer takes generator functions — use async/await (example)
  • tooltiptexttitle — works automatically in existing windows; in new windows, requires a XUL <tooltip id="html-tooltip"> element and a tooltip="html-tooltip" attribute on the <window> (example)
  • Zotero.BrowserHiddenBrowser.jsm — post on dev list for further discussion if you're currently relying on a hidden browser (details)