1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/workerloader/require.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,243 @@ 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 + 1.9 +/** 1.10 + * Implementation of a CommonJS module loader for workers. 1.11 + * 1.12 + * Use: 1.13 + * // in the .js file loaded by the constructor of the worker 1.14 + * importScripts("resource://gre/modules/workers/require.js"); 1.15 + * let module = require("resource://gre/modules/worker/myModule.js"); 1.16 + * 1.17 + * // in myModule.js 1.18 + * // Load dependencies 1.19 + * let SimpleTest = require("resource://gre/modules/workers/SimpleTest.js"); 1.20 + * let Logger = require("resource://gre/modules/workers/Logger.js"); 1.21 + * 1.22 + * // Define things that will not be exported 1.23 + * let someValue = // ... 1.24 + * 1.25 + * // Export symbols 1.26 + * exports.foo = // ... 1.27 + * exports.bar = // ... 1.28 + * 1.29 + * 1.30 + * Note #1: 1.31 + * Properties |fileName| and |stack| of errors triggered from a module 1.32 + * contain file names that do not correspond to human-readable module paths. 1.33 + * Human readers should rather use properties |moduleName| and |moduleStack|. 1.34 + * 1.35 + * Note #2: 1.36 + * The current version of |require()| only accepts absolute URIs. 1.37 + * 1.38 + * Note #3: 1.39 + * By opposition to some other module loader implementations, this module 1.40 + * loader does not enforce separation of global objects. Consequently, if 1.41 + * a module modifies a global object (e.g. |String.prototype|), all other 1.42 + * modules in the same worker may be affected. 1.43 + */ 1.44 + 1.45 + 1.46 +(function(exports) { 1.47 + "use strict"; 1.48 + 1.49 + if (exports.require) { 1.50 + // Avoid double-imports 1.51 + return; 1.52 + } 1.53 + 1.54 + // Simple implementation of |require| 1.55 + let require = (function() { 1.56 + 1.57 + /** 1.58 + * Mapping from module paths to module exports. 1.59 + * 1.60 + * @keys {string} The absolute path to a module. 1.61 + * @values {object} The |exports| objects for that module. 1.62 + */ 1.63 + let modules = new Map(); 1.64 + 1.65 + /** 1.66 + * Mapping from object urls to module paths. 1.67 + */ 1.68 + let paths = { 1.69 + /** 1.70 + * @keys {string} The object url holding a module. 1.71 + * @values {string} The absolute path to that module. 1.72 + */ 1.73 + _map: new Map(), 1.74 + /** 1.75 + * A regexp that may be used to search for all mapped paths. 1.76 + */ 1.77 + get regexp() { 1.78 + if (this._regexp) { 1.79 + return this._regexp; 1.80 + } 1.81 + let objectURLs = []; 1.82 + for (let [objectURL, _] of this._map) { 1.83 + objectURLs.push(objectURL); 1.84 + } 1.85 + return this._regexp = new RegExp(objectURLs.join("|"), "g"); 1.86 + }, 1.87 + _regexp: null, 1.88 + /** 1.89 + * Add a mapping from an object url to a path. 1.90 + */ 1.91 + set: function(url, path) { 1.92 + this._regexp = null; // invalidate regexp 1.93 + this._map.set(url, path); 1.94 + }, 1.95 + /** 1.96 + * Get a mapping from an object url to a path. 1.97 + */ 1.98 + get: function(url) { 1.99 + return this._map.get(url); 1.100 + }, 1.101 + /** 1.102 + * Transform a string by replacing all the instances of objectURLs 1.103 + * appearing in that string with the corresponding module path. 1.104 + * 1.105 + * This is used typically to translate exception stacks. 1.106 + * 1.107 + * @param {string} source A source string. 1.108 + * @return {string} The same string as |source|, in which every occurrence 1.109 + * of an objectURL registered in this object has been replaced with the 1.110 + * corresponding module path. 1.111 + */ 1.112 + substitute: function(source) { 1.113 + let map = this._map; 1.114 + return source.replace(this.regexp, function(url) { 1.115 + return map.get(url) || url; 1.116 + }, "g"); 1.117 + } 1.118 + }; 1.119 + 1.120 + /** 1.121 + * A human-readable version of |stack|. 1.122 + * 1.123 + * @type {string} 1.124 + */ 1.125 + Object.defineProperty(Error.prototype, "moduleStack", 1.126 + { 1.127 + get: function() { 1.128 + return paths.substitute(this.stack); 1.129 + } 1.130 + }); 1.131 + /** 1.132 + * A human-readable version of |fileName|. 1.133 + * 1.134 + * @type {string} 1.135 + */ 1.136 + Object.defineProperty(Error.prototype, "moduleName", 1.137 + { 1.138 + get: function() { 1.139 + return paths.substitute(this.fileName); 1.140 + } 1.141 + }); 1.142 + 1.143 + /** 1.144 + * Import a module 1.145 + * 1.146 + * @param {string} path The path to the module. 1.147 + * @return {*} An object containing the properties exported by the module. 1.148 + */ 1.149 + return function require(path) { 1.150 + if (typeof path != "string" || path.indexOf("://") == -1) { 1.151 + throw new TypeError("The argument to require() must be a string uri, got " + path); 1.152 + } 1.153 + // Automatically add ".js" if there is no extension 1.154 + let uri; 1.155 + if (path.lastIndexOf(".") <= path.lastIndexOf("/")) { 1.156 + uri = path + ".js"; 1.157 + } else { 1.158 + uri = path; 1.159 + } 1.160 + 1.161 + // Exports provided by the module 1.162 + let exports = Object.create(null); 1.163 + 1.164 + // Identification of the module 1.165 + let module = { 1.166 + id: path, 1.167 + uri: uri, 1.168 + exports: exports 1.169 + }; 1.170 + 1.171 + // Make module available immediately 1.172 + // (necessary in case of circular dependencies) 1.173 + if (modules.has(path)) { 1.174 + return modules.get(path).exports; 1.175 + } 1.176 + modules.set(path, module); 1.177 + 1.178 + let name = ":" + path; 1.179 + let objectURL; 1.180 + try { 1.181 + // Load source of module, synchronously 1.182 + let xhr = new XMLHttpRequest(); 1.183 + xhr.open("GET", uri, false); 1.184 + xhr.responseType = "text"; 1.185 + xhr.send(); 1.186 + 1.187 + 1.188 + let source = xhr.responseText; 1.189 + if (source == "") { 1.190 + // There doesn't seem to be a better way to detect that the file couldn't be found 1.191 + throw new Error("Could not find module " + path); 1.192 + } 1.193 + // From the source, build a function and an object URL. We 1.194 + // avoid any newline at the start of the file to ensure that 1.195 + // we do not mess up with line numbers. However, using object URLs 1.196 + // messes up with stack traces in instances of Error(). 1.197 + source = "require._tmpModules[\"" + name + "\"] = " + 1.198 + "function(exports, require, module) {" + 1.199 + source + 1.200 + "\n}\n"; 1.201 + let blob = new Blob( 1.202 + [ 1.203 + (new TextEncoder()).encode(source) 1.204 + ], { 1.205 + type: "application/javascript" 1.206 + }); 1.207 + objectURL = URL.createObjectURL(blob); 1.208 + paths.set(objectURL, path); 1.209 + importScripts(objectURL); 1.210 + require._tmpModules[name].call(null, exports, require, module); 1.211 + 1.212 + } catch (ex) { 1.213 + // Module loading has failed, exports should not be made available 1.214 + // after all. 1.215 + modules.delete(path); 1.216 + throw ex; 1.217 + } finally { 1.218 + if (objectURL) { 1.219 + // Clean up the object url as soon as possible. It will not be needed. 1.220 + URL.revokeObjectURL(objectURL); 1.221 + } 1.222 + delete require._tmpModules[name]; 1.223 + } 1.224 + 1.225 + Object.freeze(module.exports); 1.226 + Object.freeze(module); 1.227 + return module.exports; 1.228 + }; 1.229 + })(); 1.230 + 1.231 + /** 1.232 + * An object used to hold temporarily the module constructors 1.233 + * while they are being loaded. 1.234 + * 1.235 + * @keys {string} The path to the module, prefixed with ":". 1.236 + * @values {function} A function wrapping the module. 1.237 + */ 1.238 + require._tmpModules = Object.create(null); 1.239 + Object.freeze(require); 1.240 + 1.241 + Object.defineProperty(exports, "require", { 1.242 + value: require, 1.243 + enumerable: true, 1.244 + configurable: false 1.245 + }); 1.246 +})(this);