commit 1e90610dee834dc9aa17c036ab8b578f2e47c0a8
parent 47743cae5d026d1c6cf360fad0e969f0b9c1c479
Author: Lorenz A <me@lorenzackermann.xyz>
Date: Thu, 11 Dec 2025 07:03:48 +0000
Bug 2004234 - [devtools] Turn devtools/shared/loader/Loader.sys.mjs into an ES class. r=devtools-reviewers,bomsy
Differential Revision: https://phabricator.services.mozilla.com/D275639
Diffstat:
1 file changed, 155 insertions(+), 151 deletions(-)
diff --git a/devtools/shared/loader/Loader.sys.mjs b/devtools/shared/loader/Loader.sys.mjs
@@ -23,165 +23,169 @@ var gNextLoaderID = 0;
* |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 useDevToolsLoaderGlobal 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.
+
*/
-export function DevToolsLoader({
- freshCompartment = false,
- useDevToolsLoaderGlobal = false,
-} = {}) {
- if (useDevToolsLoaderGlobal && freshCompartment) {
- throw new Error(
- "Loader cannot use freshCompartment if useDevToolsLoaderGlobal is true"
- );
- }
+export class DevToolsLoader {
+ /**
+ * The two following boolean flags are used to control the sandboxes into
+ * which the modules are loaded.
+ *
+ * @param {object} options
+ * @param {boolean} options.freshCompartment
+ * 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 {boolean} options.useDevToolsLoaderGlobal
+ * 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.
+ */
- const paths = {
- // This resource:// URI is only registered when running DAMP tests.
- // This is done by: testing/talos/talos/tests/devtools/addon/api.js
- "damp-test": "resource://damp-test/content",
- // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
- devtools: "resource://devtools",
- // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
- // Allow access to xpcshell test items from the loader.
- "xpcshell-test": "resource://test",
-
- // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
- // Allow access to locale data using paths closer to what is
- // used in the source tree.
- "devtools/client/locales": "chrome://devtools/locale",
- "devtools/shared/locales": "chrome://devtools-shared/locale",
- "devtools/startup/locales": "chrome://devtools-startup/locale",
- "toolkit/locales": "chrome://global/locale",
- };
-
- // In case the Loader ESM is loaded in DevTools global,
- // also reuse this global for all CommonJS modules.
- const sharedGlobal =
- useDevToolsLoaderGlobal ||
- // eslint-disable-next-line mozilla/reject-globalThis-modification
- Cu.getRealmLocation(globalThis) == "DevTools global"
- ? Cu.getGlobalForObject({})
- : undefined;
- this.loader = new Loader({
- paths,
- sharedGlobal,
- freshCompartment,
- sandboxName: useDevToolsLoaderGlobal
- ? "DevTools (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 ...
- // });
- //
- // Bug 1248830 will work out a better plan here for our content module
- // loading needs, especially as we head towards devtools.html.
- 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: "devtools" });
-
- // Various globals are available from ESM, but not from sandboxes,
- // inject them into the globals list.
- // Changes here should be mirrored to devtools/.eslintrc.
- const injectedGlobals = {
- BrowsingContext,
- CanonicalBrowsingContext,
- ChromeWorker,
- console,
- DebuggerNotificationObserver,
- DOMPoint,
- DOMQuad,
- DOMRect,
- fetch,
- Glean,
- HeapSnapshot,
- IOUtils,
- L10nRegistry,
- Localization,
- NamedNodeMap,
- NodeFilter,
- PathUtils,
- Services,
- StructuredCloneHolder,
- WebExtensionPolicy,
- WebSocket,
- WindowGlobalChild,
- WindowGlobalParent,
- };
- for (const name in injectedGlobals) {
- this.loader.globals[name] = injectedGlobals[name];
- }
+ constructor({
+ freshCompartment = false,
+ useDevToolsLoaderGlobal = false,
+ } = {}) {
+ if (useDevToolsLoaderGlobal && freshCompartment) {
+ throw new Error(
+ "Loader cannot use freshCompartment if useDevToolsLoaderGlobal is true"
+ );
+ }
+
+ const paths = {
+ // This resource:// URI is only registered when running DAMP tests.
+ // This is done by: testing/talos/talos/tests/devtools/addon/api.js
+ "damp-test": "resource://damp-test/content",
+ // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
+ devtools: "resource://devtools",
+ // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
+ // Allow access to xpcshell test items from the loader.
+ "xpcshell-test": "resource://test",
+
+ // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
+ // Allow access to locale data using paths closer to what is
+ // used in the source tree.
+ "devtools/client/locales": "chrome://devtools/locale",
+ "devtools/shared/locales": "chrome://devtools-shared/locale",
+ "devtools/startup/locales": "chrome://devtools-startup/locale",
+ "toolkit/locales": "chrome://global/locale",
+ };
- // Fetch custom pseudo modules and globals
- const { modules, globals } = this.require(
- "resource://devtools/shared/loader/builtin-modules.js"
- );
-
- // 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];
+ // In case the Loader ESM is loaded in DevTools global,
+ // also reuse this global for all CommonJS modules.
+ const sharedGlobal =
+ useDevToolsLoaderGlobal ||
+ // eslint-disable-next-line mozilla/reject-globalThis-modification
+ Cu.getRealmLocation(globalThis) == "DevTools global"
+ ? Cu.getGlobalForObject({})
+ : undefined;
+ this.loader = new Loader({
+ paths,
+ sharedGlobal,
+ freshCompartment,
+ sandboxName: useDevToolsLoaderGlobal
+ ? "DevTools (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 ...
+ // });
+ //
+ // Bug 1248830 will work out a better plan here for our content module
+ // loading needs, especially as we head towards devtools.html.
+ 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: "devtools" });
+
+ // Various globals are available from ESM, but not from sandboxes,
+ // inject them into the globals list.
+ // Changes here should be mirrored to devtools/.eslintrc.
+ const injectedGlobals = {
+ BrowsingContext,
+ CanonicalBrowsingContext,
+ ChromeWorker,
+ console,
+ DebuggerNotificationObserver,
+ DOMPoint,
+ DOMQuad,
+ DOMRect,
+ fetch,
+ Glean,
+ HeapSnapshot,
+ IOUtils,
+ L10nRegistry,
+ Localization,
+ NamedNodeMap,
+ NodeFilter,
+ PathUtils,
+ Services,
+ StructuredCloneHolder,
+ WebExtensionPolicy,
+ WebSocket,
+ WindowGlobalChild,
+ WindowGlobalParent,
};
- }
+ for (const name in injectedGlobals) {
+ this.loader.globals[name] = injectedGlobals[name];
+ }
- // Register custom globals to the current loader instance
- Object.defineProperties(
- this.loader.sharedGlobal,
- Object.getOwnPropertyDescriptors(globals)
- );
-
- // Define the loader id for these two usecases:
- // * access via the ESM (this.id)
- // let { loader } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs");
- // loader.id
- this.id = gNextLoaderID++;
- // * access via module's `loader` global
- // loader.id
- globals.loader.id = this.id;
-
- // Expose lazy helpers on `loader`
- // ie. when you use it like that from a ESM:
- // let { loader } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs");
- // loader.lazyGetter(...);
- this.lazyGetter = globals.loader.lazyGetter;
- this.lazyServiceGetter = globals.loader.lazyServiceGetter;
- this.lazyRequireGetter = globals.loader.lazyRequireGetter;
-}
+ // Fetch custom pseudo modules and globals
+ const { modules, globals } = this.require(
+ "resource://devtools/shared/loader/builtin-modules.js"
+ );
+
+ // 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)
+ );
-DevToolsLoader.prototype = {
+ // Define the loader id for these two usecases:
+ // * access via the ESM (this.id)
+ // let { loader } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs");
+ // loader.id
+ this.id = gNextLoaderID++;
+ // * access via module's `loader` global
+ // loader.id
+ globals.loader.id = this.id;
+
+ // Expose lazy helpers on `loader`
+ // ie. when you use it like that from a ESM:
+ // let { loader } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs");
+ // loader.lazyGetter(...);
+ this.lazyGetter = globals.loader.lazyGetter;
+ this.lazyServiceGetter = globals.loader.lazyServiceGetter;
+ this.lazyRequireGetter = globals.loader.lazyRequireGetter;
+ }
destroy(reason = "shutdown") {
unload(this.loader, reason);
delete this.loader;
- },
+ }
/**
* Return true if |id| refers to something requiring help from a
@@ -189,8 +193,8 @@ DevToolsLoader.prototype = {
*/
isLoaderPluginId(id) {
return id.startsWith("raw!");
- },
-};
+ }
+}
// Export the standard instance of DevToolsLoader used by the tools.
export var loader = new DevToolsLoader();