Complain when Notifier/Prefs observer leaks window

This commit is contained in:
Abe Jellinek 2025-07-23 11:54:30 -04:00 • committed by Dan Stillman
parent 1eb248553a
commit 8041d7d4df
3 changed files with 29 additions and 4 deletions

View file

@ -153,11 +153,17 @@ Zotero.Notifier = new function () {
continue;
}
let ref = _observers[id].ref;
if (Zotero.Debug.enabled && Zotero.Utilities.Internal.isObjectLeakingWindow(ref)) {
Zotero.warn(`Notifier observer with id '${id}' belongs to leaked window`);
}
// Catch exceptions so all observers get notified even if
// one throws an error
try {
let t = new Date;
yield Zotero.Promise.resolve(_observers[id].ref.notify(event, type, ids, extraData));
yield Zotero.Promise.resolve(ref.notify(event, type, ids, extraData));
t = new Date - t;
if (t > 5) {
//Zotero.debug(id + " observer finished in " + t + " ms", 5);

View file

@ -402,10 +402,14 @@ Zotero.Prefs = new function() {
return;
}
var obs = _observers[data];
for (var i=0; i<obs.length; i++) {
var observersForPref = _observers[data];
for (let observer of observersForPref) {
if (Zotero.Debug.enabled && Zotero.Utilities.Internal.isObjectLeakingWindow(observer)) {
Zotero.warn(`Pref observer for '${data}' belongs to leaked window`);
}
try {
obs[i](this.get(data, true));
observer(this.get(data, true));
}
catch (e) {
Zotero.debug("Error while executing preference observer handler for " + data);

View file

@ -2561,6 +2561,21 @@ Zotero.Utilities.Internal = {
}
return textContent;
},
/**
* Check whether an object belongs to a closed window, and is therefore
* keeping it alive.
*
* @param {any} obj
* @returns {boolean}
*/
isObjectLeakingWindow(obj) {
if (typeof obj !== 'object' || obj === null) {
return false;
}
let global = Cu.getGlobalForObject(obj);
return global.constructor.name === 'Window' && global.closed;
}
};