'use strict'; /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * Manages the base loader (loader.sys.mjs) instance used to load CJS modules. */ const { Loader, Require, resolveURI, unload, } = ChromeUtils.importESModule("resource://zotero/loader.sys.mjs"); const DEFAULT_SANDBOX_NAME = "Zotero (Module loader)"; var gNextLoaderID = 0; /** * The main loader API. The standard instance of this loader is exported as * |loader| below, but if a fresh copy of the loader is needed, then a new * one can also be created. * * The two following boolean flags are used to control the sandboxes into * which the modules are loaded. * @param freshCompartment boolean * If true, the modules will be forced to be loaded in a distinct * compartment. It is typically used to load the modules in a distinct * system compartment, different from the main one, which is shared by * all ESMs, XPCOMs and modules loaded with this flag set to true. * We use this in order to debug modules loaded in this shared system * compartment. The debugger actor has to be running in a distinct * compartment than the context it is debugging. * @param useLoaderGlobal boolean * If true, the loader will reuse the current global to load other * modules instead of creating a sandbox with custom options. Cannot be * used with freshCompartment. */ function ZoteroLoader({ freshCompartment = false, useLoaderGlobal = false, } = {}) { if (useLoaderGlobal && freshCompartment) { throw new Error( "Loader cannot use freshCompartment if useLoaderGlobal is true" ); } const paths = { '': 'resource://zotero/', 'containers/': 'chrome://zotero/content/containers/', 'components/': 'chrome://zotero/content/components/', 'zotero/': 'chrome://zotero/content/' }; // In case the Loader ESM is loaded in the existing global, // also reuse this global for all CommonJS modules. const sharedGlobal = useLoaderGlobal || // eslint-disable-next-line mozilla/reject-globalThis-modification Cu.getRealmLocation(globalThis) == "Zotero global" ? Cu.getGlobalForObject({}) : undefined; this.loader = new Loader({ paths, sharedGlobal, freshCompartment, sandboxName: useLoaderGlobal ? "Zotero (Server Module Loader)" : DEFAULT_SANDBOX_NAME, // Make sure `define` function exists. JSON Viewer needs modules in AMD // format, as it currently uses RequireJS from a content document and // can't access our usual loaders. So, any modules shared with the JSON // Viewer should include a define wrapper: // // // Make this available to both AMD and CJS environments // define(function(require, exports, module) { // ... code ... // }); // supportAMDModules: true, requireHook: (id, require) => { // if (id.startsWith("raw!") || id.startsWith("theme-loader!")) { // return requireRawId(id, require); // } return require(id); }, }); this.require = Require(this.loader, { id: "zotero" }); let win = typeof window !== "undefined" ? window : {}; let doc = win.document ?? {}; let navigator = typeof win.navigator !== 'undefined' ? win.navigator : {}; // Various globals are available from ESM, but not from sandboxes, // inject them into the globals list. // Changes here should be mirrored to .eslintrc. const injectedGlobals = { BrowsingContext, CanonicalBrowsingContext, ChromeWorker, console, DebuggerNotificationObserver, DOMPoint, DOMQuad, DOMRect, fetch, HeapSnapshot, IOUtils, L10nRegistry, Localization, NamedNodeMap, NodeFilter, PathUtils, Services, StructuredCloneHolder, WebExtensionPolicy, WebSocket, WindowGlobalChild, WindowGlobalParent, window: win, document: doc, navigator, setTimeout: win.setTimeout, clearTimeout: win.clearTimeout, setInterval: win.setInterval, clearInterval: win.clearInterval, requestAnimationFrame: win.requestAnimationFrame, cancelAnimationFrame: win.requestAnimationFrame, }; for (const name in injectedGlobals) { this.loader.globals[name] = injectedGlobals[name]; } Object.defineProperty(this.loader.globals, "Zotero", { get: () => { const { Zotero } = ChromeUtils.importESModule("chrome://zotero/content/zotero.mjs"); // TODO: Cache return Zotero; } }) // Fetch custom pseudo modules and globals const { modules, globals } = { // TODO: TEMP: Stub this out modules: {}, globals: {}, } // Register custom pseudo modules to the current loader instance for (const id in modules) { const uri = resolveURI(id, this.loader.mapping); this.loader.modules[uri] = { get exports() { return modules[id]; }, }; } // Register custom globals to the current loader instance Object.defineProperties( this.loader.sharedGlobal, Object.getOwnPropertyDescriptors(globals) ); this.id = gNextLoaderID++; } ZoteroLoader.prototype = { destroy(reason = "shutdown") { unload(this.loader, reason); delete this.loader; }, }; // Export the standard instance of ZoteroLoader used by the tools. // TODO: Zotero: Not making require.js an ESM for now, so this isn't exposed // Should it be? let loader = new ZoteroLoader(); var require = loader.require;