1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/addon/runner.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,179 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +module.metadata = { 1.9 + "stability": "experimental" 1.10 +}; 1.11 + 1.12 +const { Cc, Ci } = require('chrome'); 1.13 +const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader'); 1.14 +const { once } = require('../system/events'); 1.15 +const { exit, env, staticArgs } = require('../system'); 1.16 +const { when: unload } = require('../system/unload'); 1.17 +const { loadReason } = require('../self'); 1.18 +const { rootURI } = require("@loader/options"); 1.19 +const globals = require('../system/globals'); 1.20 +const xulApp = require('../system/xul-app'); 1.21 +const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. 1.22 + getService(Ci.nsIAppShellService); 1.23 + 1.24 +const NAME2TOPIC = { 1.25 + 'Firefox': 'sessionstore-windows-restored', 1.26 + 'Fennec': 'sessionstore-windows-restored', 1.27 + 'SeaMonkey': 'sessionstore-windows-restored', 1.28 + 'Thunderbird': 'mail-startup-done' 1.29 +}; 1.30 + 1.31 +// Set 'final-ui-startup' as default topic for unknown applications 1.32 +let appStartup = 'final-ui-startup'; 1.33 + 1.34 +// Gets the topic that fit best as application startup event, in according with 1.35 +// the current application (e.g. Firefox, Fennec, Thunderbird...) 1.36 +for (let name of Object.keys(NAME2TOPIC)) { 1.37 + if (xulApp.is(name)) { 1.38 + appStartup = NAME2TOPIC[name]; 1.39 + break; 1.40 + } 1.41 +} 1.42 + 1.43 +// Initializes default preferences 1.44 +function setDefaultPrefs(prefsURI) { 1.45 + const prefs = Cc['@mozilla.org/preferences-service;1']. 1.46 + getService(Ci.nsIPrefService). 1.47 + QueryInterface(Ci.nsIPrefBranch2); 1.48 + const branch = prefs.getDefaultBranch(''); 1.49 + const sandbox = Sandbox({ 1.50 + name: prefsURI, 1.51 + prototype: { 1.52 + pref: function(key, val) { 1.53 + switch (typeof val) { 1.54 + case 'boolean': 1.55 + branch.setBoolPref(key, val); 1.56 + break; 1.57 + case 'number': 1.58 + if (val % 1 == 0) // number must be a integer, otherwise ignore it 1.59 + branch.setIntPref(key, val); 1.60 + break; 1.61 + case 'string': 1.62 + branch.setCharPref(key, val); 1.63 + break; 1.64 + } 1.65 + } 1.66 + } 1.67 + }); 1.68 + // load preferences. 1.69 + evaluate(sandbox, prefsURI); 1.70 +} 1.71 + 1.72 +function definePseudo(loader, id, exports) { 1.73 + let uri = resolveURI(id, loader.mapping); 1.74 + loader.modules[uri] = { exports: exports }; 1.75 +} 1.76 + 1.77 +function wait(reason, options) { 1.78 + once(appStartup, function() { 1.79 + startup(null, options); 1.80 + }); 1.81 +} 1.82 + 1.83 +function startup(reason, options) { 1.84 + // Try accessing hidden window to guess if we are running during firefox 1.85 + // startup, so that we should wait for session restore event before 1.86 + // running the addon 1.87 + let initialized = false; 1.88 + try { 1.89 + appShellService.hiddenDOMWindow; 1.90 + initialized = true; 1.91 + } 1.92 + catch(e) {} 1.93 + if (reason === 'startup' || !initialized) { 1.94 + return wait(reason, options); 1.95 + } 1.96 + 1.97 + // Inject globals ASAP in order to have console API working ASAP 1.98 + Object.defineProperties(options.loader.globals, descriptor(globals)); 1.99 + 1.100 + // NOTE: Module is intentionally required only now because it relies 1.101 + // on existence of hidden window, which does not exists until startup. 1.102 + let { ready } = require('../addon/window'); 1.103 + // Load localization manifest and .properties files. 1.104 + // Run the addon even in case of error (best effort approach) 1.105 + require('../l10n/loader'). 1.106 + load(rootURI). 1.107 + then(null, function failure(error) { 1.108 + console.info("Error while loading localization: " + error.message); 1.109 + }). 1.110 + then(function onLocalizationReady(data) { 1.111 + // Exports data to a pseudo module so that api-utils/l10n/core 1.112 + // can get access to it 1.113 + definePseudo(options.loader, '@l10n/data', data ? data : null); 1.114 + return ready; 1.115 + }).then(function() { 1.116 + run(options); 1.117 + }).then(null, console.exception); 1.118 + return void 0; // otherwise we raise a warning, see bug 910304 1.119 +} 1.120 + 1.121 +function run(options) { 1.122 + try { 1.123 + // Try initializing HTML localization before running main module. Just print 1.124 + // an exception in case of error, instead of preventing addon to be run. 1.125 + try { 1.126 + // Do not enable HTML localization while running test as it is hard to 1.127 + // disable. Because unit tests are evaluated in a another Loader who 1.128 + // doesn't have access to this current loader. 1.129 + if (options.main !== 'test-harness/run-tests') 1.130 + require('../l10n/html').enable(); 1.131 + } 1.132 + catch(error) { 1.133 + console.exception(error); 1.134 + } 1.135 + // Initialize inline options localization, without preventing addon to be 1.136 + // run in case of error 1.137 + try { 1.138 + require('../l10n/prefs'); 1.139 + } 1.140 + catch(error) { 1.141 + console.exception(error); 1.142 + } 1.143 + 1.144 + // TODO: When bug 564675 is implemented this will no longer be needed 1.145 + // Always set the default prefs, because they disappear on restart 1.146 + if (options.prefsURI) { 1.147 + // Only set if `prefsURI` specified 1.148 + setDefaultPrefs(options.prefsURI); 1.149 + } 1.150 + 1.151 + // this is where the addon's main.js finally run. 1.152 + let program = main(options.loader, options.main); 1.153 + 1.154 + if (typeof(program.onUnload) === 'function') 1.155 + unload(program.onUnload); 1.156 + 1.157 + if (typeof(program.main) === 'function') { 1.158 + 1.159 + program.main({ 1.160 + loadReason: loadReason, 1.161 + staticArgs: staticArgs 1.162 + }, { 1.163 + print: function print(_) { dump(_ + '\n') }, 1.164 + quit: exit 1.165 + }); 1.166 + } 1.167 + } catch (error) { 1.168 + console.exception(error); 1.169 + throw error; 1.170 + } 1.171 +} 1.172 +exports.startup = startup; 1.173 + 1.174 +// If add-on is lunched via `cfx run` we need to use `system.exit` to let 1.175 +// cfx know we're done (`cfx test` will take care of exit so we don't do 1.176 +// anything here). 1.177 +if (env.CFX_COMMAND === 'run') { 1.178 + unload(function(reason) { 1.179 + if (reason === 'shutdown') 1.180 + exit(0); 1.181 + }); 1.182 +}