Access Zotero object in bootstrapped extension

166 views
Skip to first unread message

Emiliano Heyns

unread,
Aug 19, 2020, 4:40:54 PM8/19/20
to zotero-dev

I'm trying to access the Zotero global in a bootstrapped extension (https://gist.github.com/38c2e8dac695c976b3b0b8d1afdb71cc), but when I do it like this, I get

[Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIAppShellService.hiddenDOMWindow]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: chrome://zotero/content/xpcom/zotero.js :: this.init< :: line 234"  data: no]
    this.init<@chrome://zotero/content/xpcom/zotero.js:234:15
From previous event:
    ZoteroService@file:///Applications/Zotero.app/Contents/Resources/components/zotero-service.js:352:7
    startup@resource://gre/modules/addons/XPIProvider.jsm -> file:///Users/emile/.BBTZ5TEST/extensions/schema...@iris-advies.com/bootstrap.js:13:12
    callBootstrapMethod@resource://gre/modules/addons/XPIProvider.jsm:4446:20
    startup@resource://gre/modules/addons/XPIProvider.jsm:2253:13
    callProvider@resource://gre/modules/AddonManager.jsm:253:12
    _startProvider@resource://gre/modules/AddonManager.jsm:728:5
    startup@resource://gre/modules/AddonManager.jsm:892:9
    startup@resource://gre/modules/AddonManager.jsm:2981:5
    observe@jar:file:///Applications/Zotero.app/Contents/Resources/omni.ja!/components/addonManager.js:63:9

Adomas Venčkauskas

unread,
Aug 20, 2020, 3:01:18 AM8/20/20
to zotero-dev
I think you're trying to access the Zotero object too early in application initialization process which causes a force-init but creates a race condition. Try accessing it after the 'zotero-loaded' event

Emiliano Heyns

unread,
Aug 20, 2020, 3:30:51 AM8/20/20
to zotero-dev
How do I register an observer for this?

I also need to run code whenever the item pane shows a new item/when the preferences pane is opened. I used to use the loaded event from the overlay as a kickoff event. How would I do that in a bootstrapped extension?

Adomas Venčkauskas

unread,
Aug 20, 2020, 3:44:48 AM8/20/20
to zotero-dev
On Thursday, 20 August 2020 10:30:51 UTC+3, Emiliano Heyns wrote:
How do I register an observer for this? 


 I also need to run code whenever the item pane shows a new item/when the preferences pane is opened. I used to use the loaded event from the overlay as a kickoff event. How would I do that in a bootstrapped extension?

Attach an event listener to ZoteroPane.itemsView for the 'select' event for item selection.

Monkey patch Utilities.Internal.openPreferences() to get a copy of the handle to the opened window and inject your scripts there.

Emiliano Heyns

unread,
Aug 20, 2020, 4:23:34 AM8/20/20
to zotero-dev
On Thursday, August 20, 2020 at 9:44:48 AM UTC+2 adoma...@gmail.com wrote:
On Thursday, 20 August 2020 10:30:51 UTC+3, Emiliano Heyns wrote:
How do I register an observer for this? 


I don't see "zotero-loaded" anywhere on that page.
 

Emiliano Heyns

unread,
Aug 20, 2020, 4:26:20 AM8/20/20
to zotero-dev
Ah wait I've found an instance in ipc.js

Emiliano Heyns

unread,
Aug 20, 2020, 4:29:37 AM8/20/20
to zotero-dev
No wait -- MDN says an observer is an object with an "observe" method, but ipc.js registers a function, not an object, as the observer. Are both equally valid?

Dan Stillman

unread,
Aug 20, 2020, 4:38:15 AM8/20/20
to zoter...@googlegroups.com
On 8/20/20 4:29 AM, Emiliano Heyns wrote:
> No wait -- MDN says an observer is an object with an "observe" method,
> but ipc.js registers a function, not an object, as the observer. Are
> both equally valid?

Yes, from JavaScript a function is fine.

Emiliano Heyns

unread,
Aug 28, 2020, 7:10:20 PM8/28/20
to zotero-dev
But if an extension is installed when Zotero is already running (rather than Zotero starting with the extension already installed), it will never receive zotero-loaded. Is there a safe way to check that the 'zotero-loaded' phase has passed and I can just grab

Zotero = Components.classes['@zotero.org/Zotero;1'].getService(Components.interfaces.nsISupports).wrappedJSObject;

Emiliano Heyns

unread,
Aug 28, 2020, 8:03:50 PM8/28/20
to zotero-dev
I'm going to try accessing http://127.0.0.1:23119 before anything else, if that's up, I can assume Zotero is running, of not, I'll wait for zotero-loaded.

Emiliano Heyns

unread,
Aug 28, 2020, 8:22:35 PM8/28/20
to zotero-dev
Except neither XMLHttpRequest nor nsIXMLHttpRequest seem to be available to bootstrapped extensions.

Diego de la Hera

unread,
Dec 1, 2020, 3:13:59 PM12/1/20
to zotero-dev
For future reference, I understand you solved this with a running() function here by checking if Zotero's http server is running, or else waiting until 'zotero-loaded' event is triggered, correct? Thanks!

Emiliano Heyns

unread,
Dec 2, 2020, 5:08:03 AM12/2/20
to zotero-dev
That is correct -- the difficulty is that you need to wait until zotero is ready before you may grab Components.classes['@zotero.org/Zotero;1'] -- if you do it too early, Zotero won't start at all, but to reliably know that Zotero is ready, you need to grab that object. Zotero does send a `zotero-loaded` event, but only once directly after it starts, and bootstrapped extensions start as soon as they're installed, which may be well after Zotero has sent the `zotero-loaded` notification. So I test whether the Zotero webserver is accessible, in which case I assume Zotero is running, and if I don't get an answer within a second, I assume Zotero is still starting up and I need to wait for the notification.

XY Wong

unread,
Sep 15, 2022, 7:08:36 AM9/15/22
to zotero-dev
Hi, I just found this thread. These days I tested a lot of ways to load the plugin object to the Zotero object, as we do in the past, in a bootstrapped extension.
Here is the bootstrap.js that works fine for me: https://github.com/windingwind/zotero-addon-template/blob/bootstrap/addon/bootstrap.js

Instead of using Components.Classes.import(which the Zutilo plugin uses) to import a variable, I use Services.scriptloader.loadSubScript to run the plugin code and leave the injection part inside the plugin code.
It first walks through all existing windows and tries to find the window.Zotero to start an injection(in case the Zotero is already running when the plugin is started), then adds a window listener to inject the plugin object if any window is opened in the future(and add a load event listener for them, as we did before).

In this way, we ensure the index.js of the plugin is called only if Zotero is ready so that we can load Zotero via Components.classes["@zotero.org/Zotero;1"] and it can only be loaded once. Please remember to do the clean up of both UI elements and the Zotero.pluginObject when exit!

I hope this would be helpful for the bootstrap version of BBT.

best,
Reply all
Reply to author
Forward
0 new messages