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 * This module defines custom globals injected in all our modules and also
9 * pseudo modules that aren't separate files but just dynamically set values.
11 * As it does so, the module itself doesn't have access to these globals,
12 * nor the pseudo modules. Be careful to avoid loading any other js module as
13 * they would also miss them.
16 const { Cu, Cc, Ci } = require("chrome");
17 const promise = require("resource://gre/modules/Promise.jsm").Promise;
18 const jsmScope = require("resource://devtools/shared/Loader.jsm");
19 const { Services } = require("resource://gre/modules/Services.jsm");
21 const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
23 // Steal various globals only available in JSM scope (and not Sandbox one)
26 DebuggerNotificationObserver,
33 StructuredCloneHolder,
35 } = Cu.getGlobalForObject(jsmScope);
37 // Create a single Sandbox to access global properties needed in this module.
38 // Sandbox are memory expensive, so we should create as little as possible.
39 const debuggerSandbox = Cu.Sandbox(systemPrincipal, {
40 // This sandbox is also reused for ChromeDebugger implementation.
41 // As we want to load the `Debugger` API for debugging chrome contexts,
42 // we have to ensure loading it in a distinct compartment from its debuggee.
43 // invisibleToDebugger does that and helps the Debugger API identify the boundaries
44 // between debuggee and debugger code.
45 invisibleToDebugger: true,
47 wantGlobalProperties: [
91 * Defines a getter on a specified object that will be created upon first use.
94 * The object to define the lazy getter on.
96 * The name of the getter to define on object.
98 * A function that returns what the getter should return. This will
99 * only ever be called once.
101 function defineLazyGetter(object, name, lambda) {
102 Object.defineProperty(object, name, {
104 // Redefine this accessor property as a data property.
105 // Delete it first, to rule out "too much recursion" in case object is
106 // a proxy whose defineProperty handler might unwittingly trigger this
109 const value = lambda.apply(object);
110 Object.defineProperty(object, name, {
124 * Defines a getter on a specified object for a service. The service will not
125 * be obtained until first use.
128 * The object to define the lazy getter on.
130 * The name of the getter to define on object for the service.
132 * The contract used to obtain the service.
133 * @param interfaceName
134 * The name of the interface to query the service to.
136 function defineLazyServiceGetter(object, name, contract, interfaceName) {
137 defineLazyGetter(object, name, function() {
138 return Cc[contract].getService(Ci[interfaceName]);
143 * Defines a getter on a specified object for a module. The module will not
144 * be imported until first use. The getter allows to execute setup and
145 * teardown code (e.g. to register/unregister to services) and accepts
146 * a proxy object which acts on behalf of the module until it is imported.
149 * The object to define the lazy getter on.
151 * The name of the getter to define on object for the module.
153 * The URL used to obtain the module.
155 * The name of the symbol exported by the module.
156 * This parameter is optional and defaults to name.
158 * A function that is executed when the proxy is set up.
159 * This will only ever be called once.
161 * A function that is executed when the module has been imported to
162 * run optional teardown procedures on the proxy object.
163 * This will only ever be called once.
165 * An object which acts on behalf of the module to be imported until
166 * the module has been imported.
168 function defineLazyModuleGetter(
179 if (typeof preLambda === "function") {
180 preLambda.apply(proxy);
183 defineLazyGetter(object, name, function() {
186 temp = ChromeUtils.import(resource);
188 if (typeof postLambda === "function") {
189 postLambda.apply(proxy);
192 Cu.reportError("Failed to load module " + resource + ".");
195 return temp[symbol || name];
200 * Define a getter property on the given object that requires the given
201 * module. This enables delaying importing modules until the module is
205 * The object to define the property on.
206 * @param String property
208 * @param String module
210 * @param Boolean destructure
211 * Pass true if the property name is a member of the module's exports.
213 function lazyRequireGetter(obj, property, module, destructure) {
214 Object.defineProperty(obj, property, {
216 // Redefine this accessor property as a data property.
217 // Delete it first, to rule out "too much recursion" in case obj is
218 // a proxy whose defineProperty handler might unwittingly trigger this
220 delete obj[property];
221 const value = destructure
222 ? require(module)[property]
223 : require(module || property);
224 Object.defineProperty(obj, property, {
237 // List of pseudo modules exposed to all devtools modules.
240 DebuggerNotificationObserver,
243 // Expose "chrome" Promise, which aren't related to any document
244 // and so are never frozen, even if the browser loader module which
245 // pull it is destroyed. See bug 1402779.
247 Services: Object.create(Services),
251 defineLazyGetter(exports.modules, "Debugger", () => {
252 const global = Cu.getGlobalForObject(this);
253 // Debugger may already have been added by RecordReplayControl getter
254 if (global.Debugger) {
255 return global.Debugger;
257 const { addDebuggerToGlobal } = ChromeUtils.import(
258 "resource://gre/modules/jsdebugger.jsm"
260 addDebuggerToGlobal(global);
261 return global.Debugger;
264 defineLazyGetter(exports.modules, "ChromeDebugger", () => {
265 const { addDebuggerToGlobal } = ChromeUtils.import(
266 "resource://gre/modules/jsdebugger.jsm"
268 addDebuggerToGlobal(debuggerSandbox);
269 return debuggerSandbox.Debugger;
272 defineLazyGetter(exports.modules, "RecordReplayControl", () => {
273 // addDebuggerToGlobal also adds the RecordReplayControl object.
274 const global = Cu.getGlobalForObject(this);
275 // RecordReplayControl may already have been added by Debugger getter
276 if (global.RecordReplayControl) {
277 return global.RecordReplayControl;
279 const { addDebuggerToGlobal } = ChromeUtils.import(
280 "resource://gre/modules/jsdebugger.jsm"
282 addDebuggerToGlobal(global);
283 return global.RecordReplayControl;
286 defineLazyGetter(exports.modules, "InspectorUtils", () => {
287 if (exports.modules.Debugger.recordReplayProcessKind() == "Middleman") {
288 const ReplayInspector = require("devtools/server/actors/replay/inspector");
289 return ReplayInspector.createInspectorUtils(InspectorUtils);
291 return InspectorUtils;
294 defineLazyGetter(exports.modules, "Timer", () => {
298 } = require("resource://gre/modules/Timer.jsm");
299 // Do not return Cu.import result, as DevTools loader would freeze Timer.jsm globals...
306 defineLazyGetter(exports.modules, "xpcInspector", () => {
307 return Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
310 // List of all custom globals exposed to devtools modules.
311 // Changes here should be mirrored to devtools/.eslintrc.
318 // Make sure `define` function exists. This allows defining some modules
319 // in AMD format while retaining CommonJS compatibility through this hook.
320 // JSON Viewer needs modules in AMD format, as it currently uses RequireJS
321 // from a content document and can't access our usual loaders. So, any
322 // modules shared with the JSON Viewer should include a define wrapper:
324 // // Make this available to both AMD and CJS environments
325 // define(function(require, exports, module) {
329 // Bug 1248830 will work out a better plan here for our content module
330 // loading needs, especially as we head towards devtools.html.
332 factory(this.require, this.exports, this.module);
346 lazyGetter: defineLazyGetter,
347 lazyImporter: defineLazyModuleGetter,
348 lazyServiceGetter: defineLazyServiceGetter,
349 lazyRequireGetter: lazyRequireGetter,
350 // Defined by Loader.jsm
354 reportError: Cu.reportError,
355 StructuredCloneHolder,
361 // DevTools loader copy globals property descriptors on each module global
362 // object so that we have to memoize them from here in order to instantiate each
364 // `globals` is a cache object on which we put all global values
365 // and we set getters on `exports.globals` returning `globals` values.
367 function lazyGlobal(name, getter) {
368 defineLazyGetter(globals, name, getter);
369 Object.defineProperty(exports.globals, name, {
371 return globals[name];
378 // Lazily define a few things so that the corresponding jsms are only loaded
380 lazyGlobal("clearTimeout", () => {
381 return require("resource://gre/modules/Timer.jsm").clearTimeout;
383 lazyGlobal("setTimeout", () => {
384 return require("resource://gre/modules/Timer.jsm").setTimeout;
386 lazyGlobal("clearInterval", () => {
387 return require("resource://gre/modules/Timer.jsm").clearInterval;
389 lazyGlobal("setInterval", () => {
390 return require("resource://gre/modules/Timer.jsm").setInterval;
392 lazyGlobal("WebSocket", () => {
393 return Services.appShell.hiddenDOMWindow.WebSocket;
395 lazyGlobal("indexedDB", () => {
396 return require("devtools/shared/indexed-db").createDevToolsIndexedDB(
400 lazyGlobal("isReplaying", () => {
401 return exports.modules.Debugger.recordReplayProcessKind() == "Middleman";
403 lazyGlobal("CSSRule", () => {
404 if (exports.modules.Debugger.recordReplayProcessKind() == "Middleman") {
405 const ReplayInspector = require("devtools/server/actors/replay/inspector");
406 return ReplayInspector.createCSSRule(CSSRule);