From ba2d6ecee436ec4d9f244f77d461497e4774861c Mon Sep 17 00:00:00 2001 From: Thenewmanator15 <3686761+Thenewmanator15@users.noreply.github.com> Date: Mon, 5 Oct 2026 15:51:07 +0100 Subject: [PATCH] Fix plugin loading on Firefox 153.3 (untrusted URI) (#6058) As of Firefox 153.3.0esr, Services.scriptloader refuses jar:file: and file: URIs unless allowUnsafeURL is passed (Mozilla bug 1974213), so no plugin loads: bootstrap.js fails with "Trying to load untrusted URI", then "Plugin ... is missing bootstrap method 'startup'". Pass allowUnsafeURL when loading a plugin's bootstrap.js and prefs.js, and preference pane scripts, and temporarily enable security.allow_unsafe_subscript_loads, which covers loads from plugin code outside the bootstrap scope. Also add a test that installs a fixture plugin that loads a script from its XPI and sets a default pref. --------- Co-authored-by: Dan Stillman --- app/assets/prefs.js | 5 ++++ .../content/zotero/preferences/preferences.js | 6 +++- chrome/content/zotero/xpcom/plugins.js | 11 +++++-- .../data/plugin-loading-test/bootstrap.js | 14 +++++++++ test/tests/data/plugin-loading-test/main.js | 3 ++ .../data/plugin-loading-test/manifest.json | 13 +++++++++ test/tests/data/plugin-loading-test/prefs.js | 1 + test/tests/pluginsTest.js | 29 +++++++++++++++++++ 8 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 test/tests/data/plugin-loading-test/bootstrap.js create mode 100644 test/tests/data/plugin-loading-test/main.js create mode 100644 test/tests/data/plugin-loading-test/manifest.json create mode 100644 test/tests/data/plugin-loading-test/prefs.js create mode 100644 test/tests/pluginsTest.js diff --git a/app/assets/prefs.js b/app/assets/prefs.js index 72fc9c0cf2..294f9c1414 100644 --- a/app/assets/prefs.js +++ b/app/assets/prefs.js @@ -54,6 +54,11 @@ pref("dom.disable_open_during_load", true); // scraping the page, since we don't provide any information to the site. pref("security.warn_viewing_mixed", false); +// Temporarily allow plugins to load their own scripts from jar:file: URIs with loadSubScript() +// without passing allowUnsafeURL (Mozilla bug 1974213). This will be disabled once plugins have +// had time to update. +pref("security.allow_unsafe_subscript_loads", true); + // We do need synchronous XHR pref("network.xhr.block_sync_system_requests", false); diff --git a/chrome/content/zotero/preferences/preferences.js b/chrome/content/zotero/preferences/preferences.js index 985510fd5a..1b35f91c33 100644 --- a/chrome/content/zotero/preferences/preferences.js +++ b/chrome/content/zotero/preferences/preferences.js @@ -317,7 +317,11 @@ var Zotero_Preferences = { sameZoneAs: window, }); for (let script of pane.scripts) { - Services.scriptloader.loadSubScript(script, pane.scope); + // Plugin panes load scripts from jar:file: URIs + Services.scriptloader.loadSubScriptWithOptions(script, { + target: pane.scope, + allowUnsafeURL: true + }); } } if (pane.stylesheets) { diff --git a/chrome/content/zotero/xpcom/plugins.js b/chrome/content/zotero/xpcom/plugins.js index a2ff9d1228..f4c065d159 100644 --- a/chrome/content/zotero/xpcom/plugins.js +++ b/chrome/content/zotero/xpcom/plugins.js @@ -206,7 +206,10 @@ Zotero.Plugins = new function () { uri, { target: scope, - ignoreCache: true + ignoreCache: true, + // Plugins are loaded from jar:file: URIs, which the script loader + // otherwise refuses (Mozilla bug 1974213) + allowUnsafeURL: true } ); } @@ -528,7 +531,8 @@ Zotero.Plugins = new function () { addon.getResourceURI("prefs.js").spec, { target: obj, - ignoreCache: true + ignoreCache: true, + allowUnsafeURL: true } ); } @@ -554,7 +558,8 @@ Zotero.Plugins = new function () { addon.getResourceURI("prefs.js").spec, { target: obj, - ignoreCache: true + ignoreCache: true, + allowUnsafeURL: true } ); } diff --git a/test/tests/data/plugin-loading-test/bootstrap.js b/test/tests/data/plugin-loading-test/bootstrap.js new file mode 100644 index 0000000000..7b0bc527b4 --- /dev/null +++ b/test/tests/data/plugin-loading-test/bootstrap.js @@ -0,0 +1,14 @@ +/* global Zotero, Services */ +function startup({ rootURI }) { + // Load a script from this XPI into the plugin scope + Services.scriptloader.loadSubScript(rootURI + 'main.js'); + Zotero.PluginLoadingTest = pluginLoadingTestMain(); +} + +function shutdown() { + delete Zotero.PluginLoadingTest; +} + +function install() {} + +function uninstall() {} diff --git a/test/tests/data/plugin-loading-test/main.js b/test/tests/data/plugin-loading-test/main.js new file mode 100644 index 0000000000..df4649d7c7 --- /dev/null +++ b/test/tests/data/plugin-loading-test/main.js @@ -0,0 +1,3 @@ +function pluginLoadingTestMain() { + return 'loaded'; +} diff --git a/test/tests/data/plugin-loading-test/manifest.json b/test/tests/data/plugin-loading-test/manifest.json new file mode 100644 index 0000000000..24699c7bd1 --- /dev/null +++ b/test/tests/data/plugin-loading-test/manifest.json @@ -0,0 +1,13 @@ +{ + "manifest_version": 2, + "name": "Plugin Loading Test", + "version": "1.0", + "applications": { + "zotero": { + "id": "plugin-loading-test@zotero.org", + "update_url": "https://www.zotero.org/", + "strict_min_version": "7.0", + "strict_max_version": "*" + } + } +} diff --git a/test/tests/data/plugin-loading-test/prefs.js b/test/tests/data/plugin-loading-test/prefs.js new file mode 100644 index 0000000000..7d54f7265f --- /dev/null +++ b/test/tests/data/plugin-loading-test/prefs.js @@ -0,0 +1 @@ +pref("extensions.zotero.pluginLoadingTest.pref", "default"); diff --git a/test/tests/pluginsTest.js b/test/tests/pluginsTest.js new file mode 100644 index 0000000000..f6c58da017 --- /dev/null +++ b/test/tests/pluginsTest.js @@ -0,0 +1,29 @@ +describe("Zotero.Plugins", function () { + var { AddonManager } = ChromeUtils.importESModule("resource://gre/modules/AddonManager.sys.mjs"); + + describe("Loading", function () { + var addon; + + afterEach(async function () { + if (addon) { + await addon.uninstall(); + addon = null; + } + Zotero.Prefs.clear('pluginLoadingTest.pref'); + }); + + // The fixture's bootstrap.js loads main.js from its own XPI with + // Services.scriptloader.loadSubScript(rootURI + ...), as plugins commonly do + it("should load scripts and default prefs from a plugin's XPI", async function () { + let dir = getTestDataDirectory(); + dir.append('plugin-loading-test'); + let xpiPath = PathUtils.join(await getTempDirectory(), 'plugin-loading-test.xpi'); + await Zotero.File.zipDirectory(dir.path, xpiPath); + addon = await AddonManager.installTemporaryAddon(Zotero.File.pathToFile(xpiPath)); + + await waitForCallback(() => Zotero.PluginLoadingTest, 100, 10); + assert.equal(Zotero.PluginLoadingTest, 'loaded'); + assert.equal(Zotero.Prefs.get('pluginLoadingTest.pref'), 'default'); + }); + }); +});