addon-sdk/source/lib/toolkit/loader.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/toolkit/loader.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,918 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +;(function(id, factory) { // Module boilerplate :(
     1.9 +  if (typeof(define) === 'function') { // RequireJS
    1.10 +    define(factory);
    1.11 +  } else if (typeof(require) === 'function') { // CommonJS
    1.12 +    factory.call(this, require, exports, module);
    1.13 +  } else if (~String(this).indexOf('BackstagePass')) { // JSM
    1.14 +    this[factory.name] = {};
    1.15 +    factory(function require(uri) {
    1.16 +      var imports = {};
    1.17 +      this['Components'].utils.import(uri, imports);
    1.18 +      return imports;
    1.19 +    }, this[factory.name], { uri: __URI__, id: id });
    1.20 +    this.EXPORTED_SYMBOLS = [factory.name];
    1.21 +  } else if (~String(this).indexOf('Sandbox')) { // Sandbox
    1.22 +    factory(function require(uri) {}, this, { uri: __URI__, id: id });
    1.23 +  } else {  // Browser or alike
    1.24 +    var globals = this
    1.25 +    factory(function require(id) {
    1.26 +      return globals[id];
    1.27 +    }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
    1.28 +  }
    1.29 +}).call(this, 'loader', function Loader(require, exports, module) {
    1.30 +
    1.31 +'use strict';
    1.32 +
    1.33 +module.metadata = {
    1.34 +  "stability": "unstable"
    1.35 +};
    1.36 +
    1.37 +const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
    1.38 +        results: Cr, manager: Cm } = Components;
    1.39 +const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
    1.40 +const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1'].
    1.41 +                     getService(Ci.mozIJSSubScriptLoader);
    1.42 +const { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
    1.43 +                        getService(Ci.nsIObserverService);
    1.44 +const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
    1.45 +const { Reflect } = Cu.import("resource://gre/modules/reflect.jsm", {});
    1.46 +const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm");
    1.47 +const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm");
    1.48 +
    1.49 +// Define some shortcuts.
    1.50 +const bind = Function.call.bind(Function.bind);
    1.51 +const getOwnPropertyNames = Object.getOwnPropertyNames;
    1.52 +const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
    1.53 +const define = Object.defineProperties;
    1.54 +const prototypeOf = Object.getPrototypeOf;
    1.55 +const create = Object.create;
    1.56 +const keys = Object.keys;
    1.57 +
    1.58 +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"];
    1.59 +
    1.60 +const COMPONENT_ERROR = '`Components` is not available in this context.\n' +
    1.61 +  'Functionality provided by Components may be available in an SDK\n' +
    1.62 +  'module: https://jetpack.mozillalabs.com/sdk/latest/docs/ \n\n' +
    1.63 +  'However, if you still need to import Components, you may use the\n' +
    1.64 +  '`chrome` module\'s properties for shortcuts to Component properties:\n\n' +
    1.65 +  'Shortcuts: \n' +
    1.66 +  '    Cc = Components' + '.classes \n' +
    1.67 +  '    Ci = Components' + '.interfaces \n' +
    1.68 +  '    Cu = Components' + '.utils \n' +
    1.69 +  '    CC = Components' + '.Constructor \n' +
    1.70 +  'Example: \n' +
    1.71 +  '    let { Cc, Ci } = require(\'chrome\');\n';
    1.72 +
    1.73 +// Workaround for bug 674195. Freezing objects from other compartments fail,
    1.74 +// so we use `Object.freeze` from the same component instead.
    1.75 +function freeze(object) {
    1.76 +  if (prototypeOf(object) === null) {
    1.77 +      Object.freeze(object);
    1.78 +  }
    1.79 +  else {
    1.80 +    prototypeOf(prototypeOf(object.isPrototypeOf)).
    1.81 +      constructor. // `Object` from the owner compartment.
    1.82 +      freeze(object);
    1.83 +  }
    1.84 +  return object;
    1.85 +}
    1.86 +
    1.87 +// Returns map of given `object`-s own property descriptors.
    1.88 +const descriptor = iced(function descriptor(object) {
    1.89 +  let value = {};
    1.90 +  getOwnPropertyNames(object).forEach(function(name) {
    1.91 +    value[name] = getOwnPropertyDescriptor(object, name)
    1.92 +  });
    1.93 +  return value;
    1.94 +});
    1.95 +exports.descriptor = descriptor;
    1.96 +
    1.97 +// Freeze important built-ins so they can't be used by untrusted code as a
    1.98 +// message passing channel.
    1.99 +freeze(Object);
   1.100 +freeze(Object.prototype);
   1.101 +freeze(Function);
   1.102 +freeze(Function.prototype);
   1.103 +freeze(Array);
   1.104 +freeze(Array.prototype);
   1.105 +freeze(String);
   1.106 +freeze(String.prototype);
   1.107 +
   1.108 +// This function takes `f` function sets it's `prototype` to undefined and
   1.109 +// freezes it. We need to do this kind of deep freeze with all the exposed
   1.110 +// functions so that untrusted code won't be able to use them a message
   1.111 +// passing channel.
   1.112 +function iced(f) {
   1.113 +  if (!Object.isFrozen(f)) {
   1.114 +    f.prototype = undefined;
   1.115 +  }
   1.116 +  return freeze(f);
   1.117 +}
   1.118 +
   1.119 +// Defines own properties of given `properties` object on the given
   1.120 +// target object overriding any existing property with a conflicting name.
   1.121 +// Returns `target` object. Note we only export this function because it's
   1.122 +// useful during loader bootstrap when other util modules can't be used &
   1.123 +// thats only case where this export should be used.
   1.124 +const override = iced(function override(target, source) {
   1.125 +  let properties = descriptor(target)
   1.126 +  let extension = descriptor(source || {})
   1.127 +  getOwnPropertyNames(extension).forEach(function(name) {
   1.128 +    properties[name] = extension[name];
   1.129 +  });
   1.130 +  return define({}, properties);
   1.131 +});
   1.132 +exports.override = override;
   1.133 +
   1.134 +function sourceURI(uri) { return String(uri).split(" -> ").pop(); }
   1.135 +exports.sourceURI = iced(sourceURI);
   1.136 +
   1.137 +function isntLoaderFrame(frame) { return frame.fileName !== module.uri }
   1.138 +
   1.139 +function parseURI(uri) { return String(uri).split(" -> ").pop(); }
   1.140 +exports.parseURI = parseURI;
   1.141 +
   1.142 +function parseStack(stack) {
   1.143 +  let lines = String(stack).split("\n");
   1.144 +  return lines.reduce(function(frames, line) {
   1.145 +    if (line) {
   1.146 +      let atIndex = line.indexOf("@");
   1.147 +      let columnIndex = line.lastIndexOf(":");
   1.148 +      let lineIndex = line.lastIndexOf(":", columnIndex - 1);
   1.149 +      let fileName = parseURI(line.slice(atIndex + 1, lineIndex));
   1.150 +      let lineNumber = parseInt(line.slice(lineIndex + 1, columnIndex));
   1.151 +      let columnNumber = parseInt(line.slice(columnIndex + 1));
   1.152 +      let name = line.slice(0, atIndex).split("(").shift();
   1.153 +      frames.unshift({
   1.154 +        fileName: fileName,
   1.155 +        name: name,
   1.156 +        lineNumber: lineNumber,
   1.157 +        columnNumber: columnNumber
   1.158 +      });
   1.159 +    }
   1.160 +    return frames;
   1.161 +  }, []);
   1.162 +}
   1.163 +exports.parseStack = parseStack;
   1.164 +
   1.165 +function serializeStack(frames) {
   1.166 +  return frames.reduce(function(stack, frame) {
   1.167 +    return frame.name + "@" +
   1.168 +           frame.fileName + ":" +
   1.169 +           frame.lineNumber + ":" +
   1.170 +           frame.columnNumber + "\n" +
   1.171 +           stack;
   1.172 +  }, "");
   1.173 +}
   1.174 +exports.serializeStack = serializeStack;
   1.175 +
   1.176 +function readURI(uri) {
   1.177 +  let stream = NetUtil.newChannel(uri, 'UTF-8', null).open();
   1.178 +  let count = stream.available();
   1.179 +  let data = NetUtil.readInputStreamToString(stream, count, {
   1.180 +    charset: 'UTF-8'
   1.181 +  });
   1.182 +
   1.183 +  stream.close();
   1.184 +
   1.185 +  return data;
   1.186 +}
   1.187 +
   1.188 +// Combines all arguments into a resolved, normalized path
   1.189 +function join (...paths) {
   1.190 +  let resolved = normalize(pathJoin(...paths))
   1.191 +  // OS.File `normalize` strips out the second slash in
   1.192 +  // `resource://` or `chrome://`, and third slash in
   1.193 +  // `file:///`, so we work around this
   1.194 +  resolved = resolved.replace(/^resource\:\/([^\/])/, 'resource://$1');
   1.195 +  resolved = resolved.replace(/^file\:\/([^\/])/, 'file:///$1');
   1.196 +  resolved = resolved.replace(/^chrome\:\/([^\/])/, 'chrome://$1');
   1.197 +  return resolved;
   1.198 +}
   1.199 +exports.join = join;
   1.200 +
   1.201 +// Function takes set of options and returns a JS sandbox. Function may be
   1.202 +// passed set of options:
   1.203 +//  - `name`: A string value which identifies the sandbox in about:memory. Will
   1.204 +//    throw exception if omitted.
   1.205 +// - `principal`: String URI or `nsIPrincipal` for the sandbox. Defaults to
   1.206 +//    system principal.
   1.207 +// - `prototype`: Ancestor for the sandbox that will be created. Defaults to
   1.208 +//    `{}`.
   1.209 +// - `wantXrays`: A Boolean value indicating whether code outside the sandbox
   1.210 +//    wants X-ray vision with respect to objects inside the sandbox. Defaults
   1.211 +//    to `true`.
   1.212 +// - `sandbox`: A sandbox to share JS compartment with. If omitted new
   1.213 +//    compartment will be created.
   1.214 +// - `metadata`: A metadata object associated with the sandbox. It should
   1.215 +//    be JSON-serializable.
   1.216 +// For more details see:
   1.217 +// https://developer.mozilla.org/en/Components.utils.Sandbox
   1.218 +const Sandbox = iced(function Sandbox(options) {
   1.219 +  // Normalize options and rename to match `Cu.Sandbox` expectations.
   1.220 +  options = {
   1.221 +    // Do not expose `Components` if you really need them (bad idea!) you
   1.222 +    // still can expose via prototype.
   1.223 +    wantComponents: false,
   1.224 +    sandboxName: options.name,
   1.225 +    principal: 'principal' in options ? options.principal : systemPrincipal,
   1.226 +    wantXrays: 'wantXrays' in options ? options.wantXrays : true,
   1.227 +    wantGlobalProperties: 'wantGlobalProperties' in options ?
   1.228 +                          options.wantGlobalProperties : [],
   1.229 +    sandboxPrototype: 'prototype' in options ? options.prototype : {},
   1.230 +    invisibleToDebugger: 'invisibleToDebugger' in options ?
   1.231 +                         options.invisibleToDebugger : false,
   1.232 +    metadata: 'metadata' in options ? options.metadata : {}
   1.233 +  };
   1.234 +
   1.235 +  let sandbox = Cu.Sandbox(options.principal, options);
   1.236 +
   1.237 +  // Each sandbox at creation gets set of own properties that will be shadowing
   1.238 +  // ones from it's prototype. We override delete such `sandbox` properties
   1.239 +  // to avoid shadowing.
   1.240 +  delete sandbox.Iterator;
   1.241 +  delete sandbox.Components;
   1.242 +  delete sandbox.importFunction;
   1.243 +  delete sandbox.debug;
   1.244 +
   1.245 +  return sandbox;
   1.246 +});
   1.247 +exports.Sandbox = Sandbox;
   1.248 +
   1.249 +// Evaluates code from the given `uri` into given `sandbox`. If
   1.250 +// `options.source` is passed, then that code is evaluated instead.
   1.251 +// Optionally following options may be given:
   1.252 +// - `options.encoding`: Source encoding, defaults to 'UTF-8'.
   1.253 +// - `options.line`: Line number to start count from for stack traces.
   1.254 +//    Defaults to 1.
   1.255 +// - `options.version`: Version of JS used, defaults to '1.8'.
   1.256 +const evaluate = iced(function evaluate(sandbox, uri, options) {
   1.257 +  let { source, line, version, encoding } = override({
   1.258 +    encoding: 'UTF-8',
   1.259 +    line: 1,
   1.260 +    version: '1.8',
   1.261 +    source: null
   1.262 +  }, options);
   1.263 +
   1.264 +  return source ? Cu.evalInSandbox(source, sandbox, version, uri, line)
   1.265 +                : loadSubScript(uri, sandbox, encoding);
   1.266 +});
   1.267 +exports.evaluate = evaluate;
   1.268 +
   1.269 +// Populates `exports` of the given CommonJS `module` object, in the context
   1.270 +// of the given `loader` by evaluating code associated with it.
   1.271 +const load = iced(function load(loader, module) {
   1.272 +  let { sandboxes, globals } = loader;
   1.273 +  let require = Require(loader, module);
   1.274 +
   1.275 +  // We expose set of properties defined by `CommonJS` specification via
   1.276 +  // prototype of the sandbox. Also globals are deeper in the prototype
   1.277 +  // chain so that each module has access to them as well.
   1.278 +  let descriptors = descriptor({
   1.279 +    require: require,
   1.280 +    module: module,
   1.281 +    exports: module.exports,
   1.282 +    get Components() {
   1.283 +      // Expose `Components` property to throw error on usage with
   1.284 +      // additional information
   1.285 +      throw new ReferenceError(COMPONENT_ERROR);
   1.286 +    }
   1.287 +  });
   1.288 +
   1.289 +  let sandbox = sandboxes[module.uri] = Sandbox({
   1.290 +    name: module.uri,
   1.291 +    prototype: create(globals, descriptors),
   1.292 +    wantXrays: false,
   1.293 +    wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
   1.294 +    invisibleToDebugger: loader.invisibleToDebugger,
   1.295 +    metadata: {
   1.296 +      addonID: loader.id,
   1.297 +      URI: module.uri
   1.298 +    }
   1.299 +  });
   1.300 +
   1.301 +  try {
   1.302 +    evaluate(sandbox, module.uri);
   1.303 +  } catch (error) {
   1.304 +    let { message, fileName, lineNumber } = error;
   1.305 +    let stack = error.stack || Error().stack;
   1.306 +    let frames = parseStack(stack).filter(isntLoaderFrame);
   1.307 +    let toString = String(error);
   1.308 +    let file = sourceURI(fileName);
   1.309 +
   1.310 +    // Note that `String(error)` where error is from subscript loader does
   1.311 +    // not puts `:` after `"Error"` unlike regular errors thrown by JS code.
   1.312 +    // If there is a JS stack then this error has already been handled by an
   1.313 +    // inner module load.
   1.314 +    if (String(error) === "Error opening input stream (invalid filename?)") {
   1.315 +      let caller = frames.slice(0).pop();
   1.316 +      fileName = caller.fileName;
   1.317 +      lineNumber = caller.lineNumber;
   1.318 +      message = "Module `" + module.id + "` is not found at " + module.uri;
   1.319 +      toString = message;
   1.320 +    }
   1.321 +    // Workaround for a Bug 910653. Errors thrown by subscript loader
   1.322 +    // do not include `stack` field and above created error won't have
   1.323 +    // fileName or lineNumber of the module being loaded, so we ensure
   1.324 +    // it does.
   1.325 +    else if (frames[frames.length - 1].fileName !== file) {
   1.326 +      frames.push({ fileName: file, lineNumber: lineNumber, name: "" });
   1.327 +    }
   1.328 +
   1.329 +    let prototype = typeof(error) === "object" ? error.constructor.prototype :
   1.330 +                    Error.prototype;
   1.331 +
   1.332 +    throw create(prototype, {
   1.333 +      message: { value: message, writable: true, configurable: true },
   1.334 +      fileName: { value: fileName, writable: true, configurable: true },
   1.335 +      lineNumber: { value: lineNumber, writable: true, configurable: true },
   1.336 +      stack: { value: serializeStack(frames), writable: true, configurable: true },
   1.337 +      toString: { value: function() toString, writable: true, configurable: true },
   1.338 +    });
   1.339 +  }
   1.340 +
   1.341 +  if (module.exports && typeof(module.exports) === 'object')
   1.342 +    freeze(module.exports);
   1.343 +
   1.344 +  return module;
   1.345 +});
   1.346 +exports.load = load;
   1.347 +
   1.348 +// Utility function to normalize module `uri`s so they have `.js` extension.
   1.349 +function normalizeExt (uri) {
   1.350 +  return isJSURI(uri) ? uri :
   1.351 +         isJSONURI(uri) ? uri :
   1.352 +         isJSMURI(uri) ? uri :
   1.353 +         uri + '.js';
   1.354 +}
   1.355 +
   1.356 +// Strips `rootURI` from `string` -- used to remove absolute resourceURI
   1.357 +// from a relative path
   1.358 +function stripBase (rootURI, string) {
   1.359 +  return string.replace(rootURI, './');
   1.360 +}
   1.361 +
   1.362 +// Utility function to join paths. In common case `base` is a
   1.363 +// `requirer.uri` but in some cases it may be `baseURI`. In order to
   1.364 +// avoid complexity we require `baseURI` with a trailing `/`.
   1.365 +const resolve = iced(function resolve(id, base) {
   1.366 +  if (!isRelative(id)) return id;
   1.367 +  let basePaths = base.split('/');
   1.368 +  // Pop the last element in the `base`, because it is from a
   1.369 +  // relative file
   1.370 +  // '../mod.js' from '/path/to/file.js' should resolve to '/path/mod.js'
   1.371 +  basePaths.pop();
   1.372 +  if (!basePaths.length)
   1.373 +    return normalize(id);
   1.374 +  let resolved = join(basePaths.join('/'), id);
   1.375 +
   1.376 +  // Joining and normalizing removes the './' from relative files.
   1.377 +  // We need to ensure the resolution still has the root
   1.378 +  if (isRelative(base))
   1.379 +    resolved = './' + resolved;
   1.380 +
   1.381 +  return resolved;
   1.382 +});
   1.383 +exports.resolve = resolve;
   1.384 +
   1.385 +// Node-style module lookup
   1.386 +// Takes an id and path and attempts to load a file using node's resolving
   1.387 +// algorithm.
   1.388 +// `id` should already be resolved relatively at this point.
   1.389 +// http://nodejs.org/api/modules.html#modules_all_together
   1.390 +const nodeResolve = iced(function nodeResolve(id, requirer, { manifest, rootURI }) {
   1.391 +  // Resolve again
   1.392 +  id = exports.resolve(id, requirer);
   1.393 +
   1.394 +  // we assume that extensions are correct, i.e., a directory doesnt't have '.js'
   1.395 +  // and a js file isn't named 'file.json.js'
   1.396 +
   1.397 +  let fullId = join(rootURI, id);
   1.398 +
   1.399 +  let resolvedPath;
   1.400 +  if (resolvedPath = loadAsFile(fullId))
   1.401 +    return stripBase(rootURI, resolvedPath);
   1.402 +  else if (resolvedPath = loadAsDirectory(fullId))
   1.403 +    return stripBase(rootURI, resolvedPath);
   1.404 +  // If manifest has dependencies, attempt to look up node modules
   1.405 +  // in the `dependencies` list
   1.406 +  else if (manifest.dependencies) {
   1.407 +    let dirs = getNodeModulePaths(dirname(join(rootURI, requirer))).map(dir => join(dir, id));
   1.408 +    for (let i = 0; i < dirs.length; i++) {
   1.409 +      if (resolvedPath = loadAsFile(dirs[i]))
   1.410 +        return stripBase(rootURI, resolvedPath);
   1.411 +      if (resolvedPath = loadAsDirectory(dirs[i]))
   1.412 +        return stripBase(rootURI, resolvedPath);
   1.413 +    }
   1.414 +  }
   1.415 +
   1.416 +  // We would not find lookup for things like `sdk/tabs`, as that's part of
   1.417 +  // the alias mapping. If during `generateMap`, the runtime lookup resolves
   1.418 +  // with `resolveURI` -- if during runtime, then `resolve` will throw.
   1.419 +  return void 0;
   1.420 +});
   1.421 +exports.nodeResolve = nodeResolve;
   1.422 +
   1.423 +// Attempts to load `path` and then `path.js`
   1.424 +// Returns `path` with valid file, or `undefined` otherwise
   1.425 +function loadAsFile (path) {
   1.426 +  let found;
   1.427 +
   1.428 +  // As per node's loader spec,
   1.429 +  // we first should try and load 'path' (with no extension)
   1.430 +  // before trying 'path.js'. We will not support this feature
   1.431 +  // due to performance, but may add it if necessary for adoption.
   1.432 +  try {
   1.433 +    // Append '.js' to path name unless it's another support filetype
   1.434 +    path = normalizeExt(path);
   1.435 +    readURI(path);
   1.436 +    found = path;
   1.437 +  } catch (e) {}
   1.438 +
   1.439 +  return found;
   1.440 +}
   1.441 +
   1.442 +// Attempts to load `path/package.json`'s `main` entry,
   1.443 +// followed by `path/index.js`, or `undefined` otherwise
   1.444 +function loadAsDirectory (path) {
   1.445 +  let found;
   1.446 +  try {
   1.447 +    // If `path/package.json` exists, parse the `main` entry
   1.448 +    // and attempt to load that
   1.449 +    let main = getManifestMain(JSON.parse(readURI(path + '/package.json')));
   1.450 +    if (main != null) {
   1.451 +      let tmpPath = join(path, main);
   1.452 +      if (found = loadAsFile(tmpPath))
   1.453 +        return found
   1.454 +    }
   1.455 +    try {
   1.456 +      let tmpPath = path + '/index.js';
   1.457 +      readURI(tmpPath);
   1.458 +      return tmpPath;
   1.459 +    } catch (e) {}
   1.460 +  } catch (e) {
   1.461 +    try {
   1.462 +      let tmpPath = path + '/index.js';
   1.463 +      readURI(tmpPath);
   1.464 +      return tmpPath;
   1.465 +    } catch (e) {}
   1.466 +  }
   1.467 +  return void 0;
   1.468 +}
   1.469 +
   1.470 +// From `resolve` module
   1.471 +// https://github.com/substack/node-resolve/blob/master/lib/node-modules-paths.js
   1.472 +function getNodeModulePaths (start) {
   1.473 +  // Configurable in node -- do we need this to be configurable?
   1.474 +  let moduleDir = 'node_modules';
   1.475 +
   1.476 +  let parts = start.split('/');
   1.477 +  let dirs = [];
   1.478 +  for (let i = parts.length - 1; i >= 0; i--) {
   1.479 +    if (parts[i] === moduleDir) continue;
   1.480 +    let dir = join(parts.slice(0, i + 1).join('/'), moduleDir);
   1.481 +    dirs.push(dir);
   1.482 +  }
   1.483 +  return dirs;
   1.484 +}
   1.485 +
   1.486 +
   1.487 +function addTrailingSlash (path) {
   1.488 +  return !path ? null : !path.endsWith('/') ? path + '/' : path;
   1.489 +}
   1.490 +
   1.491 +// Utility function to determine of module id `name` is a built in
   1.492 +// module in node (fs, path, etc.);
   1.493 +function isNodeModule (name) {
   1.494 +  return !!~NODE_MODULES.indexOf(name);
   1.495 +}
   1.496 +
   1.497 +// Make mapping array that is sorted from longest path to shortest path
   1.498 +// to allow overlays. Used by `resolveURI`, returns an array
   1.499 +function sortPaths (paths) {
   1.500 +  return keys(paths).
   1.501 +    sort(function(a, b) { return b.length - a.length }).
   1.502 +    map(function(path) { return [ path, paths[path] ] });
   1.503 +}
   1.504 +
   1.505 +const resolveURI = iced(function resolveURI(id, mapping) {
   1.506 +  let count = mapping.length, index = 0;
   1.507 +
   1.508 +  // Do not resolve if already a resource URI
   1.509 +  if (isResourceURI(id)) return normalizeExt(id);
   1.510 +
   1.511 +  while (index < count) {
   1.512 +    let [ path, uri ] = mapping[index ++];
   1.513 +    if (id.indexOf(path) === 0)
   1.514 +      return normalizeExt(id.replace(path, uri));
   1.515 +  }
   1.516 +  return void 0; // otherwise we raise a warning, see bug 910304
   1.517 +});
   1.518 +exports.resolveURI = resolveURI;
   1.519 +
   1.520 +// Creates version of `require` that will be exposed to the given `module`
   1.521 +// in the context of the given `loader`. Each module gets own limited copy
   1.522 +// of `require` that is allowed to load only a modules that are associated
   1.523 +// with it during link time.
   1.524 +const Require = iced(function Require(loader, requirer) {
   1.525 +  let {
   1.526 +    modules, mapping, resolve, load, manifest, rootURI, isNative, requireMap
   1.527 +  } = loader;
   1.528 +
   1.529 +  function require(id) {
   1.530 +    if (!id) // Throw if `id` is not passed.
   1.531 +      throw Error('you must provide a module name when calling require() from '
   1.532 +                  + requirer.id, requirer.uri);
   1.533 +
   1.534 +    let requirement;
   1.535 +    let uri;
   1.536 +
   1.537 +    // TODO should get native Firefox modules before doing node-style lookups
   1.538 +    // to save on loading time
   1.539 +
   1.540 +    if (isNative) {
   1.541 +      // If a requireMap is available from `generateMap`, use that to
   1.542 +      // immediately resolve the node-style mapping.
   1.543 +      if (requireMap && requireMap[requirer.id])
   1.544 +        requirement = requireMap[requirer.id][id];
   1.545 +
   1.546 +      // For native modules, we want to check if it's a module specified
   1.547 +      // in 'modules', like `chrome`, or `@loader` -- if it exists,
   1.548 +      // just set the uri to skip resolution
   1.549 +      if (!requirement && modules[id])
   1.550 +        uri = requirement = id;
   1.551 +
   1.552 +      // If no requireMap was provided, or resolution not found in
   1.553 +      // the requireMap, and not a npm dependency, attempt a runtime lookup
   1.554 +      if (!requirement && !isNodeModule(id)) {
   1.555 +        // If `isNative` defined, this is using the new, native-style
   1.556 +        // loader, not cuddlefish, so lets resolve using node's algorithm
   1.557 +        // and get back a path that needs to be resolved via paths mapping
   1.558 +        // in `resolveURI`
   1.559 +        requirement = resolve(id, requirer.id, {
   1.560 +          manifest: manifest,
   1.561 +          rootURI: rootURI
   1.562 +        });
   1.563 +      }
   1.564 +
   1.565 +      // If not found in the map, not a node module, and wasn't able to be
   1.566 +      // looked up, it's something
   1.567 +      // found in the paths most likely, like `sdk/tabs`, which should
   1.568 +      // be resolved relatively if needed using traditional resolve
   1.569 +      if (!requirement) {
   1.570 +        requirement = isRelative(id) ? exports.resolve(id, requirer.id) : id;
   1.571 +      }
   1.572 +    } else {
   1.573 +      // Resolve `id` to its requirer if it's relative.
   1.574 +      requirement = requirer ? resolve(id, requirer.id) : id;
   1.575 +    }
   1.576 +
   1.577 +    // Resolves `uri` of module using loaders resolve function.
   1.578 +    uri = uri || resolveURI(requirement, mapping);
   1.579 +
   1.580 +    if (!uri) // Throw if `uri` can not be resolved.
   1.581 +      throw Error('Module: Can not resolve "' + id + '" module required by ' +
   1.582 +                  requirer.id + ' located at ' + requirer.uri, requirer.uri);
   1.583 +
   1.584 +    let module = null;
   1.585 +    // If module is already cached by loader then just use it.
   1.586 +    if (uri in modules) {
   1.587 +      module = modules[uri];
   1.588 +    }
   1.589 +    else if (isJSMURI(uri)) {
   1.590 +      module = modules[uri] = Module(requirement, uri);
   1.591 +      module.exports = Cu.import(uri, {});
   1.592 +      freeze(module);
   1.593 +    }
   1.594 +    else if (isJSONURI(uri)) {
   1.595 +      let data;
   1.596 +
   1.597 +      // First attempt to load and parse json uri
   1.598 +      // ex: `test.json`
   1.599 +      // If that doesn't exist, check for `test.json.js`
   1.600 +      // for node parity
   1.601 +      try {
   1.602 +        data = JSON.parse(readURI(uri));
   1.603 +        module = modules[uri] = Module(requirement, uri);
   1.604 +        module.exports = data;
   1.605 +        freeze(module);
   1.606 +      }
   1.607 +      catch (err) {
   1.608 +        // If error thrown from JSON parsing, throw that, do not
   1.609 +        // attempt to find .json.js file
   1.610 +        if (err && /JSON\.parse/.test(err.message))
   1.611 +          throw err;
   1.612 +        uri = uri + '.js';
   1.613 +      }
   1.614 +    }
   1.615 +    // If not yet cached, load and cache it.
   1.616 +    // We also freeze module to prevent it from further changes
   1.617 +    // at runtime.
   1.618 +    if (!(uri in modules)) {
   1.619 +      // Many of the loader's functionalities are dependent
   1.620 +      // on modules[uri] being set before loading, so we set it and
   1.621 +      // remove it if we have any errors.
   1.622 +      module = modules[uri] = Module(requirement, uri);
   1.623 +      try {
   1.624 +        freeze(load(loader, module));
   1.625 +      }
   1.626 +      catch (e) {
   1.627 +        // Clear out modules cache so we can throw on a second invalid require
   1.628 +        delete modules[uri];
   1.629 +        // Also clear out the Sandbox that was created
   1.630 +        delete loader.sandboxes[uri];
   1.631 +        throw e;
   1.632 +      }
   1.633 +    }
   1.634 +
   1.635 +    return module.exports;
   1.636 +  }
   1.637 +  // Make `require.main === module` evaluate to true in main module scope.
   1.638 +  require.main = loader.main === requirer ? requirer : undefined;
   1.639 +  return iced(require);
   1.640 +});
   1.641 +exports.Require = Require;
   1.642 +
   1.643 +const main = iced(function main(loader, id) {
   1.644 +  // If no main entry provided, and native loader is used,
   1.645 +  // read the entry in the manifest
   1.646 +  if (!id && loader.isNative)
   1.647 +    id = getManifestMain(loader.manifest);
   1.648 +  let uri = resolveURI(id, loader.mapping);
   1.649 +  let module = loader.main = loader.modules[uri] = Module(id, uri);
   1.650 +  return loader.load(loader, module).exports;
   1.651 +});
   1.652 +exports.main = main;
   1.653 +
   1.654 +// Makes module object that is made available to CommonJS modules when they
   1.655 +// are evaluated, along with `exports` and `require`.
   1.656 +const Module = iced(function Module(id, uri) {
   1.657 +  return create(null, {
   1.658 +    id: { enumerable: true, value: id },
   1.659 +    exports: { enumerable: true, writable: true, value: create(null) },
   1.660 +    uri: { value: uri }
   1.661 +  });
   1.662 +});
   1.663 +exports.Module = Module;
   1.664 +
   1.665 +// Takes `loader`, and unload `reason` string and notifies all observers that
   1.666 +// they should cleanup after them-self.
   1.667 +const unload = iced(function unload(loader, reason) {
   1.668 +  // subject is a unique object created per loader instance.
   1.669 +  // This allows any code to cleanup on loader unload regardless of how
   1.670 +  // it was loaded. To handle unload for specific loader subject may be
   1.671 +  // asserted against loader.destructor or require('@loader/unload')
   1.672 +  // Note: We don not destroy loader's module cache or sandboxes map as
   1.673 +  // some modules may do cleanup in subsequent turns of event loop. Destroying
   1.674 +  // cache may cause module identity problems in such cases.
   1.675 +  let subject = { wrappedJSObject: loader.destructor };
   1.676 +  notifyObservers(subject, 'sdk:loader:destroy', reason);
   1.677 +});
   1.678 +exports.unload = unload;
   1.679 +
   1.680 +// Function makes new loader that can be used to load CommonJS modules
   1.681 +// described by a given `options.manifest`. Loader takes following options:
   1.682 +// - `globals`: Optional map of globals, that all module scopes will inherit
   1.683 +//   from. Map is also exposed under `globals` property of the returned loader
   1.684 +//   so it can be extended further later. Defaults to `{}`.
   1.685 +// - `modules` Optional map of built-in module exports mapped by module id.
   1.686 +//   These modules will incorporated into module cache. Each module will be
   1.687 +//   frozen.
   1.688 +// - `resolve` Optional module `id` resolution function. If given it will be
   1.689 +//   used to resolve module URIs, by calling it with require term, requirer
   1.690 +//   module object (that has `uri` property) and `baseURI` of the loader.
   1.691 +//   If `resolve` does not returns `uri` string exception will be thrown by
   1.692 +//   an associated `require` call.
   1.693 +const Loader = iced(function Loader(options) {
   1.694 +  let {
   1.695 +    modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative
   1.696 +  } = override({
   1.697 +    paths: {},
   1.698 +    modules: {},
   1.699 +    globals: {
   1.700 +      console: console
   1.701 +    },
   1.702 +    resolve: options.isNative ?
   1.703 +      exports.nodeResolve :
   1.704 +      exports.resolve,
   1.705 +  }, options);
   1.706 +
   1.707 +  // We create an identity object that will be dispatched on an unload
   1.708 +  // event as subject. This way unload listeners will be able to assert
   1.709 +  // which loader is unloaded. Please note that we intentionally don't
   1.710 +  // use `loader` as subject to prevent a loader access leakage through
   1.711 +  // observer notifications.
   1.712 +  let destructor = freeze(create(null));
   1.713 +
   1.714 +  let mapping = sortPaths(paths);
   1.715 +
   1.716 +  // Define pseudo modules.
   1.717 +  modules = override({
   1.718 +    '@loader/unload': destructor,
   1.719 +    '@loader/options': options,
   1.720 +    'chrome': { Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
   1.721 +                CC: bind(CC, Components), components: Components,
   1.722 +                // `ChromeWorker` has to be inject in loader global scope.
   1.723 +                // It is done by bootstrap.js:loadSandbox for the SDK.
   1.724 +                ChromeWorker: ChromeWorker
   1.725 +    }
   1.726 +  }, modules);
   1.727 +
   1.728 +  modules = keys(modules).reduce(function(result, id) {
   1.729 +    // We resolve `uri` from `id` since modules are cached by `uri`.
   1.730 +    let uri = resolveURI(id, mapping);
   1.731 +    // In native loader, the mapping will not contain values for
   1.732 +    // pseudomodules -- store them as their ID rather than the URI
   1.733 +    if (isNative && !uri)
   1.734 +      uri = id;
   1.735 +    let module = Module(id, uri);
   1.736 +    module.exports = freeze(modules[id]);
   1.737 +    result[uri] = freeze(module);
   1.738 +    return result;
   1.739 +  }, {});
   1.740 +
   1.741 +  // Loader object is just a representation of a environment
   1.742 +  // state. We freeze it and mark make it's properties non-enumerable
   1.743 +  // as they are pure implementation detail that no one should rely upon.
   1.744 +  let returnObj = {
   1.745 +    destructor: { enumerable: false, value: destructor },
   1.746 +    globals: { enumerable: false, value: globals },
   1.747 +    mapping: { enumerable: false, value: mapping },
   1.748 +    // Map of module objects indexed by module URIs.
   1.749 +    modules: { enumerable: false, value: modules },
   1.750 +    // Map of module sandboxes indexed by module URIs.
   1.751 +    sandboxes: { enumerable: false, value: {} },
   1.752 +    resolve: { enumerable: false, value: resolve },
   1.753 +    // ID of the addon, if provided.
   1.754 +    id: { enumerable: false, value: options.id },
   1.755 +    // Whether the modules loaded should be ignored by the debugger
   1.756 +    invisibleToDebugger: { enumerable: false,
   1.757 +                           value: options.invisibleToDebugger || false },
   1.758 +    load: { enumerable: false, value: options.load || load },
   1.759 +    // Main (entry point) module, it can be set only once, since loader
   1.760 +    // instance can have only one main module.
   1.761 +    main: new function() {
   1.762 +      let main;
   1.763 +      return {
   1.764 +        enumerable: false,
   1.765 +        get: function() { return main; },
   1.766 +        // Only set main if it has not being set yet!
   1.767 +        set: function(module) { main = main || module; }
   1.768 +      }
   1.769 +    }
   1.770 +  };
   1.771 +
   1.772 +  if (isNative) {
   1.773 +    returnObj.isNative = { enumerable: false, value: true };
   1.774 +    returnObj.manifest = { enumerable: false, value: manifest };
   1.775 +    returnObj.requireMap = { enumerable: false, value: requireMap };
   1.776 +    returnObj.rootURI = { enumerable: false, value: addTrailingSlash(rootURI) };
   1.777 +  }
   1.778 +
   1.779 +  return freeze(create(null, returnObj));
   1.780 +});
   1.781 +exports.Loader = Loader;
   1.782 +
   1.783 +let isJSONURI = uri => uri.substr(-5) === '.json';
   1.784 +let isJSMURI = uri => uri.substr(-4) === '.jsm';
   1.785 +let isJSURI = uri => uri.substr(-3) === '.js';
   1.786 +let isResourceURI = uri => uri.substr(0, 11) === 'resource://';
   1.787 +let isRelative = id => id[0] === '.'
   1.788 +
   1.789 +const generateMap = iced(function generateMap(options, callback) {
   1.790 +  let { rootURI, resolve, paths } = override({
   1.791 +    paths: {},
   1.792 +    resolve: exports.nodeResolve
   1.793 +  }, options);
   1.794 +
   1.795 +  rootURI = addTrailingSlash(rootURI);
   1.796 +
   1.797 +  let manifest;
   1.798 +  let manifestURI = join(rootURI, 'package.json');
   1.799 +
   1.800 +  if (rootURI)
   1.801 +    manifest = JSON.parse(readURI(manifestURI));
   1.802 +  else
   1.803 +    throw new Error('No `rootURI` given to generate map');
   1.804 +
   1.805 +  let main = getManifestMain(manifest);
   1.806 +
   1.807 +  findAllModuleIncludes(main, {
   1.808 +    resolve: resolve,
   1.809 +    manifest: manifest,
   1.810 +    rootURI: rootURI
   1.811 +  }, {}, callback);
   1.812 +
   1.813 +});
   1.814 +exports.generateMap = generateMap;
   1.815 +
   1.816 +// Default `main` entry to './index.js' and ensure is relative,
   1.817 +// since node allows 'lib/index.js' without relative `./`
   1.818 +function getManifestMain (manifest) {
   1.819 +  let main = manifest.main || './index.js';
   1.820 +  return isRelative(main) ? main : './' + main;
   1.821 +}
   1.822 +
   1.823 +function findAllModuleIncludes (uri, options, results, callback) {
   1.824 +  let { resolve, manifest, rootURI } = options;
   1.825 +  results = results || {};
   1.826 +
   1.827 +  // Abort if JSON or JSM
   1.828 +  if (isJSONURI(uri) || isJSMURI(uri)) {
   1.829 +    callback(results);
   1.830 +    return void 0;
   1.831 +  }
   1.832 +
   1.833 +  findModuleIncludes(join(rootURI, uri), modules => {
   1.834 +    // If no modules are included in the file, just call callback immediately
   1.835 +    if (!modules.length) {
   1.836 +      callback(results);
   1.837 +      return void 0;
   1.838 +    }
   1.839 +
   1.840 +    results[uri] = modules.reduce((agg, mod) => {
   1.841 +      let resolved = resolve(mod, uri, { manifest: manifest, rootURI: rootURI });
   1.842 +
   1.843 +      // If resolution found, store the resolution; otherwise,
   1.844 +      // skip storing it as runtime lookup will handle this
   1.845 +      if (!resolved)
   1.846 +        return agg;
   1.847 +      agg[mod] = resolved;
   1.848 +      return agg;
   1.849 +    }, {});
   1.850 +
   1.851 +    let includes = keys(results[uri]);
   1.852 +    let count = 0;
   1.853 +    let subcallback = () => { if (++count >= includes.length) callback(results) };
   1.854 +    includes.map(id => {
   1.855 +      let moduleURI = results[uri][id];
   1.856 +      if (!results[moduleURI])
   1.857 +        findAllModuleIncludes(moduleURI, options, results, subcallback);
   1.858 +      else
   1.859 +        subcallback();
   1.860 +    });
   1.861 +  });
   1.862 +}
   1.863 +
   1.864 +// From Substack's detector
   1.865 +// https://github.com/substack/node-detective
   1.866 +//
   1.867 +// Given a resource URI or source, return an array of strings passed into
   1.868 +// the require statements from the source
   1.869 +function findModuleIncludes (uri, callback) {
   1.870 +  let src = isResourceURI(uri) ? readURI(uri) : uri;
   1.871 +  let modules = [];
   1.872 +
   1.873 +  walk(src, function (node) {
   1.874 +    if (isRequire(node))
   1.875 +      modules.push(node.arguments[0].value);
   1.876 +  });
   1.877 +
   1.878 +  callback(modules);
   1.879 +}
   1.880 +
   1.881 +function walk (src, callback) {
   1.882 +  let nodes = Reflect.parse(src);
   1.883 +  traverse(nodes, callback);
   1.884 +}
   1.885 +
   1.886 +function traverse (node, cb) {
   1.887 +  if (Array.isArray(node)) {
   1.888 +    node.map(x => {
   1.889 +      if (x != null) {
   1.890 +        x.parent = node;
   1.891 +        traverse(x, cb);
   1.892 +      }
   1.893 +    });
   1.894 +  }
   1.895 +  else if (node && typeof node === 'object') {
   1.896 +    cb(node);
   1.897 +    keys(node).map(key => {
   1.898 +      if (key === 'parent' || !node[key]) return;
   1.899 +      node[key].parent = node;
   1.900 +      traverse(node[key], cb);
   1.901 +    });
   1.902 +  }
   1.903 +}
   1.904 +
   1.905 +// From Substack's detector
   1.906 +// https://github.com/substack/node-detective
   1.907 +// Check an AST node to see if its a require statement.
   1.908 +// A modification added to only evaluate to true if it actually
   1.909 +// has a value being passed in as an argument
   1.910 +function isRequire (node) {
   1.911 +  var c = node.callee;
   1.912 +  return c
   1.913 +    && node.type === 'CallExpression'
   1.914 +    && c.type === 'Identifier'
   1.915 +    && c.name === 'require'
   1.916 +    && node.arguments.length
   1.917 +   && node.arguments[0].type === 'Literal';
   1.918 +}
   1.919 +
   1.920 +});
   1.921 +

mercurial