123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- /* 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/. */
- ;(function(id, factory) { // Module boilerplate :(
- if (typeof(define) === 'function') { // RequireJS
- define(factory);
- } else if (typeof(require) === 'function') { // CommonJS
- factory.call(this, require, exports, module);
- } else if (~String(this).indexOf('BackstagePass')) { // JSM
- this[factory.name] = {};
- factory(function require(uri) {
- var imports = {};
- this['Components'].utils.import(uri, imports);
- return imports;
- }, this[factory.name], { uri: __URI__, id: id });
- this.EXPORTED_SYMBOLS = [factory.name];
- } else if (~String(this).indexOf('Sandbox')) { // Sandbox
- factory(function require(uri) {}, this, { uri: __URI__, id: id });
- } else { // Browser or alike
- var globals = this
- factory(function require(id) {
- return globals[id];
- }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
- }
- }).call(this, 'loader', function Loader(require, exports, module) {
- 'use strict';
- module.metadata = {
- "stability": "unstable"
- };
- 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 { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
- getService(Ci.nsIObserverService);
- const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
- const { Reflect } = Cu.import("resource://gre/modules/reflect.jsm", {});
- const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm");
- const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm");
- // Define some shortcuts.
- const bind = Function.call.bind(Function.bind);
- const getOwnPropertyNames = Object.getOwnPropertyNames;
- const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
- const define = Object.defineProperties;
- const prototypeOf = Object.getPrototypeOf;
- const create = Object.create;
- const keys = Object.keys;
- const NODE_MODULES = ["assert", "buffer_ieee754", "buffer", "child_process", "cluster", "console", "constants", "crypto", "_debugger", "dgram", "dns", "domain", "events", "freelist", "fs", "http", "https", "_linklist", "module", "net", "os", "path", "punycode", "querystring", "readline", "repl", "stream", "string_decoder", "sys", "timers", "tls", "tty", "url", "util", "vm", "zlib"];
- const COMPONENT_ERROR = '`Components` is not available in this context.\n' +
- 'Functionality provided by Components may be available in an SDK\n' +
- 'module: https://jetpack.mozillalabs.com/sdk/latest/docs/ \n\n' +
- 'However, if you still need to import Components, you may use the\n' +
- '`chrome` module\'s properties for shortcuts to Component properties:\n\n' +
- 'Shortcuts: \n' +
- ' Cc = Components' + '.classes \n' +
- ' Ci = Components' + '.interfaces \n' +
- ' Cu = Components' + '.utils \n' +
- ' CC = Components' + '.Constructor \n' +
- 'Example: \n' +
- ' let { Cc, Ci } = require(\'chrome\');\n';
- // Workaround for bug 674195. Freezing objects from other compartments fail,
- // so we use `Object.freeze` from the same component instead.
- function freeze(object) {
- if (prototypeOf(object) === null) {
- Object.freeze(object);
- }
- else {
- prototypeOf(prototypeOf(object.isPrototypeOf)).
- constructor. // `Object` from the owner compartment.
- freeze(object);
- }
- return object;
- }
- // Returns map of given `object`-s own property descriptors.
- const descriptor = iced(function descriptor(object) {
- let value = {};
- getOwnPropertyNames(object).forEach(function(name) {
- value[name] = getOwnPropertyDescriptor(object, name)
- });
- return value;
- });
- exports.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);
- freeze(Array.prototype);
- freeze(String);
- freeze(String.prototype);
- // This function takes `f` function sets it's `prototype` to undefined and
- // freezes it. We need to do this kind of deep freeze with all the exposed
- // functions so that untrusted code won't be able to use them a message
- // passing channel.
- function iced(f) {
- f.prototype = undefined;
- return freeze(f);
- }
- // Defines own properties of given `properties` object on the given
- // target object overriding any existing property with a conflicting name.
- // Returns `target` object. Note we only export this function because it's
- // useful during loader bootstrap when other util modules can't be used &
- // thats only case where this export should be used.
- const override = iced(function override(target, source) {
- let properties = descriptor(target)
- let extension = descriptor(source || {})
- getOwnPropertyNames(extension).forEach(function(name) {
- properties[name] = extension[name];
- });
- return define({}, properties);
- });
- exports.override = override;
- function sourceURI(uri) { return String(uri).split(" -> ").pop(); }
- exports.sourceURI = iced(sourceURI);
- function isntLoaderFrame(frame) { return frame.fileName !== module.uri }
- var parseStack = iced(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 fileName = sourceURI(line.slice(atIndex + 1, columnIndex));
- let lineNumber = parseInt(line.slice(columnIndex + 1));
- let name = line.slice(0, atIndex).split("(").shift();
- frames.unshift({
- fileName: fileName,
- name: name,
- lineNumber: lineNumber
- });
- }
- return frames;
- }, []);
- })
- exports.parseStack = parseStack
- var serializeStack = iced(function serializeStack(frames) {
- return frames.reduce(function(stack, frame) {
- return frame.name + "@" +
- frame.fileName + ":" +
- frame.lineNumber + "\n" +
- stack;
- }, "");
- })
- exports.serializeStack = serializeStack
- function readURI(uri) {
- let stream = NetUtil.newChannel(uri, 'UTF-8', null).open();
- let count = stream.available();
- let data = NetUtil.readInputStreamToString(stream, count, {
- charset: 'UTF-8'
- });
- stream.close();
- return data;
- }
- // Combines all arguments into a resolved, normalized path
- function join (...paths) {
- let resolved = normalize(pathJoin(...paths))
- // OS.File `normalize` strips out the second slash in
- // `resource://` or `chrome://`, and third slash in
- // `file:///`, so we work around this
- resolved = resolved.replace(/^resource\:\/([^\/])/, 'resource://$1');
- resolved = resolved.replace(/^file\:\/([^\/])/, 'file:///$1');
- resolved = resolved.replace(/^chrome\:\/([^\/])/, 'chrome://$1');
- return resolved;
- }
- exports.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
- // `{}`.
- // - `wantXrays`: A Boolean value indicating whether code outside the sandbox
- // wants X-ray vision with respect to objects inside the sandbox. Defaults
- // to `true`.
- // - `sandbox`: A sandbox to share JS compartment with. If omitted new
- // compartment will be created.
- // - `metadata`: A metadata object associated with the sandbox. It should
- // be JSON-serializable.
- // For more details see:
- // https://developer.mozilla.org/en/Components.utils.Sandbox
- const Sandbox = iced(function Sandbox(options) {
- // Normalize options and rename to match `Cu.Sandbox` expectations.
- options = {
- // Do not expose `Components` if you really need them (bad idea!) you
- // still can expose via prototype.
- wantComponents: false,
- sandboxName: options.name,
- principal: 'principal' in options ? options.principal : systemPrincipal,
- wantXrays: 'wantXrays' in options ? options.wantXrays : true,
- wantGlobalProperties: 'wantGlobalProperties' in options ?
- options.wantGlobalProperties : [],
- sandboxPrototype: 'prototype' in options ? options.prototype : {},
- sameGroupAs: 'sandbox' in options ? options.sandbox : null,
- invisibleToDebugger: 'invisibleToDebugger' in options ?
- options.invisibleToDebugger : false,
- metadata: 'metadata' in options ? options.metadata : {}
- };
- // Make `options.sameGroupAs` only if `sandbox` property is passed,
- // otherwise `Cu.Sandbox` will throw.
- if (!options.sameGroupAs)
- delete options.sameGroupAs;
- let sandbox = Cu.Sandbox(options.principal, options);
- // Each sandbox at creation gets set of own properties that will be shadowing
- // ones from it's prototype. We override delete such `sandbox` properties
- // to avoid shadowing.
- delete sandbox.Iterator;
- delete sandbox.Components;
- delete sandbox.importFunction;
- delete sandbox.debug;
- return sandbox;
- });
- exports.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'.
- const evaluate = iced(function evaluate(sandbox, uri, options) {
- let { source, line, version, encoding } = override({
- encoding: 'UTF-8',
- line: 1,
- version: '1.8',
- source: null
- }, options);
- return source ? Cu.evalInSandbox(source, sandbox, version, uri, line)
- : loadSubScript(uri, sandbox, encoding);
- });
- exports.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 } = loader;
- let require = Require(loader, module);
- // We expose set of properties defined by `CommonJS` specification via
- // prototype of the sandbox. Also globals are deeper in the prototype
- // chain so that each module has access to them as well.
- let descriptors = descriptor({
- require: require,
- module: module,
- exports: module.exports,
- get Components() {
- // Expose `Components` property to throw error on usage with
- // additional information
- throw new ReferenceError(COMPONENT_ERROR);
- }
- });
- let sandbox = sandboxes[module.uri] = Sandbox({
- name: module.uri,
- // Get an existing module sandbox, if any, so we can reuse its compartment
- // when creating the new one to reduce memory consumption.
- sandbox: sandboxes[keys(sandboxes).shift()],
- prototype: create(globals, descriptors),
- wantXrays: false,
- wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
- invisibleToDebugger: loader.invisibleToDebugger,
- metadata: {
- addonID: loader.id,
- URI: module.uri
- }
- });
- try {
- evaluate(sandbox, module.uri);
- } catch (error) {
- let { message, fileName, lineNumber } = error;
- let stack = error.stack || Error().stack;
- let frames = parseStack(stack).filter(isntLoaderFrame);
- let toString = String(error);
- let file = sourceURI(fileName);
- // Note that `String(error)` where error is from subscript loader does
- // not puts `:` after `"Error"` unlike regular errors thrown by JS code.
- // If there is a JS stack then this error has already been handled by an
- // inner module load.
- if (String(error) === "Error opening input stream (invalid filename?)") {
- let caller = frames.slice(0).pop();
- fileName = caller.fileName;
- lineNumber = caller.lineNumber;
- message = "Module `" + module.id + "` is not found at " + module.uri;
- toString = message;
- }
- // Workaround for a Bug 910653. Errors thrown by subscript loader
- // do not include `stack` field and above created error won't have
- // fileName or lineNumber of the module being loaded, so we ensure
- // it does.
- else if (frames[frames.length - 1].fileName !== file) {
- frames.push({ fileName: file, lineNumber: lineNumber, name: "" });
- }
- let prototype = typeof(error) === "object" ? error.constructor.prototype :
- Error.prototype;
- throw create(prototype, {
- message: { value: message, writable: true, configurable: true },
- fileName: { value: fileName, writable: true, configurable: true },
- lineNumber: { value: lineNumber, writable: true, configurable: true },
- stack: { value: serializeStack(frames), writable: true, configurable: true },
- toString: { value: function() toString, writable: true, configurable: true },
- });
- }
- if (module.exports && typeof(module.exports) === 'object')
- freeze(module.exports);
- return module;
- });
- exports.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';
- }
- // Strips `rootURI` from `string` -- used to remove absolute resourceURI
- // from a relative path
- function stripBase (rootURI, string) {
- return string.replace(rootURI, './');
- }
- // Utility function to join paths. In common case `base` is a
- // `requirer.uri` but in some cases it may be `baseURI`. In order to
- // avoid complexity we require `baseURI` with a trailing `/`.
- const resolve = iced(function resolve(id, base) {
- if (!isRelative(id)) return id;
- let basePaths = base.split('/');
- // Pop the last element in the `base`, because it is from a
- // relative file
- // '../mod.js' from '/path/to/file.js' should resolve to '/path/mod.js'
- basePaths.pop();
- if (!basePaths.length)
- return normalize(id);
- let resolved = join(basePaths.join('/'), id);
- // Joining and normalizing removes the './' from relative files.
- // We need to ensure the resolution still has the root
- if (isRelative(base))
- resolved = './' + resolved;
- return resolved;
- });
- exports.resolve = resolve;
- // 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, { manifest, rootURI }) {
- // Resolve again
- id = exports.resolve(id, requirer);
- // we assume that extensions are correct, i.e., a directory doesnt't have '.js'
- // and a js file isn't named 'file.json.js'
- let fullId = join(rootURI, id);
- let resolvedPath;
- if (resolvedPath = loadAsFile(fullId))
- return stripBase(rootURI, resolvedPath);
- else if (resolvedPath = loadAsDirectory(fullId))
- return stripBase(rootURI, resolvedPath);
- // If manifest has dependencies, attempt to look up node modules
- // in the `dependencies` list
- else if (manifest.dependencies) {
- let dirs = getNodeModulePaths(dirname(join(rootURI, requirer))).map(dir => join(dir, id));
- for (let i = 0; i < dirs.length; i++) {
- if (resolvedPath = loadAsFile(dirs[i]))
- return stripBase(rootURI, resolvedPath);
- if (resolvedPath = loadAsDirectory(dirs[i]))
- return stripBase(rootURI, resolvedPath);
- }
- }
- // 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 void 0;
- });
- exports.nodeResolve = nodeResolve;
- // Attempts to load `path` and then `path.js`
- // Returns `path` with valid file, or `undefined` otherwise
- function loadAsFile (path) {
- let found;
- // As per node's loader spec,
- // we first should try and load 'path' (with no extension)
- // before trying 'path.js'. We will not support this feature
- // due to performance, but may add it if necessary for adoption.
- try {
- // Append '.js' to path name unless it's another support filetype
- path = normalizeExt(path);
- readURI(path);
- found = path;
- } catch (e) {}
- return found;
- }
- // Attempts to load `path/package.json`'s `main` entry,
- // followed by `path/index.js`, or `undefined` otherwise
- function loadAsDirectory (path) {
- let found;
- try {
- // If `path/package.json` exists, parse the `main` entry
- // and attempt to load that
- let main = getManifestMain(JSON.parse(readURI(path + '/package.json')));
- if (main != null) {
- let tmpPath = join(path, main);
- if (found = loadAsFile(tmpPath))
- return found
- }
- try {
- let tmpPath = path + '/index.js';
- readURI(tmpPath);
- return tmpPath;
- } catch (e) {}
- } catch (e) {
- try {
- let tmpPath = path + '/index.js';
- readURI(tmpPath);
- return tmpPath;
- } catch (e) {}
- }
- return void 0;
- }
- // From `resolve` module
- // https://github.com/substack/node-resolve/blob/master/lib/node-modules-paths.js
- function getNodeModulePaths (start) {
- // Configurable in node -- do we need this to be configurable?
- let moduleDir = 'node_modules';
- let parts = start.split('/');
- let dirs = [];
- for (let i = parts.length - 1; i >= 0; i--) {
- if (parts[i] === moduleDir) continue;
- let dir = join(parts.slice(0, i + 1).join('/'), moduleDir);
- dirs.push(dir);
- }
- return dirs;
- }
- function addTrailingSlash (path) {
- return !path ? null : !path.endsWith('/') ? path + '/' : path;
- }
- // Utility function to determine of module id `name` is a built in
- // module in node (fs, path, etc.);
- function isNodeModule (name) {
- return !!~NODE_MODULES.indexOf(name);
- }
- // Make mapping array that is sorted from longest path to shortest path
- // to allow overlays. Used by `resolveURI`, returns an array
- function sortPaths (paths) {
- return keys(paths).
- sort(function(a, b) { return b.length - a.length }).
- map(function(path) { return [ path, paths[path] ] });
- }
- const resolveURI = iced(function resolveURI(id, mapping) {
- let count = mapping.length, index = 0;
- // Do not resolve if already a resource URI
- if (isResourceURI(id)) return normalizeExt(id);
- while (index < count) {
- let [ path, uri ] = mapping[index ++];
- if (id.indexOf(path) === 0)
- return normalizeExt(id.replace(path, uri));
- }
- return void 0; // otherwise we raise a warning, see bug 910304
- });
- exports.resolveURI = resolveURI;
- // Creates version of `require` that will be exposed to the given `module`
- // in the context of the given `loader`. Each module gets own limited copy
- // of `require` that is allowed to load only a modules that are associated
- // with it during link time.
- const Require = iced(function Require(loader, requirer) {
- let {
- modules, mapping, resolve, load, manifest, rootURI, isNative, requireMap
- } = loader;
- 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);
- let requirement;
- let uri;
- // TODO should get native Firefox modules before doing node-style lookups
- // to save on loading time
- if (isNative) {
- // If a requireMap is available from `generateMap`, use that to
- // immediately resolve the node-style mapping.
- if (requireMap && requireMap[requirer.id])
- requirement = requireMap[requirer.id][id];
- // For native modules, we want to check if it's a module specified
- // in 'modules', like `chrome`, or `@loader` -- if it exists,
- // just set the uri to skip resolution
- if (!requirement && modules[id])
- uri = requirement = id;
- // If no requireMap was provided, or resolution not found in
- // the requireMap, and not a npm dependency, attempt a runtime lookup
- if (!requirement && !isNodeModule(id)) {
- // If `isNative` defined, this is using the new, native-style
- // loader, not cuddlefish, so lets resolve using node's algorithm
- // and get back a path that needs to be resolved via paths mapping
- // in `resolveURI`
- requirement = resolve(id, requirer.id, {
- manifest: manifest,
- rootURI: rootURI
- });
- }
- // 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) ? exports.resolve(id, requirer.id) : id;
- }
- } else {
- // Resolve `id` to its requirer if it's relative.
- requirement = requirer ? resolve(id, requirer.id) : id;
- }
- // Resolves `uri` of module using loaders resolve function.
- uri = uri || resolveURI(requirement, mapping);
- if (!uri) // Throw if `uri` can not be resolved.
- throw Error('Module: Can not resolve "' + id + '" module required by ' +
- requirer.id + ' located at ' + requirer.uri, requirer.uri);
- let module = null;
- // If module is already cached by loader then just use it.
- if (uri in modules) {
- module = modules[uri];
- }
- else if (isJSMURI(uri)) {
- module = modules[uri] = Module(requirement, uri);
- module.exports = Cu.import(uri, {});
- freeze(module);
- }
- else if (isJSONURI(uri)) {
- let data;
- // First attempt to load and parse json uri
- // ex: `test.json`
- // If that doesn't exist, check for `test.json.js`
- // for node parity
- try {
- data = JSON.parse(readURI(uri));
- module = modules[uri] = Module(requirement, uri);
- module.exports = data;
- freeze(module);
- }
- catch (err) {
- // If error thrown from JSON parsing, throw that, do not
- // attempt to find .json.js file
- if (err && /JSON\.parse/.test(err.message))
- throw err;
- uri = uri + '.js';
- }
- }
- // If not yet cached, load and cache it.
- // We also freeze module to prevent it from further changes
- // at runtime.
- if (!(uri in modules)) {
- // Many of the loader's functionalities are dependent
- // on modules[uri] being set before loading, so we set it and
- // remove it if we have any errors.
- module = modules[uri] = Module(requirement, uri);
- try {
- freeze(load(loader, module));
- }
- catch (e) {
- // Clear out modules cache so we can throw on a second invalid require
- delete modules[uri];
- // Also clear out the Sandbox that was created
- delete loader.sandboxes[uri];
- throw e;
- }
- }
- return module.exports;
- }
- // Make `require.main === module` evaluate to true in main module scope.
- require.main = loader.main === requirer ? requirer : undefined;
- return iced(require);
- });
- exports.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;
- });
- exports.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 create(null, {
- id: { enumerable: true, value: id },
- exports: { enumerable: true, writable: true, value: create(null) },
- uri: { value: uri }
- });
- });
- exports.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);
- });
- exports.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
- // frozen.
- // - `resolve` Optional module `id` resolution function. If given it will be
- // used to resolve module URIs, by calling it with require term, requirer
- // module object (that has `uri` property) and `baseURI` of the loader.
- // If `resolve` does not returns `uri` string exception will be thrown by
- // an associated `require` call.
- const Loader = iced(function Loader(options) {
- let {
- modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative
- } = override({
- paths: {},
- modules: {},
- globals: {
- console: console
- },
- resolve: options.isNative ?
- exports.nodeResolve :
- exports.resolve,
- }, options);
- // We create an identity object that will be dispatched on an unload
- // event as subject. This way unload listeners will be able to assert
- // which loader is unloaded. Please note that we intentionally don't
- // use `loader` as subject to prevent a loader access leakage through
- // observer notifications.
- let destructor = freeze(create(null));
- let mapping = sortPaths(paths);
- // Define pseudo modules.
- modules = override({
- '@loader/unload': destructor,
- '@loader/options': options,
- 'chrome': { Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
- CC: bind(CC, Components), components: Components,
- // `ChromeWorker` has to be inject in loader global scope.
- // It is done by bootstrap.js:loadSandbox for the SDK.
- ChromeWorker: ChromeWorker
- }
- }, modules);
- modules = keys(modules).reduce(function(result, id) {
- // We resolve `uri` from `id` since modules are cached by `uri`.
- let uri = resolveURI(id, mapping);
- // In native loader, the mapping will not contain values for
- // pseudomodules -- store them as their ID rather than the URI
- if (isNative && !uri)
- uri = id;
- let module = Module(id, uri);
- module.exports = freeze(modules[id]);
- result[uri] = freeze(module);
- return result;
- }, {});
- // Loader object is just a representation of a environment
- // state. We freeze it and mark make it's properties non-enumerable
- // as they are pure implementation detail that no one should rely upon.
- let returnObj = {
- destructor: { enumerable: false, value: destructor },
- globals: { enumerable: false, value: globals },
- mapping: { enumerable: false, value: mapping },
- // Map of module objects indexed by module URIs.
- modules: { enumerable: false, value: modules },
- // Map of module sandboxes indexed by module URIs.
- sandboxes: { enumerable: false, value: {} },
- resolve: { enumerable: false, value: resolve },
- // ID of the addon, if provided.
- id: { enumerable: false, value: options.id },
- // Whether the modules loaded should be ignored by the debugger
- invisibleToDebugger: { enumerable: false,
- value: options.invisibleToDebugger || false },
- load: { enumerable: false, value: options.load || load },
- // Main (entry point) module, it can be set only once, since loader
- // instance can have only one main module.
- main: new function() {
- let main;
- return {
- enumerable: false,
- get: function() { return main; },
- // Only set main if it has not being set yet!
- set: function(module) { main = main || module; }
- }
- }
- };
- if (isNative) {
- returnObj.isNative = { enumerable: false, value: true };
- returnObj.manifest = { enumerable: false, value: manifest };
- returnObj.requireMap = { enumerable: false, value: requireMap };
- returnObj.rootURI = { enumerable: false, value: addTrailingSlash(rootURI) };
- }
- return freeze(create(null, returnObj));
- });
- exports.Loader = Loader;
- let isJSONURI = uri => uri.substr(-5) === '.json';
- let isJSMURI = uri => uri.substr(-4) === '.jsm';
- let isJSURI = uri => uri.substr(-3) === '.js';
- let isResourceURI = uri => uri.substr(0, 11) === 'resource://';
- let isRelative = id => id[0] === '.'
- const generateMap = iced(function generateMap(options, callback) {
- let { rootURI, resolve, paths } = override({
- paths: {},
- resolve: exports.nodeResolve
- }, options);
- rootURI = addTrailingSlash(rootURI);
- let manifest;
- let manifestURI = join(rootURI, 'package.json');
- if (rootURI)
- manifest = JSON.parse(readURI(manifestURI));
- else
- throw new Error('No `rootURI` given to generate map');
- let main = getManifestMain(manifest);
- findAllModuleIncludes(main, {
- resolve: resolve,
- manifest: manifest,
- rootURI: rootURI
- }, {}, callback);
- });
- exports.generateMap = generateMap;
- // 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;
- }
- function findAllModuleIncludes (uri, options, results, callback) {
- let { resolve, manifest, rootURI } = options;
- results = results || {};
- // Abort if JSON or JSM
- if (isJSONURI(uri) || isJSMURI(uri)) {
- callback(results);
- return void 0;
- }
- findModuleIncludes(join(rootURI, uri), modules => {
- // If no modules are included in the file, just call callback immediately
- if (!modules.length) {
- callback(results);
- return void 0;
- }
- results[uri] = modules.reduce((agg, mod) => {
- let resolved = resolve(mod, uri, { manifest: manifest, rootURI: rootURI });
- // If resolution found, store the resolution; otherwise,
- // skip storing it as runtime lookup will handle this
- if (!resolved)
- return agg;
- agg[mod] = resolved;
- return agg;
- }, {});
- let includes = keys(results[uri]);
- let count = 0;
- let subcallback = () => { if (++count >= includes.length) callback(results) };
- includes.map(id => {
- let moduleURI = results[uri][id];
- if (!results[moduleURI])
- findAllModuleIncludes(moduleURI, options, results, subcallback);
- else
- subcallback();
- });
- });
- }
- // From Substack's detector
- // https://github.com/substack/node-detective
- //
- // Given a resource URI or source, return an array of strings passed into
- // the require statements from the source
- function findModuleIncludes (uri, callback) {
- let src = isResourceURI(uri) ? readURI(uri) : uri;
- let modules = [];
- walk(src, function (node) {
- if (isRequire(node))
- modules.push(node.arguments[0].value);
- });
- callback(modules);
- }
- function walk (src, callback) {
- let nodes = Reflect.parse(src);
- traverse(nodes, callback);
- }
- function traverse (node, cb) {
- if (Array.isArray(node)) {
- node.map(x => {
- if (x != null) {
- x.parent = node;
- traverse(x, cb);
- }
- });
- }
- else if (node && typeof node === 'object') {
- cb(node);
- keys(node).map(key => {
- if (key === 'parent' || !node[key]) return;
- node[key].parent = node;
- traverse(node[key], cb);
- });
- }
- }
- // From Substack's detector
- // https://github.com/substack/node-detective
- // Check an AST node to see if its a require statement.
- // A modification added to only evaluate to true if it actually
- // has a value being passed in as an argument
- function isRequire (node) {
- var c = node.callee;
- return c
- && node.type === 'CallExpression'
- && c.type === 'Identifier'
- && c.name === 'require'
- && node.arguments.length
- && node.arguments[0].type === 'Literal';
- }
- });
|