services/sync/tps/extensions/mozmill/resource/stdlib/securable-module.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

michael@0 1 /* ***** BEGIN LICENSE BLOCK *****
michael@0 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
michael@0 3 *
michael@0 4 * The contents of this file are subject to the Mozilla Public License Version
michael@0 5 * 1.1 (the "License"); you may not use this file except in compliance with
michael@0 6 * the License. You may obtain a copy of the License at
michael@0 7 * http://www.mozilla.org/MPL/
michael@0 8 *
michael@0 9 * Software distributed under the License is distributed on an "AS IS" basis,
michael@0 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
michael@0 11 * for the specific language governing rights and limitations under the
michael@0 12 * License.
michael@0 13 *
michael@0 14 * The Original Code is Jetpack.
michael@0 15 *
michael@0 16 * The Initial Developer of the Original Code is Mozilla.
michael@0 17 * Portions created by the Initial Developer are Copyright (C) 2007
michael@0 18 * the Initial Developer. All Rights Reserved.
michael@0 19 *
michael@0 20 * Contributor(s):
michael@0 21 * Atul Varma <atul@mozilla.com>
michael@0 22 *
michael@0 23 * Alternatively, the contents of this file may be used under the terms of
michael@0 24 * either the GNU General Public License Version 2 or later (the "GPL"), or
michael@0 25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
michael@0 26 * in which case the provisions of the GPL or the LGPL are applicable instead
michael@0 27 * of those above. If you wish to allow use of your version of this file only
michael@0 28 * under the terms of either the GPL or the LGPL, and not to allow others to
michael@0 29 * use your version of this file under the terms of the MPL, indicate your
michael@0 30 * decision by deleting the provisions above and replace them with the notice
michael@0 31 * and other provisions required by the GPL or the LGPL. If you do not delete
michael@0 32 * the provisions above, a recipient may use your version of this file under
michael@0 33 * the terms of any one of the MPL, the GPL or the LGPL.
michael@0 34 *
michael@0 35 * ***** END LICENSE BLOCK ***** */
michael@0 36
michael@0 37 (function(global) {
michael@0 38 const Cc = Components.classes;
michael@0 39 const Ci = Components.interfaces;
michael@0 40 const Cu = Components.utils;
michael@0 41 const Cr = Components.results;
michael@0 42
michael@0 43 var exports = {};
michael@0 44
michael@0 45 var ios = Cc['@mozilla.org/network/io-service;1']
michael@0 46 .getService(Ci.nsIIOService);
michael@0 47
michael@0 48 var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
michael@0 49 .createInstance(Ci.nsIPrincipal);
michael@0 50
michael@0 51 function resolvePrincipal(principal, defaultPrincipal) {
michael@0 52 if (principal === undefined)
michael@0 53 return defaultPrincipal;
michael@0 54 if (principal == "system")
michael@0 55 return systemPrincipal;
michael@0 56 return principal;
michael@0 57 }
michael@0 58
michael@0 59 // The base URI to we use when we're given relative URLs, if any.
michael@0 60 var baseURI = null;
michael@0 61 if (global.window)
michael@0 62 baseURI = ios.newURI(global.location.href, null, null);
michael@0 63 exports.baseURI = baseURI;
michael@0 64
michael@0 65 // The "parent" chrome URI to use if we're loading code that
michael@0 66 // needs chrome privileges but may not have a filename that
michael@0 67 // matches any of SpiderMonkey's defined system filename prefixes.
michael@0 68 // The latter is needed so that wrappers can be automatically
michael@0 69 // made for the code. For more information on this, see
michael@0 70 // bug 418356:
michael@0 71 //
michael@0 72 // https://bugzilla.mozilla.org/show_bug.cgi?id=418356
michael@0 73 var parentChromeURIString;
michael@0 74 if (baseURI)
michael@0 75 // We're being loaded from a chrome-privileged document, so
michael@0 76 // use its URL as the parent string.
michael@0 77 parentChromeURIString = baseURI.spec;
michael@0 78 else
michael@0 79 // We're being loaded from a chrome-privileged JS module or
michael@0 80 // SecurableModule, so use its filename (which may itself
michael@0 81 // contain a reference to a parent).
michael@0 82 parentChromeURIString = Components.stack.filename;
michael@0 83
michael@0 84 function maybeParentifyFilename(filename) {
michael@0 85 var doParentifyFilename = true;
michael@0 86 try {
michael@0 87 // TODO: Ideally we should just make
michael@0 88 // nsIChromeRegistry.wrappersEnabled() available from script
michael@0 89 // and use it here. Until that's in the platform, though,
michael@0 90 // we'll play it safe and parentify the filename unless
michael@0 91 // we're absolutely certain things will be ok if we don't.
michael@0 92 var filenameURI = ios.newURI(options.filename,
michael@0 93 null,
michael@0 94 baseURI);
michael@0 95 if (filenameURI.scheme == 'chrome' &&
michael@0 96 filenameURI.path.indexOf('/content/') == 0)
michael@0 97 // Content packages will always have wrappers made for them;
michael@0 98 // if automatic wrappers have been disabled for the
michael@0 99 // chrome package via a chrome manifest flag, then
michael@0 100 // this still works too, to the extent that the
michael@0 101 // content package is insecure anyways.
michael@0 102 doParentifyFilename = false;
michael@0 103 } catch (e) {}
michael@0 104 if (doParentifyFilename)
michael@0 105 return parentChromeURIString + " -> " + filename;
michael@0 106 return filename;
michael@0 107 }
michael@0 108
michael@0 109 function getRootDir(urlStr) {
michael@0 110 // TODO: This feels hacky, and like there will be edge cases.
michael@0 111 return urlStr.slice(0, urlStr.lastIndexOf("/") + 1);
michael@0 112 }
michael@0 113
michael@0 114 exports.SandboxFactory = function SandboxFactory(defaultPrincipal) {
michael@0 115 // Unless specified otherwise, use a principal with limited
michael@0 116 // privileges.
michael@0 117 this._defaultPrincipal = resolvePrincipal(defaultPrincipal,
michael@0 118 "http://www.mozilla.org");
michael@0 119 },
michael@0 120
michael@0 121 exports.SandboxFactory.prototype = {
michael@0 122 createSandbox: function createSandbox(options) {
michael@0 123 var principal = resolvePrincipal(options.principal,
michael@0 124 this._defaultPrincipal);
michael@0 125
michael@0 126 return {
michael@0 127 _sandbox: new Cu.Sandbox(principal),
michael@0 128 _principal: principal,
michael@0 129 get globalScope() {
michael@0 130 return this._sandbox;
michael@0 131 },
michael@0 132 defineProperty: function defineProperty(name, value) {
michael@0 133 this._sandbox[name] = value;
michael@0 134 },
michael@0 135 getProperty: function getProperty(name) {
michael@0 136 return this._sandbox[name];
michael@0 137 },
michael@0 138 evaluate: function evaluate(options) {
michael@0 139 if (typeof(options) == 'string')
michael@0 140 options = {contents: options};
michael@0 141 options = {__proto__: options};
michael@0 142 if (typeof(options.contents) != 'string')
michael@0 143 throw new Error('Expected string for options.contents');
michael@0 144 if (options.lineNo === undefined)
michael@0 145 options.lineNo = 1;
michael@0 146 if (options.jsVersion === undefined)
michael@0 147 options.jsVersion = "1.8";
michael@0 148 if (typeof(options.filename) != 'string')
michael@0 149 options.filename = '<string>';
michael@0 150
michael@0 151 if (this._principal == systemPrincipal)
michael@0 152 options.filename = maybeParentifyFilename(options.filename);
michael@0 153
michael@0 154 return Cu.evalInSandbox(options.contents,
michael@0 155 this._sandbox,
michael@0 156 options.jsVersion,
michael@0 157 options.filename,
michael@0 158 options.lineNo);
michael@0 159 }
michael@0 160 };
michael@0 161 }
michael@0 162 };
michael@0 163
michael@0 164 exports.Loader = function Loader(options) {
michael@0 165 options = {__proto__: options};
michael@0 166 if (options.fs === undefined) {
michael@0 167 var rootPaths = options.rootPath || options.rootPaths;
michael@0 168 if (rootPaths) {
michael@0 169 if (rootPaths.constructor.name != "Array")
michael@0 170 rootPaths = [rootPaths];
michael@0 171 var fses = [new exports.LocalFileSystem(path)
michael@0 172 for each (path in rootPaths)];
michael@0 173 options.fs = new exports.CompositeFileSystem(fses);
michael@0 174 } else
michael@0 175 options.fs = new exports.LocalFileSystem();
michael@0 176 }
michael@0 177 if (options.sandboxFactory === undefined)
michael@0 178 options.sandboxFactory = new exports.SandboxFactory(
michael@0 179 options.defaultPrincipal
michael@0 180 );
michael@0 181 if (options.modules === undefined)
michael@0 182 options.modules = {};
michael@0 183 if (options.globals === undefined)
michael@0 184 options.globals = {};
michael@0 185
michael@0 186 this.fs = options.fs;
michael@0 187 this.sandboxFactory = options.sandboxFactory;
michael@0 188 this.sandboxes = {};
michael@0 189 this.modules = options.modules;
michael@0 190 this.globals = options.globals;
michael@0 191 };
michael@0 192
michael@0 193 exports.Loader.prototype = {
michael@0 194 _makeRequire: function _makeRequire(rootDir) {
michael@0 195 var self = this;
michael@0 196 return function require(module) {
michael@0 197 if (module == "chrome") {
michael@0 198 var chrome = { Cc: Components.classes,
michael@0 199 Ci: Components.interfaces,
michael@0 200 Cu: Components.utils,
michael@0 201 Cr: Components.results,
michael@0 202 Cm: Components.manager,
michael@0 203 components: Components
michael@0 204 };
michael@0 205 return chrome;
michael@0 206 }
michael@0 207 var path = self.fs.resolveModule(rootDir, module);
michael@0 208 if (!path)
michael@0 209 throw new Error('Module "' + module + '" not found');
michael@0 210 if (!(path in self.modules)) {
michael@0 211 var options = self.fs.getFile(path);
michael@0 212 if (options.filename === undefined)
michael@0 213 options.filename = path;
michael@0 214
michael@0 215 var exports = {};
michael@0 216 var sandbox = self.sandboxFactory.createSandbox(options);
michael@0 217 self.sandboxes[path] = sandbox;
michael@0 218 for (name in self.globals)
michael@0 219 sandbox.defineProperty(name, self.globals[name]);
michael@0 220 sandbox.defineProperty('require', self._makeRequire(path));
michael@0 221 sandbox.evaluate("var exports = {};");
michael@0 222 let ES5 = self.modules.es5;
michael@0 223 if (ES5) {
michael@0 224 let { Object, Array, Function } = sandbox.globalScope;
michael@0 225 ES5.init(Object, Array, Function);
michael@0 226 }
michael@0 227 self.modules[path] = sandbox.getProperty("exports");
michael@0 228 sandbox.evaluate(options);
michael@0 229 }
michael@0 230 return self.modules[path];
michael@0 231 };
michael@0 232 },
michael@0 233
michael@0 234 // This is only really used by unit tests and other
michael@0 235 // development-related facilities, allowing access to symbols
michael@0 236 // defined in the global scope of a module.
michael@0 237 findSandboxForModule: function findSandboxForModule(module) {
michael@0 238 var path = this.fs.resolveModule(null, module);
michael@0 239 if (!path)
michael@0 240 throw new Error('Module "' + module + '" not found');
michael@0 241 if (!(path in this.sandboxes))
michael@0 242 this.require(module);
michael@0 243 if (!(path in this.sandboxes))
michael@0 244 throw new Error('Internal error: path not in sandboxes: ' +
michael@0 245 path);
michael@0 246 return this.sandboxes[path];
michael@0 247 },
michael@0 248
michael@0 249 require: function require(module) {
michael@0 250 return (this._makeRequire(null))(module);
michael@0 251 },
michael@0 252
michael@0 253 runScript: function runScript(options, extraOutput) {
michael@0 254 if (typeof(options) == 'string')
michael@0 255 options = {contents: options};
michael@0 256 options = {__proto__: options};
michael@0 257 var sandbox = this.sandboxFactory.createSandbox(options);
michael@0 258 if (extraOutput)
michael@0 259 extraOutput.sandbox = sandbox;
michael@0 260 for (name in this.globals)
michael@0 261 sandbox.defineProperty(name, this.globals[name]);
michael@0 262 sandbox.defineProperty('require', this._makeRequire(null));
michael@0 263 return sandbox.evaluate(options);
michael@0 264 }
michael@0 265 };
michael@0 266
michael@0 267 exports.CompositeFileSystem = function CompositeFileSystem(fses) {
michael@0 268 this.fses = fses;
michael@0 269 this._pathMap = {};
michael@0 270 };
michael@0 271
michael@0 272 exports.CompositeFileSystem.prototype = {
michael@0 273 resolveModule: function resolveModule(base, path) {
michael@0 274 for (var i = 0; i < this.fses.length; i++) {
michael@0 275 var fs = this.fses[i];
michael@0 276 var absPath = fs.resolveModule(base, path);
michael@0 277 if (absPath) {
michael@0 278 this._pathMap[absPath] = fs;
michael@0 279 return absPath;
michael@0 280 }
michael@0 281 }
michael@0 282 return null;
michael@0 283 },
michael@0 284 getFile: function getFile(path) {
michael@0 285 return this._pathMap[path].getFile(path);
michael@0 286 }
michael@0 287 };
michael@0 288
michael@0 289 exports.LocalFileSystem = function LocalFileSystem(root) {
michael@0 290 if (root === undefined) {
michael@0 291 if (!baseURI)
michael@0 292 throw new Error("Need a root path for module filesystem");
michael@0 293 root = baseURI;
michael@0 294 }
michael@0 295 if (typeof(root) == 'string')
michael@0 296 root = ios.newURI(root, null, baseURI);
michael@0 297 if (root instanceof Ci.nsIFile)
michael@0 298 root = ios.newFileURI(root);
michael@0 299 if (!(root instanceof Ci.nsIURI))
michael@0 300 throw new Error('Expected nsIFile, nsIURI, or string for root');
michael@0 301
michael@0 302 this.root = root.spec;
michael@0 303 this._rootURI = root;
michael@0 304 this._rootURIDir = getRootDir(root.spec);
michael@0 305 };
michael@0 306
michael@0 307 exports.LocalFileSystem.prototype = {
michael@0 308 resolveModule: function resolveModule(base, path) {
michael@0 309 path = path + ".js";
michael@0 310
michael@0 311 var baseURI;
michael@0 312 if (!base)
michael@0 313 baseURI = this._rootURI;
michael@0 314 else
michael@0 315 baseURI = ios.newURI(base, null, null);
michael@0 316 var newURI = ios.newURI(path, null, baseURI);
michael@0 317 var channel = ios.newChannelFromURI(newURI);
michael@0 318 try {
michael@0 319 channel.open().close();
michael@0 320 } catch (e if e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
michael@0 321 return null;
michael@0 322 }
michael@0 323 return newURI.spec;
michael@0 324 },
michael@0 325 getFile: function getFile(path) {
michael@0 326 var channel = ios.newChannel(path, null, null);
michael@0 327 var iStream = channel.open();
michael@0 328 var ciStream = Cc["@mozilla.org/intl/converter-input-stream;1"].
michael@0 329 createInstance(Ci.nsIConverterInputStream);
michael@0 330 var bufLen = 0x8000;
michael@0 331 ciStream.init(iStream, "UTF-8", bufLen,
michael@0 332 Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
michael@0 333 var chunk = {};
michael@0 334 var data = "";
michael@0 335 while (ciStream.readString(bufLen, chunk) > 0)
michael@0 336 data += chunk.value;
michael@0 337 ciStream.close();
michael@0 338 iStream.close();
michael@0 339 return {contents: data};
michael@0 340 }
michael@0 341 };
michael@0 342
michael@0 343 if (global.window) {
michael@0 344 // We're being loaded in a chrome window, or a web page with
michael@0 345 // UniversalXPConnect privileges.
michael@0 346 global.SecurableModule = exports;
michael@0 347 } else if (global.exports) {
michael@0 348 // We're being loaded in a SecurableModule.
michael@0 349 for (name in exports) {
michael@0 350 global.exports[name] = exports[name];
michael@0 351 }
michael@0 352 } else {
michael@0 353 // We're being loaded in a JS module.
michael@0 354 global.EXPORTED_SYMBOLS = [];
michael@0 355 for (name in exports) {
michael@0 356 global.EXPORTED_SYMBOLS.push(name);
michael@0 357 global[name] = exports[name];
michael@0 358 }
michael@0 359 }
michael@0 360 })(this);

mercurial