|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 'use strict'; |
|
5 |
|
6 module.metadata = { |
|
7 "stability": "unstable" |
|
8 }; |
|
9 |
|
10 // This module is manually loaded by bootstrap.js in a sandbox and immediatly |
|
11 // put in module cache so that it is never loaded in any other way. |
|
12 |
|
13 /* Workarounds to include dependencies in the manifest |
|
14 require('chrome') // Otherwise CFX will complain about Components |
|
15 require('toolkit/loader') // Otherwise CFX will stip out loader.js |
|
16 require('sdk/addon/runner') // Otherwise CFX will stip out addon/runner.js |
|
17 require('sdk/system/xul-app') // Otherwise CFX will stip out sdk/system/xul-app |
|
18 */ |
|
19 |
|
20 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components; |
|
21 |
|
22 // `loadSandbox` is exposed by bootstrap.js |
|
23 const loaderURI = module.uri.replace("sdk/loader/cuddlefish.js", |
|
24 "toolkit/loader.js"); |
|
25 const xulappURI = module.uri.replace("loader/cuddlefish.js", |
|
26 "system/xul-app.js"); |
|
27 // We need to keep a reference to the sandbox in order to unload it in |
|
28 // bootstrap.js |
|
29 |
|
30 const loaderSandbox = loadSandbox(loaderURI); |
|
31 const loaderModule = loaderSandbox.exports; |
|
32 |
|
33 const xulappSandbox = loadSandbox(xulappURI); |
|
34 const xulappModule = xulappSandbox.exports; |
|
35 |
|
36 const { override, load } = loaderModule; |
|
37 |
|
38 /** |
|
39 * Ensure the current application satisfied the requirements specified in the |
|
40 * module given. If not, an exception related to the incompatibility is |
|
41 * returned; `null` otherwise. |
|
42 * |
|
43 * @param {Object} module |
|
44 * The module to check |
|
45 * @returns {Error} |
|
46 */ |
|
47 function incompatibility(module) { |
|
48 let { metadata, id } = module; |
|
49 |
|
50 // if metadata or engines are not specified we assume compatibility is not |
|
51 // an issue. |
|
52 if (!metadata || !("engines" in metadata)) |
|
53 return null; |
|
54 |
|
55 let { engines } = metadata; |
|
56 |
|
57 if (engines === null || typeof(engines) !== "object") |
|
58 return new Error("Malformed engines' property in metadata"); |
|
59 |
|
60 let applications = Object.keys(engines); |
|
61 |
|
62 let versionRange; |
|
63 applications.forEach(function(name) { |
|
64 if (xulappModule.is(name)) { |
|
65 versionRange = engines[name]; |
|
66 // Continue iteration. We want to ensure the module doesn't |
|
67 // contain a typo in the applications' name or some unknown |
|
68 // application - `is` function throws an exception in that case. |
|
69 } |
|
70 }); |
|
71 |
|
72 if (typeof(versionRange) === "string") { |
|
73 if (xulappModule.satisfiesVersion(versionRange)) |
|
74 return null; |
|
75 |
|
76 return new Error("Unsupported Application version: The module " + id + |
|
77 " currently supports only version " + versionRange + " of " + |
|
78 xulappModule.name + "."); |
|
79 } |
|
80 |
|
81 return new Error("Unsupported Application: The module " + id + |
|
82 " currently supports only " + applications.join(", ") + ".") |
|
83 } |
|
84 |
|
85 function CuddlefishLoader(options) { |
|
86 let { manifest } = options; |
|
87 |
|
88 options = override(options, { |
|
89 // Put `api-utils/loader` and `api-utils/cuddlefish` loaded as JSM to module |
|
90 // cache to avoid subsequent loads via `require`. |
|
91 modules: override({ |
|
92 'toolkit/loader': loaderModule, |
|
93 'sdk/loader/cuddlefish': exports, |
|
94 'sdk/system/xul-app': xulappModule |
|
95 }, options.modules), |
|
96 resolve: function resolve(id, requirer) { |
|
97 let entry = requirer && requirer in manifest && manifest[requirer]; |
|
98 let uri = null; |
|
99 |
|
100 // If manifest entry for this requirement is present we follow manifest. |
|
101 // Note: Standard library modules like 'panel' will be present in |
|
102 // manifest unless they were moved to platform. |
|
103 if (entry) { |
|
104 let requirement = entry.requirements[id]; |
|
105 // If requirer entry is in manifest and it's requirement is not, than |
|
106 // it has no authority to load since linker was not able to find it. |
|
107 if (!requirement) |
|
108 throw Error('Module: ' + requirer + ' has no authority to load: ' |
|
109 + id, requirer); |
|
110 |
|
111 uri = requirement; |
|
112 } else { |
|
113 // If requirer is off manifest than it's a system module and we allow it |
|
114 // to go off manifest by resolving a relative path. |
|
115 uri = loaderModule.resolve(id, requirer); |
|
116 } |
|
117 return uri; |
|
118 }, |
|
119 load: function(loader, module) { |
|
120 let result; |
|
121 let error; |
|
122 |
|
123 // In order to get the module's metadata, we need to load the module. |
|
124 // if an exception is raised here, it could be that is due to application |
|
125 // incompatibility. Therefore the exception is stored, and thrown again |
|
126 // only if the module seems be compatible with the application currently |
|
127 // running. Otherwise the incompatibility message takes the precedence. |
|
128 try { |
|
129 result = load(loader, module); |
|
130 } |
|
131 catch (e) { |
|
132 error = e; |
|
133 } |
|
134 |
|
135 error = incompatibility(module) || error; |
|
136 |
|
137 if (error) |
|
138 throw error; |
|
139 |
|
140 return result; |
|
141 } |
|
142 }); |
|
143 |
|
144 let loader = loaderModule.Loader(options); |
|
145 // Hack to allow loading from `toolkit/loader`. |
|
146 loader.modules[loaderURI] = loaderSandbox; |
|
147 return loader; |
|
148 } |
|
149 |
|
150 exports = override(loaderModule, { |
|
151 Loader: CuddlefishLoader |
|
152 }); |