From dc6d55a1376fb572d2efcee48edbd4676ff26aa7 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Wed, 29 Jul 2026 10:56:11 -0400 Subject: [PATCH] Remove collectionTreeRow from the plugin menu context Menu plugins reading it acted on one arbitrary row of the selection. Reading it now throws and names collectionTreeRows, which the context has already provided since multi-collection selection landed. The context now copies property descriptors rather than values, since copying values would evaluate the throwing collectionTreeRow getter for every menu. --- chrome/content/zotero/xpcom/pluginAPI/menuManager.js | 10 +++++++++- chrome/content/zotero/zoteroPane.js | 12 ++++++------ test/tests/pluginAPITest.js | 2 -- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/chrome/content/zotero/xpcom/pluginAPI/menuManager.js b/chrome/content/zotero/xpcom/pluginAPI/menuManager.js index 2cc9a8af9a..93474bd398 100644 --- a/chrome/content/zotero/xpcom/pluginAPI/menuManager.js +++ b/chrome/content/zotero/xpcom/pluginAPI/menuManager.js @@ -632,8 +632,16 @@ _menuElem.style.setProperty("--custom-menu-icon-dark", `url(${darkIcon || icon})`); }, }; + // ZoteroPane's menu contexts define a collectionTreeRow property that + // throws when read, so copy descriptors rather than values, which would + // evaluate it every time a menu is built let wrappedGetContext = () => { - return Object.assign({}, defaultContext, getContext ? getContext() : {}); + let context = {}; + Object.defineProperties(context, Object.getOwnPropertyDescriptors(defaultContext)); + if (getContext) { + Object.defineProperties(context, Object.getOwnPropertyDescriptors(getContext())); + } + return context; }; // Add hooks diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 76ff8ffd69..e3d4fd659d 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -4113,9 +4113,9 @@ var ZoteroPane = new function () { "main/library/collection", { getContext: () => ({ - // collectionTreeRow is the first selected row, kept for - // backward compatibility; collectionTreeRows is the full selection - collectionTreeRow: collectionTreeRows[0], + get collectionTreeRow() { + throw new Error("collectionTreeRow was removed -- use collectionTreeRows"); + }, collectionTreeRows, tabType: "library", tabSubType: undefined, @@ -4659,9 +4659,9 @@ var ZoteroPane = new function () { "main/library/item", { getContext: () => ({ - // collectionTreeRow is the first selected row, kept for - // backward compatibility; collectionTreeRows is the full selection - collectionTreeRow: collectionTreeRows[0], + get collectionTreeRow() { + throw new Error("collectionTreeRow was removed -- use collectionTreeRows"); + }, collectionTreeRows, items, tabType: "library", diff --git a/test/tests/pluginAPITest.js b/test/tests/pluginAPITest.js index 04bf2651f9..70db8ca94b 100644 --- a/test/tests/pluginAPITest.js +++ b/test/tests/pluginAPITest.js @@ -943,7 +943,6 @@ describe("Plugin API", function () { contextKeys: [ ...defaultContextKeys, "items", - "collectionTreeRow", "collectionTreeRows", ] }, @@ -954,7 +953,6 @@ describe("Plugin API", function () { }, contextKeys: [ ...defaultContextKeys, - "collectionTreeRow", "collectionTreeRows", ] },