Bug 1539764 - Add a targetFront attribute to the Front class to retrieve the target...
[gecko.git] / devtools / shared / builtin-modules.js
blobd5abdd847e3f654ddd1772ba2a65c6dbf7ed48ae
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/. */
5 "use strict";
7 /**
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.
10  *
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.
14  */
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)
24 const {
25   console,
26   DebuggerNotificationObserver,
27   DOMPoint,
28   DOMQuad,
29   DOMRect,
30   HeapSnapshot,
31   NamedNodeMap,
32   NodeFilter,
33   StructuredCloneHolder,
34   TelemetryStopwatch,
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: [
48     "atob",
49     "btoa",
50     "Blob",
51     "ChromeUtils",
52     "CSS",
53     "CSSRule",
54     "DOMParser",
55     "Element",
56     "Event",
57     "FileReader",
58     "FormData",
59     "indexedDB",
60     "InspectorUtils",
61     "Node",
62     "TextDecoder",
63     "TextEncoder",
64     "URL",
65     "XMLHttpRequest",
66   ],
67 });
69 const {
70   atob,
71   btoa,
72   Blob,
73   ChromeUtils,
74   CSS,
75   CSSRule,
76   DOMParser,
77   Element,
78   Event,
79   FileReader,
80   FormData,
81   indexedDB,
82   InspectorUtils,
83   Node,
84   TextDecoder,
85   TextEncoder,
86   URL,
87   XMLHttpRequest,
88 } = debuggerSandbox;
90 /**
91  * Defines a getter on a specified object that will be created upon first use.
92  *
93  * @param object
94  *        The object to define the lazy getter on.
95  * @param name
96  *        The name of the getter to define on object.
97  * @param lambda
98  *        A function that returns what the getter should return.  This will
99  *        only ever be called once.
100  */
101 function defineLazyGetter(object, name, lambda) {
102   Object.defineProperty(object, name, {
103     get: function() {
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
107       // getter again.
108       delete object[name];
109       const value = lambda.apply(object);
110       Object.defineProperty(object, name, {
111         value,
112         writable: true,
113         configurable: true,
114         enumerable: true,
115       });
116       return value;
117     },
118     configurable: true,
119     enumerable: true,
120   });
124  * Defines a getter on a specified object for a service.  The service will not
125  * be obtained until first use.
127  * @param object
128  *        The object to define the lazy getter on.
129  * @param name
130  *        The name of the getter to define on object for the service.
131  * @param contract
132  *        The contract used to obtain the service.
133  * @param interfaceName
134  *        The name of the interface to query the service to.
135  */
136 function defineLazyServiceGetter(object, name, contract, interfaceName) {
137   defineLazyGetter(object, name, function() {
138     return Cc[contract].getService(Ci[interfaceName]);
139   });
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.
148  * @param object
149  *        The object to define the lazy getter on.
150  * @param name
151  *        The name of the getter to define on object for the module.
152  * @param resource
153  *        The URL used to obtain the module.
154  * @param symbol
155  *        The name of the symbol exported by the module.
156  *        This parameter is optional and defaults to name.
157  * @param preLambda
158  *        A function that is executed when the proxy is set up.
159  *        This will only ever be called once.
160  * @param postLambda
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.
164  * @param proxy
165  *        An object which acts on behalf of the module to be imported until
166  *        the module has been imported.
167  */
168 function defineLazyModuleGetter(
169   object,
170   name,
171   resource,
172   symbol,
173   preLambda,
174   postLambda,
175   proxy
176 ) {
177   proxy = proxy || {};
179   if (typeof preLambda === "function") {
180     preLambda.apply(proxy);
181   }
183   defineLazyGetter(object, name, function() {
184     let temp = {};
185     try {
186       temp = ChromeUtils.import(resource);
188       if (typeof postLambda === "function") {
189         postLambda.apply(proxy);
190       }
191     } catch (ex) {
192       Cu.reportError("Failed to load module " + resource + ".");
193       throw ex;
194     }
195     return temp[symbol || name];
196   });
200  * Define a getter property on the given object that requires the given
201  * module. This enables delaying importing modules until the module is
202  * actually used.
204  * @param Object obj
205  *    The object to define the property on.
206  * @param String property
207  *    The property name.
208  * @param String module
209  *    The module path.
210  * @param Boolean destructure
211  *    Pass true if the property name is a member of the module's exports.
212  */
213 function lazyRequireGetter(obj, property, module, destructure) {
214   Object.defineProperty(obj, property, {
215     get: () => {
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
219       // getter again.
220       delete obj[property];
221       const value = destructure
222         ? require(module)[property]
223         : require(module || property);
224       Object.defineProperty(obj, property, {
225         value,
226         writable: true,
227         configurable: true,
228         enumerable: true,
229       });
230       return value;
231     },
232     configurable: true,
233     enumerable: true,
234   });
237 // List of pseudo modules exposed to all devtools modules.
238 exports.modules = {
239   ChromeUtils,
240   DebuggerNotificationObserver,
241   HeapSnapshot,
242   promise,
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.
246   Promise,
247   Services: Object.create(Services),
248   TelemetryStopwatch,
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;
256   }
257   const { addDebuggerToGlobal } = ChromeUtils.import(
258     "resource://gre/modules/jsdebugger.jsm"
259   );
260   addDebuggerToGlobal(global);
261   return global.Debugger;
264 defineLazyGetter(exports.modules, "ChromeDebugger", () => {
265   const { addDebuggerToGlobal } = ChromeUtils.import(
266     "resource://gre/modules/jsdebugger.jsm"
267   );
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;
278   }
279   const { addDebuggerToGlobal } = ChromeUtils.import(
280     "resource://gre/modules/jsdebugger.jsm"
281   );
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);
290   }
291   return InspectorUtils;
294 defineLazyGetter(exports.modules, "Timer", () => {
295   const {
296     setTimeout,
297     clearTimeout,
298   } = require("resource://gre/modules/Timer.jsm");
299   // Do not return Cu.import result, as DevTools loader would freeze Timer.jsm globals...
300   return {
301     setTimeout,
302     clearTimeout,
303   };
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.
312 exports.globals = {
313   atob,
314   Blob,
315   btoa,
316   console,
317   CSS,
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:
323   //
324   //   // Make this available to both AMD and CJS environments
325   //   define(function(require, exports, module) {
326   //     ... code ...
327   //   });
328   //
329   // Bug 1248830 will work out a better plan here for our content module
330   // loading needs, especially as we head towards devtools.html.
331   define(factory) {
332     factory(this.require, this.exports, this.module);
333   },
334   DOMParser,
335   DOMPoint,
336   DOMQuad,
337   NamedNodeMap,
338   NodeFilter,
339   DOMRect,
340   Element,
341   Event,
342   FileReader,
343   FormData,
344   isWorker: false,
345   loader: {
346     lazyGetter: defineLazyGetter,
347     lazyImporter: defineLazyModuleGetter,
348     lazyServiceGetter: defineLazyServiceGetter,
349     lazyRequireGetter: lazyRequireGetter,
350     // Defined by Loader.jsm
351     id: null,
352   },
353   Node,
354   reportError: Cu.reportError,
355   StructuredCloneHolder,
356   TextDecoder,
357   TextEncoder,
358   URL,
359   XMLHttpRequest,
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
363 // global only once.
364 // `globals` is a cache object on which we put all global values
365 // and we set getters on `exports.globals` returning `globals` values.
366 const globals = {};
367 function lazyGlobal(name, getter) {
368   defineLazyGetter(globals, name, getter);
369   Object.defineProperty(exports.globals, name, {
370     get: function() {
371       return globals[name];
372     },
373     configurable: true,
374     enumerable: true,
375   });
378 // Lazily define a few things so that the corresponding jsms are only loaded
379 // when used.
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(
397     indexedDB
398   );
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);
407   }
408   return CSSRule;