1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * Manages the base loader (base-loader.js) instance used to load the developer tools.
11 var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
12 var { Loader, Require, resolveURI, unload } = ChromeUtils.import(
13 "resource://devtools/shared/base-loader.js"
15 var { requireRawId } = ChromeUtils.import(
16 "resource://devtools/shared/loader-plugin-raw.jsm"
19 this.EXPORTED_SYMBOLS = [
23 // Export StructuredCloneHolder for its use from builtin-modules
24 "StructuredCloneHolder",
27 var gNextLoaderID = 0;
30 * The main devtools API. The standard instance of this loader is exported as
31 * |loader| below, but if a fresh copy of the loader is needed, then a new
32 * one can also be created.
34 * The two following boolean flags are used to control the sandboxes into
35 * which the modules are loaded.
36 * @param invisibleToDebugger boolean
37 * If true, the modules won't be visible by the Debugger API.
38 * This typically allows to hide server modules from the debugger panel.
39 * @param freshCompartment boolean
40 * If true, the modules will be forced to be loaded in a distinct
41 * compartment. It is typically used to load the modules in a distinct
42 * system compartment, different from the main one, which is shared by
43 * all JSMs, XPCOMs and modules loaded with this flag set to true.
44 * We use this in order to debug modules loaded in this shared system
45 * compartment. The debugger actor has to be running in a distinct
46 * compartment than the context it is debugging.
48 this.DevToolsLoader = function DevToolsLoader({
49 invisibleToDebugger = false,
50 freshCompartment = false,
53 // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
54 devtools: "resource://devtools",
55 // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
56 acorn: "resource://devtools/shared/acorn",
57 // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
58 "acorn/util/walk": "resource://devtools/shared/acorn/walk.js",
59 // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
60 // Allow access to xpcshell test items from the loader.
61 "xpcshell-test": "resource://test",
63 // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
64 // Allow access to locale data using paths closer to what is
65 // used in the source tree.
66 "devtools/client/locales": "chrome://devtools/locale",
67 "devtools/shared/locales": "chrome://devtools-shared/locale",
68 "devtools/startup/locales": "chrome://devtools-startup/locale",
69 "toolkit/locales": "chrome://global/locale",
72 // When creating a Loader invisible to the Debugger, we have to ensure
73 // using only modules and not depend on any JSM. As everything that is
74 // not loaded with Loader isn't going to respect `invisibleToDebugger`.
75 // But we have to keep using Promise.jsm for other loader to prevent
76 // breaking unhandled promise rejection in tests.
77 if (invisibleToDebugger) {
78 paths.promise = "resource://gre/modules/Promise-backend.js";
81 this.loader = new Loader({
85 sandboxName: "DevTools (Module loader)",
86 requireHook: (id, require) => {
87 if (id.startsWith("raw!") || id.startsWith("theme-loader!")) {
88 return requireRawId(id, require);
94 this.require = Require(this.loader, { id: "devtools" });
96 // Fetch custom pseudo modules and globals
97 const { modules, globals } = this.require("devtools/shared/builtin-modules");
99 // When creating a Loader for the browser toolbox, we have to use
100 // Promise-backend.js, as a Loader module. Instead of Promise.jsm which
101 // can't be flagged as invisible to debugger.
102 if (invisibleToDebugger) {
103 delete modules.promise;
106 // Register custom pseudo modules to the current loader instance
107 for (const id in modules) {
108 const uri = resolveURI(id, this.loader.mapping);
109 this.loader.modules[uri] = {
116 // Register custom globals to the current loader instance
117 Object.defineProperties(
119 Object.getOwnPropertyDescriptors(globals)
122 // Define the loader id for these two usecases:
123 // * access via the JSM (this.id)
124 // let { loader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
126 this.id = gNextLoaderID++;
127 // * access via module's `loader` global
129 globals.loader.id = this.id;
131 // Expose lazy helpers on `loader`
132 // ie. when you use it like that from a JSM:
133 // let { loader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
134 // loader.lazyGetter(...);
135 this.lazyGetter = globals.loader.lazyGetter;
136 this.lazyImporter = globals.loader.lazyImporter;
137 this.lazyServiceGetter = globals.loader.lazyServiceGetter;
138 this.lazyRequireGetter = globals.loader.lazyRequireGetter;
140 // When replaying, modify the require hook to allow the ReplayInspector to
141 // replace chrome interfaces with alternatives that understand the proxies
142 // created for objects in the recording/replaying process.
143 if (globals.isReplaying) {
144 const oldHook = this.loader.requireHook;
145 const ReplayInspector = this.require(
146 "devtools/server/actors/replay/inspector"
148 this.loader.requireHook = ReplayInspector.wrapRequireHook(oldHook);
152 DevToolsLoader.prototype = {
153 destroy: function(reason = "shutdown") {
154 unload(this.loader, reason);
159 * Return true if |id| refers to something requiring help from a
162 isLoaderPluginId: function(id) {
163 return id.startsWith("raw!");
167 // Export the standard instance of DevToolsLoader used by the tools.
168 this.loader = new DevToolsLoader({
170 * Sets whether the compartments loaded by this instance should be invisible
171 * to the debugger. Invisibility is needed for loaders that support debugging
172 * of chrome code. This is true of remote target environments, like Fennec or
173 * B2G. It is not the default case for desktop Firefox because we offer the
174 * Browser Toolbox for chrome debugging there, which uses its own, separate
176 * @see devtools/client/framework/ToolboxProcess.jsm
178 invisibleToDebugger: Services.appinfo.name !== "Firefox",
181 this.require = this.loader.require;