|
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 |
|
5 module.metadata = { |
|
6 "stability": "experimental" |
|
7 }; |
|
8 |
|
9 const { Cc, Ci } = require('chrome'); |
|
10 const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader'); |
|
11 const { once } = require('../system/events'); |
|
12 const { exit, env, staticArgs } = require('../system'); |
|
13 const { when: unload } = require('../system/unload'); |
|
14 const { loadReason } = require('../self'); |
|
15 const { rootURI } = require("@loader/options"); |
|
16 const globals = require('../system/globals'); |
|
17 const xulApp = require('../system/xul-app'); |
|
18 const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. |
|
19 getService(Ci.nsIAppShellService); |
|
20 |
|
21 const NAME2TOPIC = { |
|
22 'Firefox': 'sessionstore-windows-restored', |
|
23 'Fennec': 'sessionstore-windows-restored', |
|
24 'SeaMonkey': 'sessionstore-windows-restored', |
|
25 'Thunderbird': 'mail-startup-done' |
|
26 }; |
|
27 |
|
28 // Set 'final-ui-startup' as default topic for unknown applications |
|
29 let appStartup = 'final-ui-startup'; |
|
30 |
|
31 // Gets the topic that fit best as application startup event, in according with |
|
32 // the current application (e.g. Firefox, Fennec, Thunderbird...) |
|
33 for (let name of Object.keys(NAME2TOPIC)) { |
|
34 if (xulApp.is(name)) { |
|
35 appStartup = NAME2TOPIC[name]; |
|
36 break; |
|
37 } |
|
38 } |
|
39 |
|
40 // Initializes default preferences |
|
41 function setDefaultPrefs(prefsURI) { |
|
42 const prefs = Cc['@mozilla.org/preferences-service;1']. |
|
43 getService(Ci.nsIPrefService). |
|
44 QueryInterface(Ci.nsIPrefBranch2); |
|
45 const branch = prefs.getDefaultBranch(''); |
|
46 const sandbox = Sandbox({ |
|
47 name: prefsURI, |
|
48 prototype: { |
|
49 pref: function(key, val) { |
|
50 switch (typeof val) { |
|
51 case 'boolean': |
|
52 branch.setBoolPref(key, val); |
|
53 break; |
|
54 case 'number': |
|
55 if (val % 1 == 0) // number must be a integer, otherwise ignore it |
|
56 branch.setIntPref(key, val); |
|
57 break; |
|
58 case 'string': |
|
59 branch.setCharPref(key, val); |
|
60 break; |
|
61 } |
|
62 } |
|
63 } |
|
64 }); |
|
65 // load preferences. |
|
66 evaluate(sandbox, prefsURI); |
|
67 } |
|
68 |
|
69 function definePseudo(loader, id, exports) { |
|
70 let uri = resolveURI(id, loader.mapping); |
|
71 loader.modules[uri] = { exports: exports }; |
|
72 } |
|
73 |
|
74 function wait(reason, options) { |
|
75 once(appStartup, function() { |
|
76 startup(null, options); |
|
77 }); |
|
78 } |
|
79 |
|
80 function startup(reason, options) { |
|
81 // Try accessing hidden window to guess if we are running during firefox |
|
82 // startup, so that we should wait for session restore event before |
|
83 // running the addon |
|
84 let initialized = false; |
|
85 try { |
|
86 appShellService.hiddenDOMWindow; |
|
87 initialized = true; |
|
88 } |
|
89 catch(e) {} |
|
90 if (reason === 'startup' || !initialized) { |
|
91 return wait(reason, options); |
|
92 } |
|
93 |
|
94 // Inject globals ASAP in order to have console API working ASAP |
|
95 Object.defineProperties(options.loader.globals, descriptor(globals)); |
|
96 |
|
97 // NOTE: Module is intentionally required only now because it relies |
|
98 // on existence of hidden window, which does not exists until startup. |
|
99 let { ready } = require('../addon/window'); |
|
100 // Load localization manifest and .properties files. |
|
101 // Run the addon even in case of error (best effort approach) |
|
102 require('../l10n/loader'). |
|
103 load(rootURI). |
|
104 then(null, function failure(error) { |
|
105 console.info("Error while loading localization: " + error.message); |
|
106 }). |
|
107 then(function onLocalizationReady(data) { |
|
108 // Exports data to a pseudo module so that api-utils/l10n/core |
|
109 // can get access to it |
|
110 definePseudo(options.loader, '@l10n/data', data ? data : null); |
|
111 return ready; |
|
112 }).then(function() { |
|
113 run(options); |
|
114 }).then(null, console.exception); |
|
115 return void 0; // otherwise we raise a warning, see bug 910304 |
|
116 } |
|
117 |
|
118 function run(options) { |
|
119 try { |
|
120 // Try initializing HTML localization before running main module. Just print |
|
121 // an exception in case of error, instead of preventing addon to be run. |
|
122 try { |
|
123 // Do not enable HTML localization while running test as it is hard to |
|
124 // disable. Because unit tests are evaluated in a another Loader who |
|
125 // doesn't have access to this current loader. |
|
126 if (options.main !== 'test-harness/run-tests') |
|
127 require('../l10n/html').enable(); |
|
128 } |
|
129 catch(error) { |
|
130 console.exception(error); |
|
131 } |
|
132 // Initialize inline options localization, without preventing addon to be |
|
133 // run in case of error |
|
134 try { |
|
135 require('../l10n/prefs'); |
|
136 } |
|
137 catch(error) { |
|
138 console.exception(error); |
|
139 } |
|
140 |
|
141 // TODO: When bug 564675 is implemented this will no longer be needed |
|
142 // Always set the default prefs, because they disappear on restart |
|
143 if (options.prefsURI) { |
|
144 // Only set if `prefsURI` specified |
|
145 setDefaultPrefs(options.prefsURI); |
|
146 } |
|
147 |
|
148 // this is where the addon's main.js finally run. |
|
149 let program = main(options.loader, options.main); |
|
150 |
|
151 if (typeof(program.onUnload) === 'function') |
|
152 unload(program.onUnload); |
|
153 |
|
154 if (typeof(program.main) === 'function') { |
|
155 |
|
156 program.main({ |
|
157 loadReason: loadReason, |
|
158 staticArgs: staticArgs |
|
159 }, { |
|
160 print: function print(_) { dump(_ + '\n') }, |
|
161 quit: exit |
|
162 }); |
|
163 } |
|
164 } catch (error) { |
|
165 console.exception(error); |
|
166 throw error; |
|
167 } |
|
168 } |
|
169 exports.startup = startup; |
|
170 |
|
171 // If add-on is lunched via `cfx run` we need to use `system.exit` to let |
|
172 // cfx know we're done (`cfx test` will take care of exit so we don't do |
|
173 // anything here). |
|
174 if (env.CFX_COMMAND === 'run') { |
|
175 unload(function(reason) { |
|
176 if (reason === 'shutdown') |
|
177 exit(0); |
|
178 }); |
|
179 } |