Bug 1622408 [wpt PR 22244] - Restore the event delegate for a CSSTransition after...
[gecko.git] / devtools / shared / builtin-modules.js
blobb22c0cdc6e548d0384487b2164d52b9a0b9ab034
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   BrowsingContext,
26   console,
27   DebuggerNotificationObserver,
28   DOMPoint,
29   DOMQuad,
30   DOMRect,
31   HeapSnapshot,
32   NamedNodeMap,
33   NodeFilter,
34   StructuredCloneHolder,
35   TelemetryStopwatch,
36 } = Cu.getGlobalForObject(jsmScope);
38 // Create a single Sandbox to access global properties needed in this module.
39 // Sandbox are memory expensive, so we should create as little as possible.
40 const debuggerSandbox = Cu.Sandbox(systemPrincipal, {
41   // This sandbox is also reused for ChromeDebugger implementation.
42   // As we want to load the `Debugger` API for debugging chrome contexts,
43   // we have to ensure loading it in a distinct compartment from its debuggee.
44   freshCompartment: true,
46   wantGlobalProperties: [
47     "atob",
48     "btoa",
49     "Blob",
50     "ChromeUtils",
51     "CSS",
52     "CSSRule",
53     "DOMParser",
54     "Element",
55     "Event",
56     "FileReader",
57     "FormData",
58     "indexedDB",
59     "InspectorUtils",
60     "Node",
61     "TextDecoder",
62     "TextEncoder",
63     "URL",
64     "XMLHttpRequest",
65   ],
66 });
68 const {
69   atob,
70   btoa,
71   Blob,
72   ChromeUtils,
73   CSS,
74   CSSRule,
75   DOMParser,
76   Element,
77   Event,
78   FileReader,
79   FormData,
80   indexedDB,
81   InspectorUtils,
82   Node,
83   TextDecoder,
84   TextEncoder,
85   URL,
86   XMLHttpRequest,
87 } = debuggerSandbox;
89 /**
90  * Defines a getter on a specified object that will be created upon first use.
91  *
92  * @param object
93  *        The object to define the lazy getter on.
94  * @param name
95  *        The name of the getter to define on object.
96  * @param lambda
97  *        A function that returns what the getter should return.  This will
98  *        only ever be called once.
99  */
100 function defineLazyGetter(object, name, lambda) {
101   Object.defineProperty(object, name, {
102     get: function() {
103       // Redefine this accessor property as a data property.
104       // Delete it first, to rule out "too much recursion" in case object is
105       // a proxy whose defineProperty handler might unwittingly trigger this
106       // getter again.
107       delete object[name];
108       const value = lambda.apply(object);
109       Object.defineProperty(object, name, {
110         value,
111         writable: true,
112         configurable: true,
113         enumerable: true,
114       });
115       return value;
116     },
117     configurable: true,
118     enumerable: true,
119   });
123  * Defines a getter on a specified object for a service.  The service will not
124  * be obtained until first use.
126  * @param object
127  *        The object to define the lazy getter on.
128  * @param name
129  *        The name of the getter to define on object for the service.
130  * @param contract
131  *        The contract used to obtain the service.
132  * @param interfaceName
133  *        The name of the interface to query the service to.
134  */
135 function defineLazyServiceGetter(object, name, contract, interfaceName) {
136   defineLazyGetter(object, name, function() {
137     return Cc[contract].getService(Ci[interfaceName]);
138   });
142  * Defines a getter on a specified object for a module.  The module will not
143  * be imported until first use. The getter allows to execute setup and
144  * teardown code (e.g.  to register/unregister to services) and accepts
145  * a proxy object which acts on behalf of the module until it is imported.
147  * @param object
148  *        The object to define the lazy getter on.
149  * @param name
150  *        The name of the getter to define on object for the module.
151  * @param resource
152  *        The URL used to obtain the module.
153  * @param symbol
154  *        The name of the symbol exported by the module.
155  *        This parameter is optional and defaults to name.
156  * @param preLambda
157  *        A function that is executed when the proxy is set up.
158  *        This will only ever be called once.
159  * @param postLambda
160  *        A function that is executed when the module has been imported to
161  *        run optional teardown procedures on the proxy object.
162  *        This will only ever be called once.
163  * @param proxy
164  *        An object which acts on behalf of the module to be imported until
165  *        the module has been imported.
166  */
167 function defineLazyModuleGetter(
168   object,
169   name,
170   resource,
171   symbol,
172   preLambda,
173   postLambda,
174   proxy
175 ) {
176   proxy = proxy || {};
178   if (typeof preLambda === "function") {
179     preLambda.apply(proxy);
180   }
182   defineLazyGetter(object, name, function() {
183     let temp = {};
184     try {
185       temp = ChromeUtils.import(resource);
187       if (typeof postLambda === "function") {
188         postLambda.apply(proxy);
189       }
190     } catch (ex) {
191       Cu.reportError("Failed to load module " + resource + ".");
192       throw ex;
193     }
194     return temp[symbol || name];
195   });
199  * Define a getter property on the given object that requires the given
200  * module. This enables delaying importing modules until the module is
201  * actually used.
203  * @param Object obj
204  *    The object to define the property on.
205  * @param String property
206  *    The property name.
207  * @param String module
208  *    The module path.
209  * @param Boolean destructure
210  *    Pass true if the property name is a member of the module's exports.
211  */
212 function lazyRequireGetter(obj, property, module, destructure) {
213   Object.defineProperty(obj, property, {
214     get: () => {
215       // Redefine this accessor property as a data property.
216       // Delete it first, to rule out "too much recursion" in case obj is
217       // a proxy whose defineProperty handler might unwittingly trigger this
218       // getter again.
219       delete obj[property];
220       const value = destructure
221         ? require(module)[property]
222         : require(module || property);
223       Object.defineProperty(obj, property, {
224         value,
225         writable: true,
226         configurable: true,
227         enumerable: true,
228       });
229       return value;
230     },
231     configurable: true,
232     enumerable: true,
233   });
236 // List of pseudo modules exposed to all devtools modules.
237 exports.modules = {
238   ChromeUtils,
239   DebuggerNotificationObserver,
240   HeapSnapshot,
241   promise,
242   // Expose "chrome" Promise, which aren't related to any document
243   // and so are never frozen, even if the browser loader module which
244   // pull it is destroyed. See bug 1402779.
245   Promise,
246   Services: Object.create(Services),
247   TelemetryStopwatch,
250 defineLazyGetter(exports.modules, "Debugger", () => {
251   const global = Cu.getGlobalForObject(this);
252   // Debugger may already have been added.
253   if (global.Debugger) {
254     return global.Debugger;
255   }
256   const { addDebuggerToGlobal } = ChromeUtils.import(
257     "resource://gre/modules/jsdebugger.jsm"
258   );
259   addDebuggerToGlobal(global);
260   return global.Debugger;
263 defineLazyGetter(exports.modules, "ChromeDebugger", () => {
264   const { addDebuggerToGlobal } = ChromeUtils.import(
265     "resource://gre/modules/jsdebugger.jsm"
266   );
267   addDebuggerToGlobal(debuggerSandbox);
268   return debuggerSandbox.Debugger;
271 defineLazyGetter(exports.modules, "InspectorUtils", () => {
272   return InspectorUtils;
275 defineLazyGetter(exports.modules, "Timer", () => {
276   const {
277     setTimeout,
278     clearTimeout,
279   } = require("resource://gre/modules/Timer.jsm");
280   // Do not return Cu.import result, as DevTools loader would freeze Timer.jsm globals...
281   return {
282     setTimeout,
283     clearTimeout,
284   };
287 defineLazyGetter(exports.modules, "xpcInspector", () => {
288   return Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
291 // List of all custom globals exposed to devtools modules.
292 // Changes here should be mirrored to devtools/.eslintrc.
293 exports.globals = {
294   atob,
295   Blob,
296   btoa,
297   BrowsingContext,
298   console,
299   CSS,
300   // Make sure `define` function exists.  This allows defining some modules
301   // in AMD format while retaining CommonJS compatibility through this hook.
302   // JSON Viewer needs modules in AMD format, as it currently uses RequireJS
303   // from a content document and can't access our usual loaders.  So, any
304   // modules shared with the JSON Viewer should include a define wrapper:
305   //
306   //   // Make this available to both AMD and CJS environments
307   //   define(function(require, exports, module) {
308   //     ... code ...
309   //   });
310   //
311   // Bug 1248830 will work out a better plan here for our content module
312   // loading needs, especially as we head towards devtools.html.
313   define(factory) {
314     factory(this.require, this.exports, this.module);
315   },
316   DOMParser,
317   DOMPoint,
318   DOMQuad,
319   NamedNodeMap,
320   NodeFilter,
321   DOMRect,
322   Element,
323   FileReader,
324   FormData,
325   isWorker: false,
326   loader: {
327     lazyGetter: defineLazyGetter,
328     lazyImporter: defineLazyModuleGetter,
329     lazyServiceGetter: defineLazyServiceGetter,
330     lazyRequireGetter: lazyRequireGetter,
331     // Defined by Loader.jsm
332     id: null,
333   },
334   Node,
335   reportError: Cu.reportError,
336   StructuredCloneHolder,
337   TextDecoder,
338   TextEncoder,
339   URL,
340   XMLHttpRequest,
342 // DevTools loader copy globals property descriptors on each module global
343 // object so that we have to memoize them from here in order to instantiate each
344 // global only once.
345 // `globals` is a cache object on which we put all global values
346 // and we set getters on `exports.globals` returning `globals` values.
347 const globals = {};
348 function lazyGlobal(name, getter) {
349   defineLazyGetter(globals, name, getter);
350   Object.defineProperty(exports.globals, name, {
351     get: function() {
352       return globals[name];
353     },
354     configurable: true,
355     enumerable: true,
356   });
359 // Lazily define a few things so that the corresponding jsms are only loaded
360 // when used.
361 lazyGlobal("clearTimeout", () => {
362   return require("resource://gre/modules/Timer.jsm").clearTimeout;
364 lazyGlobal("setTimeout", () => {
365   return require("resource://gre/modules/Timer.jsm").setTimeout;
367 lazyGlobal("clearInterval", () => {
368   return require("resource://gre/modules/Timer.jsm").clearInterval;
370 lazyGlobal("setInterval", () => {
371   return require("resource://gre/modules/Timer.jsm").setInterval;
373 lazyGlobal("WebSocket", () => {
374   return Services.appShell.hiddenDOMWindow.WebSocket;
376 lazyGlobal("indexedDB", () => {
377   return require("devtools/shared/indexed-db").createDevToolsIndexedDB(
378     indexedDB
379   );
382 const inspectorGlobals = {
383   CSSRule,
384   Event,
387 for (const [name, value] of Object.entries(inspectorGlobals)) {
388   lazyGlobal(name, () => {
389     return value;
390   });