aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.4/packages/api-utils/lib/cuddlefish.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.4/packages/api-utils/lib/cuddlefish.js')
-rw-r--r--tools/addon-sdk-1.4/packages/api-utils/lib/cuddlefish.js320
1 files changed, 320 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.4/packages/api-utils/lib/cuddlefish.js b/tools/addon-sdk-1.4/packages/api-utils/lib/cuddlefish.js
new file mode 100644
index 0000000..c0c9935
--- /dev/null
+++ b/tools/addon-sdk-1.4/packages/api-utils/lib/cuddlefish.js
@@ -0,0 +1,320 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* ***** 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Irakli Gozalishvili <gozala@mozilla.com> (Original Author)
+ *
+ * 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 ***** */
+var EXPORTED_SYMBOLS = [ 'Loader' ];
+
+!function(exports) {
+
+"use strict";
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
+ results: Cr, manager: Cm } = Components;
+const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
+const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
+ getService(Ci.mozIJSSubScriptLoader);
+
+const Sandbox = {
+ new: function (prototype, principal) {
+ let sandbox = Object.create(Sandbox, {
+ sandbox: {
+ value: Cu.Sandbox(principal || Sandbox.principal, {
+ sandboxPrototype: prototype || Sandbox.prototype,
+ wantXrays: Sandbox.wantXrays
+ })
+ }
+ });
+ // There are few properties (dump, Iterator) that by default appear in
+ // sandboxes shadowing properties provided by a prototype. To workaround
+ // this we override all such properties by copying them directly to the
+ // sandbox.
+ Object.keys(prototype).forEach(function onEach(key) {
+ if (sandbox.sandbox[key] !== prototype[key])
+ sandbox.sandbox[key] = prototype[key];
+ });
+ return sandbox;
+ },
+ evaluate: function evaluate(source, uri, lineNumber) {
+ return Cu.evalInSandbox(
+ source,
+ this.sandbox,
+ this.version,
+ uri,
+ lineNumber || this.lineNumber
+ );
+ },
+ load: function load(uri) {
+ scriptLoader.loadSubScript(uri, this.sandbox);
+ },
+ merge: function merge(properties) {
+ Object.getOwnPropertyNames(properties).forEach(function(name) {
+ Object.defineProperty(this.sandbox, name,
+ Object.getOwnPropertyDescriptor(properties, name));
+ }, this);
+ },
+ principal: systemPrincipal,
+ version: '1.8',
+ lineNumber: 1,
+ wantXrays: false,
+ prototype: {}
+};
+
+// the Module object made available to CommonJS modules when they are
+// evaluated, along with 'exports' and 'uri'
+const Module = {
+ new: function(id, path, uri) {
+ let module = Object.create(this);
+
+ module.id = id;
+ module.path = path;
+ module.uri = uri;
+ module.exports = {};
+
+ return module;
+ },
+ // TODO: I'd like to remove this, it's not used adds complexity and does
+ // not has much adoption in commonjs either.
+ setExports: function setExports(exports) {
+ this.exports = exports;
+ }
+};
+
+const Loader = {
+ new: function (options) {
+ let loader = Object.create(Loader, {
+ // Manifest generated by a linker, containing map of module url's mapped
+ // to it's requirements, comes from harness-options.json
+ manifest: { value: options.manifest || {} },
+
+ // Following property may be passed in (usually for mocking purposes) in
+ // order to override default modules cache.
+ modules: { value: options.modules || Object.create(Loader.modules) },
+ globals: { value: options.globals || {} },
+
+ uriPrefix: { value: options.uriPrefix },
+
+ sandboxes: { value: {} }
+ });
+ loader.require = this.require.bind(loader, options.loader);
+
+ // some 'magic' modules, that have no corresponding .js file
+ loader.modules['@packaging'] = Object.freeze({
+ id: '@packaging',
+ exports: JSON.parse(JSON.stringify(options))
+ });
+ loader.modules['@loader'] = Object.freeze({
+ exports: Object.freeze({ Loader: Loader }),
+ id: '@loader'
+ });
+
+ // This special module defines globals which will be added to every
+ // module this loader creates
+ let globals = loader.require('api-utils/globals!');
+ Object.getOwnPropertyNames(globals).forEach(function(name) {
+ Object.defineProperty(loader.globals, name,
+ Object.getOwnPropertyDescriptor(globals, name));
+ });
+ // Freeze globals so that modules won't have a chance to mutate scope of
+ // other modules.
+ Object.freeze(globals);
+
+ // Override global `dump` so that it behaves same as in any other module (
+ // currently we override dump to write to a file instead of `stdout` so that
+ // python can read it on windows).
+ dump = globals.dump;
+
+ return Object.freeze(loader);
+ },
+ modules: {
+ 'chrome': Object.freeze({
+ exports: Object.freeze({
+ Cc: Cc,
+ CC: CC,
+ Ci: Ci,
+ Cu: Cu,
+ Cr: Cr,
+ Cm: Cm,
+ components: Components,
+ messageManager: 'addMessageListener' in exports ? exports : null
+ }),
+ id: 'chrome'
+ }),
+ 'self': function self(loader, requirer) {
+ return loader.require('api-utils/self!').create(requirer.path);
+ },
+ },
+
+ // populate a Module by evaluating the CommonJS module code in the sandbox
+ load: function load(module) {
+ let require = Loader.require.bind(this, module.path);
+ require.main = this.main;
+ let sandbox = this.sandboxes[module.path] = Sandbox.new(this.globals);
+ sandbox.merge({
+ require: require,
+ module: module,
+ exports: module.exports
+ });
+
+ sandbox.load(module.uri);
+
+ // Workaround for bug 674195. Freezing objects from other sandboxes fail,
+ // so we create descendant and freeze it instead.
+ if (typeof(module.exports) === 'object') {
+ module.exports = Object.prototype.isPrototypeOf(module.exports) ?
+ Object.freeze(module.exports) :
+ Object.freeze(Object.create(module.exports));
+ }
+ },
+
+ // this require() is the main entry point for regular CommonJS modules. The
+ // bind() in load (above) causes those modules to get a very restricted
+ // form of this require(): one which can only ever reference this one
+ // loader, and which always uses their URI as a "base" (so they're limited
+ // to their own manifest entries, and can't import anything off the
+ // manifest).
+ require: function require(base, id) {
+ let module, manifest = this.manifest[base], requirer = this.modules[base];
+
+ if (!id)
+ throw Error("you must provide a module name when calling require() from "
+ + (requirer && requirer.id), base, id);
+
+ // If we have a manifest for requirer, then all it's requirements have been
+ // registered by linker and we should have a `path` to the required module.
+ // Even pseudo-modules like 'chrome', 'self', '@packaging', and '@loader'
+ // have pseudo-paths: exactly those same names.
+ // details see: Bug-697422.
+ let requirement = manifest && manifest.requirements[id];
+ if (!requirement)
+ throw Error("Module: " + (requirer && requirer.id) + ' located at ' +
+ base + " has no authority to load: " + id);
+ let path = requirement.path;
+
+ if (path in this.modules) {
+ module = this.modules[path];
+ }
+ else {
+ let uri = this.uriPrefix + path;
+ module = this.modules[path] = Module.new(id, path, uri);
+ this.load(module);
+ Object.freeze(module);
+ }
+
+ // "magic" modules which have contents that depend upon who imports them
+ // (like "self") are expressed in the Loader's pre-populated 'modules'
+ // table as callable functions, which are given the reference to this
+ // Loader and a copy of the importer's URI
+ //
+ // TODO: Find a better way to implement `self`.
+ // Maybe something like require('self!path/to/data')
+ if (typeof(module) === 'function')
+ module = module(this, requirer);
+
+ return module.exports;
+ },
+
+ // process.process() will eventually cause a call to main() to be evaluated
+ // in the addon's context. This function loads and executes the addon's
+ // entry point module.
+ main: function main(id, path) {
+ try {
+ let uri = this.uriPrefix + path;
+ let module = this.modules[path] = Module.new(id, path, uri);
+ this.load(module); // this is where the addon's main.js finally runs
+ let program = Object.freeze(module).exports;
+
+ if (typeof(program.onUnload) === 'function')
+ this.require('api-utils/unload').when(program.onUnload);
+
+ if (program.main) {
+ let { exit, staticArgs } = this.require('api-utils/system');
+ let { loadReason } = this.require('@packaging');
+ program.main({ loadReason: loadReason, staticArgs: staticArgs },
+ { print: function($) dump($ + '\n'), quit: exit });
+ }
+ } catch (error) {
+ Cu.reportError(error);
+ if (this.globals.console) this.globals.console.exception(error);
+ throw error;
+ }
+ },
+
+ // This is the main entry-point: bootstrap.js calls this when the add-on is
+ // installed. The order of calls is a bit confusing, but here's what
+ // happens (in temporal order):
+ // * process.spawn creates a new XUL 'browser' element which will house the
+ // main addon code. When e10s is active, this uses a real separate OS
+ // process. When e10s is disabled, this element lives in the one original
+ // process. Either way, its API is the same.
+ // * Grab the channel named "require!" and attach a handler which will load
+ // modules (in the chrome process) when requested to by the addon
+ // process. This handler uses Loader.require to import the module, then
+ // calls the module's .initialize() function to connect a new channel.
+ // The remote caller winds up with a channel reference, which they can
+ // use to send messages to the newly loaded module. This is for e10s.
+ // * After the channel handler is attached, process.process() (invoked by
+ // process.spawn()) will use loadScript() to evaluate code in the
+ // 'browser' element (which is where the main addon code starts running),
+ // to do the following:
+ // * create a Loader, initialized with the same manifest and
+ // harness-options.json that we've got
+ // * invoke it's main() method, with the name and path of the addon's
+ // entry module (which comes from linker via harness-options.js, and is
+ // usually main.js). That executes main(), above.
+ // * main() loads the addon's main.js, which executes all top-level
+ // forms. If the module defines an "exports.main=" function, we invoke
+ // that too. This is where the addon finally gets to run.
+ spawn: function spawn(id, path) {
+ let loader = this;
+ let process = this.require('api-utils/process');
+ process.spawn(id, path)(function(addon) {
+ // Listen to `require!` channel's input messages from the add-on process
+ // and load modules being required.
+ addon.channel('require!').input(function({ requirer: { path }, id }) {
+ try {
+ Loader.require.call(loader, path, id).initialize(addon.channel(id));
+ } catch (error) {
+ this.globals.console.exception(error);
+ }
+ });
+ });
+ },
+ unload: function unload(reason, callback) {
+ this.require('api-utils/unload').send(reason, callback);
+ }
+};
+exports.Loader = Loader;
+
+}(this);