summaryrefslogtreecommitdiffstats
path: root/devtools/shared/builtin-modules.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/builtin-modules.js')
-rw-r--r--devtools/shared/builtin-modules.js317
1 files changed, 317 insertions, 0 deletions
diff --git a/devtools/shared/builtin-modules.js b/devtools/shared/builtin-modules.js
new file mode 100644
index 0000000000..47d4b3d153
--- /dev/null
+++ b/devtools/shared/builtin-modules.js
@@ -0,0 +1,317 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * This module defines custom globals injected in all our modules and also
+ * pseudo modules that aren't separate files but just dynamically set values.
+ *
+ * As it does so, the module itself doesn't have access to these globals,
+ * nor the pseudo modules. Be careful to avoid loading any other js module as
+ * they would also miss them.
+ */
+
+const { Cu, Cc, Ci } = require("chrome");
+const promise = require("resource://gre/modules/Promise.jsm").Promise;
+const jsmScope = require("resource://devtools/shared/Loader.jsm");
+const { Services } = require("resource://gre/modules/Services.jsm");
+
+const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+
+// Steal various globals only available in JSM scope (and not Sandbox one)
+const {
+ BrowsingContext,
+ console,
+ DebuggerNotificationObserver,
+ DOMPoint,
+ DOMQuad,
+ DOMRect,
+ HeapSnapshot,
+ NamedNodeMap,
+ NodeFilter,
+ StructuredCloneHolder,
+ TelemetryStopwatch,
+} = Cu.getGlobalForObject(jsmScope);
+
+// Create a single Sandbox to access global properties needed in this module.
+// Sandbox are memory expensive, so we should create as little as possible.
+const debuggerSandbox = Cu.Sandbox(systemPrincipal, {
+ // This sandbox is also reused for ChromeDebugger implementation.
+ // As we want to load the `Debugger` API for debugging chrome contexts,
+ // we have to ensure loading it in a distinct compartment from its debuggee.
+ freshCompartment: true,
+
+ wantGlobalProperties: [
+ "atob",
+ "btoa",
+ "Blob",
+ "ChromeUtils",
+ "CSS",
+ "CSSRule",
+ "DOMParser",
+ "Element",
+ "Event",
+ "FileReader",
+ "FormData",
+ "indexedDB",
+ "InspectorUtils",
+ "Node",
+ "TextDecoder",
+ "TextEncoder",
+ "URL",
+ "XMLHttpRequest",
+ ],
+});
+
+const {
+ atob,
+ btoa,
+ Blob,
+ ChromeUtils,
+ CSS,
+ CSSRule,
+ DOMParser,
+ Element,
+ Event,
+ FileReader,
+ FormData,
+ indexedDB,
+ InspectorUtils,
+ Node,
+ TextDecoder,
+ TextEncoder,
+ URL,
+ XMLHttpRequest,
+} = debuggerSandbox;
+
+/**
+ * Defines a getter on a specified object that will be created upon first use.
+ *
+ * @param object
+ * The object to define the lazy getter on.
+ * @param name
+ * The name of the getter to define on object.
+ * @param lambda
+ * A function that returns what the getter should return. This will
+ * only ever be called once.
+ */
+function defineLazyGetter(object, name, lambda) {
+ Object.defineProperty(object, name, {
+ get: function() {
+ // Redefine this accessor property as a data property.
+ // Delete it first, to rule out "too much recursion" in case object is
+ // a proxy whose defineProperty handler might unwittingly trigger this
+ // getter again.
+ delete object[name];
+ const value = lambda.apply(object);
+ Object.defineProperty(object, name, {
+ value,
+ writable: true,
+ configurable: true,
+ enumerable: true,
+ });
+ return value;
+ },
+ configurable: true,
+ enumerable: true,
+ });
+}
+
+/**
+ * Defines a getter on a specified object for a service. The service will not
+ * be obtained until first use.
+ *
+ * @param object
+ * The object to define the lazy getter on.
+ * @param name
+ * The name of the getter to define on object for the service.
+ * @param contract
+ * The contract used to obtain the service.
+ * @param interfaceName
+ * The name of the interface to query the service to.
+ */
+function defineLazyServiceGetter(object, name, contract, interfaceName) {
+ defineLazyGetter(object, name, function() {
+ return Cc[contract].getService(Ci[interfaceName]);
+ });
+}
+
+/**
+ * Defines a getter on a specified object for a module. The module will not
+ * be imported until first use.
+ *
+ * @param object
+ * The object to define the lazy getter on.
+ * @param name
+ * The name of the getter to define on object for the module.
+ * @param resource
+ * The URL used to obtain the module.
+ */
+function defineLazyModuleGetter(object, name, resource) {
+ defineLazyGetter(object, name, function() {
+ try {
+ return ChromeUtils.import(resource)[name];
+ } catch (ex) {
+ Cu.reportError("Failed to load module " + resource + ".");
+ throw ex;
+ }
+ });
+}
+
+/**
+ * Define a getter property on the given object that requires the given
+ * module. This enables delaying importing modules until the module is
+ * actually used.
+ *
+ * Several getters can be defined at once by providing an array of
+ * properties and enabling destructuring.
+ *
+ * @param { Object } obj
+ * The object to define the property on.
+ * @param { String | Array<String> } properties
+ * String: Name of the property for the getter.
+ * Array<String>: When destructure is true, properties can be an array of
+ * strings to create several getters at once.
+ * @param { String } module
+ * The module path.
+ * @param { Boolean } destructure
+ * Pass true if the property name is a member of the module's exports.
+ */
+function lazyRequireGetter(obj, properties, module, destructure) {
+ if (Array.isArray(properties) && !destructure) {
+ throw new Error(
+ "Pass destructure=true to call lazyRequireGetter with an array of properties"
+ );
+ }
+
+ if (!Array.isArray(properties)) {
+ properties = [properties];
+ }
+
+ for (const property of properties) {
+ defineLazyGetter(obj, property, () => {
+ return destructure
+ ? require(module)[property]
+ : require(module || property);
+ });
+ }
+}
+
+// List of pseudo modules exposed to all devtools modules.
+exports.modules = {
+ ChromeUtils,
+ DebuggerNotificationObserver,
+ HeapSnapshot,
+ InspectorUtils,
+ promise,
+ // Expose "chrome" Promise, which aren't related to any document
+ // and so are never frozen, even if the browser loader module which
+ // pull it is destroyed. See bug 1402779.
+ Promise,
+ Services: Object.create(Services),
+ TelemetryStopwatch,
+};
+
+defineLazyGetter(exports.modules, "Debugger", () => {
+ const global = Cu.getGlobalForObject(this);
+ // Debugger may already have been added.
+ if (global.Debugger) {
+ return global.Debugger;
+ }
+ const { addDebuggerToGlobal } = ChromeUtils.import(
+ "resource://gre/modules/jsdebugger.jsm"
+ );
+ addDebuggerToGlobal(global);
+ return global.Debugger;
+});
+
+defineLazyGetter(exports.modules, "ChromeDebugger", () => {
+ const { addDebuggerToGlobal } = ChromeUtils.import(
+ "resource://gre/modules/jsdebugger.jsm"
+ );
+ addDebuggerToGlobal(debuggerSandbox);
+ return debuggerSandbox.Debugger;
+});
+
+defineLazyGetter(exports.modules, "xpcInspector", () => {
+ return Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
+});
+
+// List of all custom globals exposed to devtools modules.
+// Changes here should be mirrored to devtools/.eslintrc.
+exports.globals = {
+ atob,
+ Blob,
+ btoa,
+ BrowsingContext,
+ console,
+ CSS,
+ CSSRule,
+ DOMParser,
+ DOMPoint,
+ DOMQuad,
+ Event,
+ NamedNodeMap,
+ NodeFilter,
+ DOMRect,
+ Element,
+ FileReader,
+ FormData,
+ isWorker: false,
+ loader: {
+ lazyGetter: defineLazyGetter,
+ lazyImporter: defineLazyModuleGetter,
+ lazyServiceGetter: defineLazyServiceGetter,
+ lazyRequireGetter: lazyRequireGetter,
+ // Defined by Loader.jsm
+ id: null,
+ },
+ Node,
+ reportError: Cu.reportError,
+ StructuredCloneHolder,
+ TextDecoder,
+ TextEncoder,
+ URL,
+ XMLHttpRequest,
+};
+// DevTools loader copy globals property descriptors on each module global
+// object so that we have to memoize them from here in order to instantiate each
+// global only once.
+// `globals` is a cache object on which we put all global values
+// and we set getters on `exports.globals` returning `globals` values.
+const globals = {};
+function lazyGlobal(name, getter) {
+ defineLazyGetter(globals, name, getter);
+ Object.defineProperty(exports.globals, name, {
+ get: function() {
+ return globals[name];
+ },
+ configurable: true,
+ enumerable: true,
+ });
+}
+
+// Lazily define a few things so that the corresponding jsms are only loaded
+// when used.
+lazyGlobal("clearTimeout", () => {
+ return require("resource://gre/modules/Timer.jsm").clearTimeout;
+});
+lazyGlobal("setTimeout", () => {
+ return require("resource://gre/modules/Timer.jsm").setTimeout;
+});
+lazyGlobal("clearInterval", () => {
+ return require("resource://gre/modules/Timer.jsm").clearInterval;
+});
+lazyGlobal("setInterval", () => {
+ return require("resource://gre/modules/Timer.jsm").setInterval;
+});
+lazyGlobal("WebSocket", () => {
+ return Services.appShell.hiddenDOMWindow.WebSocket;
+});
+lazyGlobal("indexedDB", () => {
+ return require("devtools/shared/indexed-db").createDevToolsIndexedDB(
+ indexedDB
+ );
+});