michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: 'use strict'; michael@0: michael@0: module.metadata = { michael@0: "stability": "unstable" michael@0: }; michael@0: michael@0: // This module is manually loaded by bootstrap.js in a sandbox and immediatly michael@0: // put in module cache so that it is never loaded in any other way. michael@0: michael@0: /* Workarounds to include dependencies in the manifest michael@0: require('chrome') // Otherwise CFX will complain about Components michael@0: require('toolkit/loader') // Otherwise CFX will stip out loader.js michael@0: require('sdk/addon/runner') // Otherwise CFX will stip out addon/runner.js michael@0: require('sdk/system/xul-app') // Otherwise CFX will stip out sdk/system/xul-app michael@0: */ michael@0: michael@0: const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components; michael@0: michael@0: // `loadSandbox` is exposed by bootstrap.js michael@0: const loaderURI = module.uri.replace("sdk/loader/cuddlefish.js", michael@0: "toolkit/loader.js"); michael@0: const xulappURI = module.uri.replace("loader/cuddlefish.js", michael@0: "system/xul-app.js"); michael@0: // We need to keep a reference to the sandbox in order to unload it in michael@0: // bootstrap.js michael@0: michael@0: const loaderSandbox = loadSandbox(loaderURI); michael@0: const loaderModule = loaderSandbox.exports; michael@0: michael@0: const xulappSandbox = loadSandbox(xulappURI); michael@0: const xulappModule = xulappSandbox.exports; michael@0: michael@0: const { override, load } = loaderModule; michael@0: michael@0: /** michael@0: * Ensure the current application satisfied the requirements specified in the michael@0: * module given. If not, an exception related to the incompatibility is michael@0: * returned; `null` otherwise. michael@0: * michael@0: * @param {Object} module michael@0: * The module to check michael@0: * @returns {Error} michael@0: */ michael@0: function incompatibility(module) { michael@0: let { metadata, id } = module; michael@0: michael@0: // if metadata or engines are not specified we assume compatibility is not michael@0: // an issue. michael@0: if (!metadata || !("engines" in metadata)) michael@0: return null; michael@0: michael@0: let { engines } = metadata; michael@0: michael@0: if (engines === null || typeof(engines) !== "object") michael@0: return new Error("Malformed engines' property in metadata"); michael@0: michael@0: let applications = Object.keys(engines); michael@0: michael@0: let versionRange; michael@0: applications.forEach(function(name) { michael@0: if (xulappModule.is(name)) { michael@0: versionRange = engines[name]; michael@0: // Continue iteration. We want to ensure the module doesn't michael@0: // contain a typo in the applications' name or some unknown michael@0: // application - `is` function throws an exception in that case. michael@0: } michael@0: }); michael@0: michael@0: if (typeof(versionRange) === "string") { michael@0: if (xulappModule.satisfiesVersion(versionRange)) michael@0: return null; michael@0: michael@0: return new Error("Unsupported Application version: The module " + id + michael@0: " currently supports only version " + versionRange + " of " + michael@0: xulappModule.name + "."); michael@0: } michael@0: michael@0: return new Error("Unsupported Application: The module " + id + michael@0: " currently supports only " + applications.join(", ") + ".") michael@0: } michael@0: michael@0: function CuddlefishLoader(options) { michael@0: let { manifest } = options; michael@0: michael@0: options = override(options, { michael@0: // Put `api-utils/loader` and `api-utils/cuddlefish` loaded as JSM to module michael@0: // cache to avoid subsequent loads via `require`. michael@0: modules: override({ michael@0: 'toolkit/loader': loaderModule, michael@0: 'sdk/loader/cuddlefish': exports, michael@0: 'sdk/system/xul-app': xulappModule michael@0: }, options.modules), michael@0: resolve: function resolve(id, requirer) { michael@0: let entry = requirer && requirer in manifest && manifest[requirer]; michael@0: let uri = null; michael@0: michael@0: // If manifest entry for this requirement is present we follow manifest. michael@0: // Note: Standard library modules like 'panel' will be present in michael@0: // manifest unless they were moved to platform. michael@0: if (entry) { michael@0: let requirement = entry.requirements[id]; michael@0: // If requirer entry is in manifest and it's requirement is not, than michael@0: // it has no authority to load since linker was not able to find it. michael@0: if (!requirement) michael@0: throw Error('Module: ' + requirer + ' has no authority to load: ' michael@0: + id, requirer); michael@0: michael@0: uri = requirement; michael@0: } else { michael@0: // If requirer is off manifest than it's a system module and we allow it michael@0: // to go off manifest by resolving a relative path. michael@0: uri = loaderModule.resolve(id, requirer); michael@0: } michael@0: return uri; michael@0: }, michael@0: load: function(loader, module) { michael@0: let result; michael@0: let error; michael@0: michael@0: // In order to get the module's metadata, we need to load the module. michael@0: // if an exception is raised here, it could be that is due to application michael@0: // incompatibility. Therefore the exception is stored, and thrown again michael@0: // only if the module seems be compatible with the application currently michael@0: // running. Otherwise the incompatibility message takes the precedence. michael@0: try { michael@0: result = load(loader, module); michael@0: } michael@0: catch (e) { michael@0: error = e; michael@0: } michael@0: michael@0: error = incompatibility(module) || error; michael@0: michael@0: if (error) michael@0: throw error; michael@0: michael@0: return result; michael@0: } michael@0: }); michael@0: michael@0: let loader = loaderModule.Loader(options); michael@0: // Hack to allow loading from `toolkit/loader`. michael@0: loader.modules[loaderURI] = loaderSandbox; michael@0: return loader; michael@0: } michael@0: michael@0: exports = override(loaderModule, { michael@0: Loader: CuddlefishLoader michael@0: });