diff options
Diffstat (limited to 'services/sync/tps/extensions/mozmill/resource/stdlib/securable-module.js')
-rw-r--r-- | services/sync/tps/extensions/mozmill/resource/stdlib/securable-module.js | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/services/sync/tps/extensions/mozmill/resource/stdlib/securable-module.js b/services/sync/tps/extensions/mozmill/resource/stdlib/securable-module.js new file mode 100644 index 000000000..2648afd27 --- /dev/null +++ b/services/sync/tps/extensions/mozmill/resource/stdlib/securable-module.js @@ -0,0 +1,370 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma <atul@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +(function(global) { + const Cc = Components.classes; + const Ci = Components.interfaces; + const Cu = Components.utils; + const Cr = Components.results; + + Cu.import("resource://gre/modules/NetUtil.jsm"); + + var exports = {}; + + var ios = Cc['@mozilla.org/network/io-service;1'] + .getService(Ci.nsIIOService); + + var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"] + .createInstance(Ci.nsIPrincipal); + + function resolvePrincipal(principal, defaultPrincipal) { + if (principal === undefined) + return defaultPrincipal; + if (principal == "system") + return systemPrincipal; + return principal; + } + + // The base URI to we use when we're given relative URLs, if any. + var baseURI = null; + if (global.window) + baseURI = ios.newURI(global.location.href, null, null); + exports.baseURI = baseURI; + + // The "parent" chrome URI to use if we're loading code that + // needs chrome privileges but may not have a filename that + // matches any of SpiderMonkey's defined system filename prefixes. + // The latter is needed so that wrappers can be automatically + // made for the code. For more information on this, see + // bug 418356: + // + // https://bugzilla.mozilla.org/show_bug.cgi?id=418356 + var parentChromeURIString; + if (baseURI) + // We're being loaded from a chrome-privileged document, so + // use its URL as the parent string. + parentChromeURIString = baseURI.spec; + else + // We're being loaded from a chrome-privileged JS module or + // SecurableModule, so use its filename (which may itself + // contain a reference to a parent). + parentChromeURIString = Components.stack.filename; + + function maybeParentifyFilename(filename) { + var doParentifyFilename = true; + try { + // TODO: Ideally we should just make + // nsIChromeRegistry.wrappersEnabled() available from script + // and use it here. Until that's in the platform, though, + // we'll play it safe and parentify the filename unless + // we're absolutely certain things will be ok if we don't. + var filenameURI = ios.newURI(options.filename, + null, + baseURI); + if (filenameURI.scheme == 'chrome' && + filenameURI.path.indexOf('/content/') == 0) + // Content packages will always have wrappers made for them; + // if automatic wrappers have been disabled for the + // chrome package via a chrome manifest flag, then + // this still works too, to the extent that the + // content package is insecure anyways. + doParentifyFilename = false; + } catch (e) {} + if (doParentifyFilename) + return parentChromeURIString + " -> " + filename; + return filename; + } + + function getRootDir(urlStr) { + // TODO: This feels hacky, and like there will be edge cases. + return urlStr.slice(0, urlStr.lastIndexOf("/") + 1); + } + + exports.SandboxFactory = function SandboxFactory(defaultPrincipal) { + // Unless specified otherwise, use a principal with limited + // privileges. + this._defaultPrincipal = resolvePrincipal(defaultPrincipal, + "http://www.mozilla.org"); + }, + + exports.SandboxFactory.prototype = { + createSandbox: function createSandbox(options) { + var principal = resolvePrincipal(options.principal, + this._defaultPrincipal); + + return { + _sandbox: new Cu.Sandbox(principal), + _principal: principal, + get globalScope() { + return this._sandbox; + }, + defineProperty: function defineProperty(name, value) { + this._sandbox[name] = value; + }, + getProperty: function getProperty(name) { + return this._sandbox[name]; + }, + evaluate: function evaluate(options) { + if (typeof(options) == 'string') + options = {contents: options}; + options = {__proto__: options}; + if (typeof(options.contents) != 'string') + throw new Error('Expected string for options.contents'); + if (options.lineNo === undefined) + options.lineNo = 1; + if (options.jsVersion === undefined) + options.jsVersion = "1.8"; + if (typeof(options.filename) != 'string') + options.filename = '<string>'; + + if (this._principal == systemPrincipal) + options.filename = maybeParentifyFilename(options.filename); + + return Cu.evalInSandbox(options.contents, + this._sandbox, + options.jsVersion, + options.filename, + options.lineNo); + } + }; + } + }; + + exports.Loader = function Loader(options) { + options = {__proto__: options}; + if (options.fs === undefined) { + var rootPaths = options.rootPath || options.rootPaths; + if (rootPaths) { + if (rootPaths.constructor.name != "Array") + rootPaths = [rootPaths]; + var fses = rootPaths.map(path => new exports.LocalFileSystem(path)); + options.fs = new exports.CompositeFileSystem(fses); + } else + options.fs = new exports.LocalFileSystem(); + } + if (options.sandboxFactory === undefined) + options.sandboxFactory = new exports.SandboxFactory( + options.defaultPrincipal + ); + if (options.modules === undefined) + options.modules = {}; + if (options.globals === undefined) + options.globals = {}; + + this.fs = options.fs; + this.sandboxFactory = options.sandboxFactory; + this.sandboxes = {}; + this.modules = options.modules; + this.globals = options.globals; + }; + + exports.Loader.prototype = { + _makeRequire: function _makeRequire(rootDir) { + var self = this; + return function require(module) { + if (module == "chrome") { + var chrome = { Cc: Components.classes, + Ci: Components.interfaces, + Cu: Components.utils, + Cr: Components.results, + Cm: Components.manager, + components: Components + }; + return chrome; + } + var path = self.fs.resolveModule(rootDir, module); + if (!path) + throw new Error('Module "' + module + '" not found'); + if (!(path in self.modules)) { + var options = self.fs.getFile(path); + if (options.filename === undefined) + options.filename = path; + + var exports = {}; + var sandbox = self.sandboxFactory.createSandbox(options); + self.sandboxes[path] = sandbox; + for (name in self.globals) + sandbox.defineProperty(name, self.globals[name]); + sandbox.defineProperty('require', self._makeRequire(path)); + sandbox.evaluate("var exports = {};"); + let ES5 = self.modules.es5; + if (ES5) { + let { Object, Array, Function } = sandbox.globalScope; + ES5.init(Object, Array, Function); + } + self.modules[path] = sandbox.getProperty("exports"); + sandbox.evaluate(options); + } + return self.modules[path]; + }; + }, + + // This is only really used by unit tests and other + // development-related facilities, allowing access to symbols + // defined in the global scope of a module. + findSandboxForModule: function findSandboxForModule(module) { + var path = this.fs.resolveModule(null, module); + if (!path) + throw new Error('Module "' + module + '" not found'); + if (!(path in this.sandboxes)) + this.require(module); + if (!(path in this.sandboxes)) + throw new Error('Internal error: path not in sandboxes: ' + + path); + return this.sandboxes[path]; + }, + + require: function require(module) { + return (this._makeRequire(null))(module); + }, + + runScript: function runScript(options, extraOutput) { + if (typeof(options) == 'string') + options = {contents: options}; + options = {__proto__: options}; + var sandbox = this.sandboxFactory.createSandbox(options); + if (extraOutput) + extraOutput.sandbox = sandbox; + for (name in this.globals) + sandbox.defineProperty(name, this.globals[name]); + sandbox.defineProperty('require', this._makeRequire(null)); + return sandbox.evaluate(options); + } + }; + + exports.CompositeFileSystem = function CompositeFileSystem(fses) { + this.fses = fses; + this._pathMap = {}; + }; + + exports.CompositeFileSystem.prototype = { + resolveModule: function resolveModule(base, path) { + for (var i = 0; i < this.fses.length; i++) { + var fs = this.fses[i]; + var absPath = fs.resolveModule(base, path); + if (absPath) { + this._pathMap[absPath] = fs; + return absPath; + } + } + return null; + }, + getFile: function getFile(path) { + return this._pathMap[path].getFile(path); + } + }; + + exports.LocalFileSystem = function LocalFileSystem(root) { + if (root === undefined) { + if (!baseURI) + throw new Error("Need a root path for module filesystem"); + root = baseURI; + } + if (typeof(root) == 'string') + root = ios.newURI(root, null, baseURI); + if (root instanceof Ci.nsIFile) + root = ios.newFileURI(root); + if (!(root instanceof Ci.nsIURI)) + throw new Error('Expected nsIFile, nsIURI, or string for root'); + + this.root = root.spec; + this._rootURI = root; + this._rootURIDir = getRootDir(root.spec); + }; + + exports.LocalFileSystem.prototype = { + resolveModule: function resolveModule(base, path) { + path = path + ".js"; + + var baseURI; + if (!base) + baseURI = this._rootURI; + else + baseURI = ios.newURI(base, null, null); + var newURI = ios.newURI(path, null, baseURI); + var channel = NetUtil.newChannel({ + uri: newURI, + loadUsingSystemPrincipal: true + }); + try { + channel.open2().close(); + } catch (e) { + if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) { + throw e; + } + return null; + } + return newURI.spec; + }, + getFile: function getFile(path) { + var channel = NetUtil.newChannel({ + uri: path, + loadUsingSystemPrincipal: true + }); + var iStream = channel.open2(); + var ciStream = Cc["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Ci.nsIConverterInputStream); + var bufLen = 0x8000; + ciStream.init(iStream, "UTF-8", bufLen, + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + var chunk = {}; + var data = ""; + while (ciStream.readString(bufLen, chunk) > 0) + data += chunk.value; + ciStream.close(); + iStream.close(); + return {contents: data}; + } + }; + + if (global.window) { + // We're being loaded in a chrome window, or a web page with + // UniversalXPConnect privileges. + global.SecurableModule = exports; + } else if (global.exports) { + // We're being loaded in a SecurableModule. + for (name in exports) { + global.exports[name] = exports[name]; + } + } else { + // We're being loaded in a JS module. + global.EXPORTED_SYMBOLS = []; + for (name in exports) { + global.EXPORTED_SYMBOLS.push(name); + global[name] = exports[name]; + } + } + })(this); |