--- a/devtools/shared/Loader.jsm
+++ b/devtools/shared/Loader.jsm
@@ -5,17 +5,18 @@
"use strict";
/**
* Manages the base loader (base-loader.js) instance used to load the developer tools.
*/
var { utils: Cu } = Components;
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-var { Loader, descriptor, resolveURI } = Cu.import("resource://devtools/shared/base-loader.js", {});
+var { Loader, Require, descriptor, resolveURI, unload } =
+ Cu.import("resource://devtools/shared/base-loader.js", {});
var { requireRawId } = Cu.import("resource://devtools/shared/loader-plugin-raw.jsm", {});
this.EXPORTED_SYMBOLS = ["DevToolsLoader", "devtools", "BuiltinProvider",
"require", "loader"];
/**
* Providers are different strategies for loading the devtools.
*/
@@ -62,17 +63,17 @@ BuiltinProvider.prototype = {
// When creating a Loader invisible to the Debugger, we have to ensure
// using only modules and not depend on any JSM. As everything that is
// not loaded with Loader isn't going to respect `invisibleToDebugger`.
// But we have to keep using Promise.jsm for other loader to prevent
// breaking unhandled promise rejection in tests.
if (this.invisibleToDebugger) {
paths.promise = "resource://gre/modules/Promise-backend.js";
}
- this.loader = new Loader.Loader({
+ this.loader = new Loader({
id: "fx-devtools",
paths,
invisibleToDebugger: this.invisibleToDebugger,
sharedGlobal: true,
sharedGlobalBlocklist: [],
sandboxName: "DevTools (Module loader)",
noSandboxAddonId: true,
requireHook: (id, require) => {
@@ -80,17 +81,17 @@ BuiltinProvider.prototype = {
return requireRawId(id, require);
}
return require(id);
},
});
},
unload: function (reason) {
- Loader.unload(this.loader, reason);
+ unload(this.loader, reason);
delete this.loader;
},
};
var gNextLoaderID = 0;
/**
* The main devtools API. The standard instance of this loader is exported as
@@ -163,17 +164,17 @@ DevToolsLoader.prototype = {
this._provider.unload("newprovider");
}
this._provider = provider;
// Pass through internal loader settings specific to this loader instance
this._provider.invisibleToDebugger = this.invisibleToDebugger;
this._provider.load();
- this.require = Loader.Require(this._provider.loader, { id: "devtools" });
+ this.require = Require(this._provider.loader, { id: "devtools" });
// Fetch custom pseudo modules and globals
let { modules, globals } = this.require("devtools/shared/builtin-modules");
// When creating a Loader for the browser toolbox, we have to use
// Promise-backend.js, as a Loader module. Instead of Promise.jsm which
// can't be flagged as invisible to debugger.
if (this.invisibleToDebugger) {
--- a/devtools/shared/base-loader.js
+++ b/devtools/shared/base-loader.js
@@ -1,32 +1,15 @@
/* 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/. */
-;((factory) => { // Module boilerplate :(
- if (typeof(require) === 'function') { // CommonJS
- require("chrome").Cu.import(module.uri, exports);
- }
- else if (~String(this).indexOf('BackstagePass')) { // JSM
- let module = { uri: __URI__, id: "toolkit/loader", exports: Object.create(null) }
- factory(module);
- Object.assign(this, module.exports);
- this.EXPORTED_SYMBOLS = Object.getOwnPropertyNames(module.exports);
- }
- else {
- throw Error("Loading environment is not supported");
- }
-})(module => {
-
'use strict';
-module.metadata = {
- "stability": "unstable"
-};
+this.EXPORTED_SYMBOLS = ["Loader", "resolveURI", "Module", "Require"]
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
results: Cr, manager: Cm } = Components;
const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1'].
getService(Ci.mozIJSSubScriptLoader);
const { addObserver, notifyObservers } = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
@@ -126,17 +109,16 @@ function freeze(object) {
// Returns map of given `object`-s own property descriptors.
const descriptor = iced(function descriptor(object) {
let value = {};
for (let name of getOwnIdentifiers(object))
value[name] = getOwnPropertyDescriptor(object, name)
return value;
});
-Loader.descriptor = descriptor;
// Freeze important built-ins so they can't be used by untrusted code as a
// message passing channel.
freeze(Object);
freeze(Object.prototype);
freeze(Function);
freeze(Function.prototype);
freeze(Array);
@@ -163,25 +145,22 @@ function iced(f) {
const override = iced(function override(target, source) {
let properties = descriptor(target);
for (let name of getOwnIdentifiers(source || {}))
properties[name] = getOwnPropertyDescriptor(source, name);
return Object.defineProperties({}, properties);
});
-Loader.override = override;
function sourceURI(uri) { return String(uri).split(" -> ").pop(); }
-Loader.sourceURI = iced(sourceURI);
-function isntLoaderFrame(frame) { return frame.fileName !== module.uri }
+function isntLoaderFrame(frame) { return frame.fileName !== __URI__ }
function parseURI(uri) { return String(uri).split(" -> ").pop(); }
-Loader.parseURI = parseURI;
function parseStack(stack) {
let lines = String(stack).split("\n");
return lines.reduce(function(frames, line) {
if (line) {
let atIndex = line.indexOf("@");
let columnIndex = line.lastIndexOf(":");
let lineIndex = line.lastIndexOf(":", columnIndex - 1);
@@ -194,28 +173,26 @@ function parseStack(stack) {
name: name,
lineNumber: lineNumber,
columnNumber: columnNumber
});
}
return frames;
}, []);
}
-Loader.parseStack = parseStack;
function serializeStack(frames) {
return frames.reduce(function(stack, frame) {
return frame.name + "@" +
frame.fileName + ":" +
frame.lineNumber + ":" +
frame.columnNumber + "\n" +
stack;
}, "");
}
-Loader.serializeStack = serializeStack;
class DefaultMap extends Map {
constructor(createItem, items = undefined) {
super(items);
this.createItem = createItem;
}
@@ -388,17 +365,16 @@ function join(base, ...paths) {
// or we wind up stripping too many slashes and producing invalid URLs.
let match = /^((?:resource|file|chrome)\:\/\/[^\/]*|jar:[^!]+!)(.*)/.exec(base);
if (match) {
return match[1] + normalize([match[2], ...paths].join("/"));
}
return normalize([base, ...paths].join("/"));
}
-Loader.join = join;
// Function takes set of options and returns a JS sandbox. Function may be
// passed set of options:
// - `name`: A string value which identifies the sandbox in about:memory. Will
// throw exception if omitted.
// - `principal`: String URI or `nsIPrincipal` for the sandbox. Defaults to
// system principal.
// - `prototype`: Ancestor for the sandbox that will be created. Defaults to
@@ -441,17 +417,16 @@ const Sandbox = iced(function Sandbox(op
// to avoid shadowing.
delete sandbox.Iterator;
delete sandbox.Components;
delete sandbox.importFunction;
delete sandbox.debug;
return sandbox;
});
-Loader.Sandbox = Sandbox;
// Evaluates code from the given `uri` into given `sandbox`. If
// `options.source` is passed, then that code is evaluated instead.
// Optionally following options may be given:
// - `options.encoding`: Source encoding, defaults to 'UTF-8'.
// - `options.line`: Line number to start count from for stack traces.
// Defaults to 1.
// - `options.version`: Version of JS used, defaults to '1.8'.
@@ -461,17 +436,16 @@ const evaluate = iced(function evaluate(
line: 1,
version: '1.8',
source: null
}, options);
return source ? Cu.evalInSandbox(source, sandbox, version, uri, line)
: loadSubScript(uri, sandbox, encoding);
});
-Loader.evaluate = evaluate;
// Populates `exports` of the given CommonJS `module` object, in the context
// of the given `loader` by evaluating code associated with it.
const load = iced(function load(loader, module) {
let { sandboxes, globals, loadModuleHook } = loader;
let require = Require(loader, module);
// We expose set of properties defined by `CommonJS` specification via
@@ -589,17 +563,16 @@ const load = iced(function load(loader,
// Only freeze the exports object if we created it ourselves. Modules
// which completely replace the exports object and still want it
// frozen need to freeze it themselves.
if (module.exports === originalExports)
Object.freeze(module.exports);
return module;
});
-Loader.load = load;
// Utility function to normalize module `uri`s so they have `.js` extension.
function normalizeExt(uri) {
return isJSURI(uri) ? uri :
isJSONURI(uri) ? uri :
isJSMURI(uri) ? uri :
uri + '.js';
}
@@ -621,17 +594,16 @@ const resolve = iced(function resolve(id
// Joining and normalizing removes the './' from relative files.
// We need to ensure the resolution still has the root
if (base.startsWith('./'))
resolved = './' + resolved;
return resolved;
});
-Loader.resolve = resolve;
// Attempts to load `path` and then `path.js`
// Returns `path` with valid file, or `undefined` otherwise
function resolveAsFile(path) {
// Append '.js' to path name unless it's another support filetype
path = normalizeExt(path);
if (urlCache.exists(path)) {
return path;
@@ -693,17 +665,17 @@ function* getNodeModulePaths(rootURI, st
// Node-style module lookup
// Takes an id and path and attempts to load a file using node's resolving
// algorithm.
// `id` should already be resolved relatively at this point.
// http://nodejs.org/api/modules.html#modules_all_together
const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
// Resolve again
- id = Loader.resolve(id, requirer);
+ id = resolve(id, requirer);
// If this is already an absolute URI then there is no resolution to do
if (isAbsoluteURI(id)) {
return null;
}
// we assume that extensions are correct, i.e., a directory doesnt't have '.js'
// and a js file isn't named 'file.json.js'
@@ -728,18 +700,16 @@ const nodeResolve = iced(function nodeRe
}
// We would not find lookup for things like `sdk/tabs`, as that's part of
// the alias mapping. If during `generateMap`, the runtime lookup resolves
// with `resolveURI` -- if during runtime, then `resolve` will throw.
return null;
});
-Loader.nodeResolve = nodeResolve;
-
function addTrailingSlash(path) {
return path.replace(/\/*$/, "/");
}
function compileMapping(paths) {
// Make mapping array that is sorted from longest path to shortest path.
let mapping = Object.keys(paths)
.sort((a, b) => b.length - a.length)
@@ -786,17 +756,16 @@ function compileMapping(paths) {
const resolveURI = iced(function resolveURI(id, mapping) {
// Do not resolve if already a resource URI
if (isAbsoluteURI(id))
return normalizeExt(id);
return normalizeExt(mapping(id))
});
-Loader.resolveURI = resolveURI;
/**
* Defines lazy getters on the given object, which lazily require the
* given module the first time they are accessed, and then resolve that
* module's exported properties.
*
* @param {object} obj
* The target object on which to define the lazy getters.
@@ -853,17 +822,17 @@ const Require = iced(function Require(lo
manifest, rootURI, isNative, requireHook
} = loader;
if (isSystemURI(requirer.uri)) {
// Built-in modules don't require the expensive module resolution
// algorithm used by SDK add-ons, so give them the more efficient standard
// resolve instead.
isNative = false;
- loaderResolve = Loader.resolve;
+ loaderResolve = resolve;
}
function require(id) {
if (!id) // Throw if `id` is not passed.
throw Error('You must provide a module name when calling require() from '
+ requirer.id, requirer.uri);
if (requireHook) {
@@ -977,17 +946,17 @@ const Require = iced(function Require(lo
});
}
// If not found in the map, not a node module, and wasn't able to be
// looked up, it's something
// found in the paths most likely, like `sdk/tabs`, which should
// be resolved relatively if needed using traditional resolve
if (!requirement) {
- requirement = isRelative(id) ? Loader.resolve(id, requirer.id) : id;
+ requirement = isRelative(id) ? resolve(id, requirer.id) : id;
}
}
else if (modules[id]) {
uri = requirement = id;
}
else if (requirer) {
// Resolve `id` to its requirer if it's relative.
requirement = loaderResolve(id, requirer.id);
@@ -1028,55 +997,51 @@ const Require = iced(function Require(lo
return require(prefix + id);
};
};
// Make `require.main === module` evaluate to true in main module scope.
require.main = loader.main === requirer ? requirer : undefined;
return iced(require);
});
-Loader.Require = Require;
const main = iced(function main(loader, id) {
// If no main entry provided, and native loader is used,
// read the entry in the manifest
if (!id && loader.isNative)
id = getManifestMain(loader.manifest);
let uri = resolveURI(id, loader.mapping);
let module = loader.main = loader.modules[uri] = Module(id, uri);
return loader.load(loader, module).exports;
});
-Loader.main = main;
// Makes module object that is made available to CommonJS modules when they
// are evaluated, along with `exports` and `require`.
const Module = iced(function Module(id, uri) {
return Object.create(null, {
id: { enumerable: true, value: id },
exports: { enumerable: true, writable: true, value: Object.create(null),
configurable: true },
uri: { value: uri }
});
});
-Loader.Module = Module;
// Takes `loader`, and unload `reason` string and notifies all observers that
// they should cleanup after them-self.
const unload = iced(function unload(loader, reason) {
// subject is a unique object created per loader instance.
// This allows any code to cleanup on loader unload regardless of how
// it was loaded. To handle unload for specific loader subject may be
// asserted against loader.destructor or require('@loader/unload')
// Note: We don not destroy loader's module cache or sandboxes map as
// some modules may do cleanup in subsequent turns of event loop. Destroying
// cache may cause module identity problems in such cases.
let subject = { wrappedJSObject: loader.destructor };
notifyObservers(subject, 'sdk:loader:destroy', reason);
});
-Loader.unload = unload;
// Function makes new loader that can be used to load CommonJS modules
// described by a given `options.manifest`. Loader takes following options:
// - `globals`: Optional map of globals, that all module scopes will inherit
// from. Map is also exposed under `globals` property of the returned loader
// so it can be extended further later. Defaults to `{}`.
// - `modules` Optional map of built-in module exports mapped by module id.
// These modules will incorporated into module cache. Each module will be
@@ -1109,18 +1074,18 @@ function Loader(options) {
});
Object.defineProperty(this, "console", { value: console });
return this.console;
}
},
checkCompatibility: false,
resolve: options.isNative ?
// Make the returned resolve function have the same signature
- (id, requirer) => Loader.nodeResolve(id, requirer, { rootURI: normalizeRootURI(rootURI) }) :
- Loader.resolve,
+ (id, requirer) => nodeResolve(id, requirer, { rootURI: normalizeRootURI(rootURI) }) :
+ resolve,
sharedGlobalBlocklist: ["sdk/indexed-db"],
waiveIntereposition: false
}, options);
// Create overrides defaults, none at the moment
if (typeof manifest != "object" || !manifest) {
manifest = {};
}
@@ -1245,27 +1210,23 @@ function Loader(options) {
if (isNative) {
returnObj.isNative = { enumerable: false, value: true };
returnObj.manifest = { enumerable: false, value: manifest };
returnObj.rootURI = { enumerable: false, value: normalizeRootURI(rootURI) };
}
return freeze(Object.create(null, returnObj));
};
-Loader.Loader = Loader;
var isSystemURI = uri => /^resource:\/\/(gre|devtools|testing-common)\//.test(uri);
var isJSONURI = uri => uri.endsWith('.json');
var isJSMURI = uri => uri.endsWith('.jsm');
var isJSURI = uri => uri.endsWith('.js');
var isAbsoluteURI = uri => /^(resource|chrome|file|jar):/.test(uri);
var isRelative = id => id.startsWith(".");
// Default `main` entry to './index.js' and ensure is relative,
// since node allows 'lib/index.js' without relative `./`
function getManifestMain(manifest) {
let main = manifest.main || './index.js';
return isRelative(main) ? main : './' + main;
}
-
-module.exports = iced(Loader);
-});
--- a/devtools/shared/builtin-modules.js
+++ b/devtools/shared/builtin-modules.js
@@ -9,17 +9,16 @@
* 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, Cc, Ci } = require("chrome");
-const { Loader } = Cu.import("resource://devtools/shared/base-loader.js", {});
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
const jsmScope = Cu.import("resource://gre/modules/Services.jsm", {});
const { Services } = jsmScope;
// Steal various globals only available in JSM scope (and not Sandbox one)
const { PromiseDebugging, ChromeUtils, ThreadSafeChromeUtils, HeapSnapshot,
atob, btoa, TextEncoder, TextDecoder } = jsmScope;
// Create a single Sandbox to access global properties needed in this module.
@@ -167,17 +166,16 @@ function lazyRequireGetter(obj, property
configurable: true,
enumerable: true
});
}
// List of pseudo modules exposed to all devtools modules.
exports.modules = {
"Services": Object.create(Services),
- "toolkit/loader": Loader,
promise,
PromiseDebugging,
ChromeUtils,
ThreadSafeChromeUtils,
HeapSnapshot,
FileReader,
};