mirror of
https://github.com/zotero/zotero.git
synced 2026-10-06 02:50:03 +00:00
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/
This commit is contained in:
parent
08ed64f17f
commit
fac6c8ebf6
3 changed files with 123 additions and 1 deletions
|
|
@ -50,6 +50,25 @@ const { ZOTERO_CONFIG } = ChromeUtils.importESModule('resource://zotero/config.m
|
|||
|
||||
const COLORED_TAGS_RE = new RegExp("^(?:Numpad|Digit)([0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1})$");
|
||||
|
||||
/**
|
||||
* Message for a failed items list load, naming the plugin responsible if the error came
|
||||
* from plugin code
|
||||
*
|
||||
* @param {Error} e
|
||||
* @return {Promise<String>}
|
||||
*/
|
||||
async function getLoadErrorMessage(e) {
|
||||
let plugin = await Zotero.Plugins.getPluginFromError(e);
|
||||
if (!plugin) {
|
||||
return Zotero.getString('pane.items.loadError');
|
||||
}
|
||||
Zotero.warn(`Items list failed to load in a call from plugin ${plugin.name} (${plugin.id})`);
|
||||
return Zotero.ftl.formatValueSync('items-list-load-error-plugin', {
|
||||
// The message is rendered as HTML
|
||||
plugin: Zotero.Utilities.htmlSpecialChars(plugin.name)
|
||||
});
|
||||
}
|
||||
|
||||
// Minimal CollectionTreeRow-like object for callers that pass plain objects to
|
||||
// changeCollectionTreeRow()/setCollectionTreeRow() (e.g. advanced search).
|
||||
const STUB_COLLECTION_TREE_ROW = {
|
||||
|
|
@ -628,7 +647,7 @@ class CollectionViewItemTreeRowProvider extends ItemTreeRowProvider {
|
|||
}
|
||||
await Zotero.Promise.delay();
|
||||
this.runListeners('update', true, {
|
||||
message: Zotero.getString('pane.items.loadError')
|
||||
message: await getLoadErrorMessage(e)
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,6 +334,107 @@ Zotero.Plugins = new function () {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Identify the plugin, if any, whose code appears in an error's stack
|
||||
*
|
||||
* The topmost plugin frame wins, so an error thrown by Zotero code that a plugin called
|
||||
* is attributed to the plugin that called it.
|
||||
*
|
||||
* @param {Error|String} error - An error or a stack string
|
||||
* @return {Promise<Object|false>} - { id, name }, or false if no plugin frame was found
|
||||
*/
|
||||
this.getPluginFromError = async function (error) {
|
||||
try {
|
||||
var stack = typeof error == 'string' ? error : error?.stack;
|
||||
if (!stack) {
|
||||
return false;
|
||||
}
|
||||
var { addons } = await AddonManager.getActiveAddons(["extension"]);
|
||||
if (!addons?.length) {
|
||||
return false;
|
||||
}
|
||||
var roots = addons.map(addon => [addon, _archiveOrDirURI(addon.getResourceURI().spec)]);
|
||||
for (let line of stack.split('\n')) {
|
||||
let uri = _stackFrameURI(line);
|
||||
if (!uri) {
|
||||
continue;
|
||||
}
|
||||
uri = _resolveChromeOrResourceURI(uri);
|
||||
if (!uri) {
|
||||
continue;
|
||||
}
|
||||
uri = _archiveOrDirURI(uri);
|
||||
for (let [addon, root] of roots) {
|
||||
if (root && uri.startsWith(root)) {
|
||||
return { id: addon.id, name: addon.name };
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Resolve a chrome:// or resource:// URI to the file or JAR URI it points at, since
|
||||
* plugins can register packages and substitutions under both
|
||||
*
|
||||
* @param {String} uri
|
||||
* @return {String} - The resolved URI, or an empty string if it can't be resolved
|
||||
*/
|
||||
function _resolveChromeOrResourceURI(uri) {
|
||||
try {
|
||||
if (uri.startsWith('chrome://')) {
|
||||
return Cc["@mozilla.org/chrome/chrome-registry;1"]
|
||||
.getService(Ci.nsIChromeRegistry)
|
||||
.convertChromeURL(Services.io.newURI(uri))
|
||||
.spec;
|
||||
}
|
||||
if (uri.startsWith('resource://')) {
|
||||
return Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler)
|
||||
.resolveURI(Services.io.newURI(uri));
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return '';
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reduce a URI to something comparable between plugin root URIs and stack frames:
|
||||
* the containing archive for a jar: URI (which points inside an XPI), or the URI itself
|
||||
* for a plugin installed unpacked
|
||||
*
|
||||
* @param {String} uri
|
||||
* @return {String}
|
||||
*/
|
||||
function _archiveOrDirURI(uri) {
|
||||
if (uri.startsWith('jar:')) {
|
||||
let pos = uri.indexOf('!/');
|
||||
return pos == -1 ? uri.substring(4) : uri.substring(4, pos);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {String} line - A line from an error stack, "name@uri:line:column"
|
||||
* @return {String} - The URI, without the line and column
|
||||
*/
|
||||
function _stackFrameURI(line) {
|
||||
// A function name can't contain "@", while a URI can (plugin IDs are email-like)
|
||||
return line.slice(line.indexOf('@') + 1).replace(/:\d+(?::\d+)?$/, '');
|
||||
}
|
||||
|
||||
|
||||
this.getAllPluginIDs = async function () {
|
||||
let addons = await AddonManager.getAddonsByTypes(["extension"]);
|
||||
return addons.map(addon => addon.id);
|
||||
|
|
|
|||
|
|
@ -206,6 +206,8 @@ collections-menu-clear-all-last-read =
|
|||
.label = Clear All Last Read Dates…
|
||||
recently-read-clear-all-confirm = All Last Read dates in this library will be erased.
|
||||
|
||||
items-list-load-error-plugin = Error loading items list. Disabling the “{ $plugin }” plugin and restarting { -app-name } may fix this.
|
||||
|
||||
# Item list section headers for a multiple-row collection-tree selection within one library
|
||||
items-section-collections-selected =
|
||||
{ $count ->
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue