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: michael@0: module.metadata = { michael@0: "stability": "experimental" michael@0: }; michael@0: michael@0: const { Cc, Ci } = require('chrome'); michael@0: const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader'); michael@0: const { once } = require('../system/events'); michael@0: const { exit, env, staticArgs } = require('../system'); michael@0: const { when: unload } = require('../system/unload'); michael@0: const { loadReason } = require('../self'); michael@0: const { rootURI } = require("@loader/options"); michael@0: const globals = require('../system/globals'); michael@0: const xulApp = require('../system/xul-app'); michael@0: const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. michael@0: getService(Ci.nsIAppShellService); michael@0: michael@0: const NAME2TOPIC = { michael@0: 'Firefox': 'sessionstore-windows-restored', michael@0: 'Fennec': 'sessionstore-windows-restored', michael@0: 'SeaMonkey': 'sessionstore-windows-restored', michael@0: 'Thunderbird': 'mail-startup-done' michael@0: }; michael@0: michael@0: // Set 'final-ui-startup' as default topic for unknown applications michael@0: let appStartup = 'final-ui-startup'; michael@0: michael@0: // Gets the topic that fit best as application startup event, in according with michael@0: // the current application (e.g. Firefox, Fennec, Thunderbird...) michael@0: for (let name of Object.keys(NAME2TOPIC)) { michael@0: if (xulApp.is(name)) { michael@0: appStartup = NAME2TOPIC[name]; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Initializes default preferences michael@0: function setDefaultPrefs(prefsURI) { michael@0: const prefs = Cc['@mozilla.org/preferences-service;1']. michael@0: getService(Ci.nsIPrefService). michael@0: QueryInterface(Ci.nsIPrefBranch2); michael@0: const branch = prefs.getDefaultBranch(''); michael@0: const sandbox = Sandbox({ michael@0: name: prefsURI, michael@0: prototype: { michael@0: pref: function(key, val) { michael@0: switch (typeof val) { michael@0: case 'boolean': michael@0: branch.setBoolPref(key, val); michael@0: break; michael@0: case 'number': michael@0: if (val % 1 == 0) // number must be a integer, otherwise ignore it michael@0: branch.setIntPref(key, val); michael@0: break; michael@0: case 'string': michael@0: branch.setCharPref(key, val); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: }); michael@0: // load preferences. michael@0: evaluate(sandbox, prefsURI); michael@0: } michael@0: michael@0: function definePseudo(loader, id, exports) { michael@0: let uri = resolveURI(id, loader.mapping); michael@0: loader.modules[uri] = { exports: exports }; michael@0: } michael@0: michael@0: function wait(reason, options) { michael@0: once(appStartup, function() { michael@0: startup(null, options); michael@0: }); michael@0: } michael@0: michael@0: function startup(reason, options) { michael@0: // Try accessing hidden window to guess if we are running during firefox michael@0: // startup, so that we should wait for session restore event before michael@0: // running the addon michael@0: let initialized = false; michael@0: try { michael@0: appShellService.hiddenDOMWindow; michael@0: initialized = true; michael@0: } michael@0: catch(e) {} michael@0: if (reason === 'startup' || !initialized) { michael@0: return wait(reason, options); michael@0: } michael@0: michael@0: // Inject globals ASAP in order to have console API working ASAP michael@0: Object.defineProperties(options.loader.globals, descriptor(globals)); michael@0: michael@0: // NOTE: Module is intentionally required only now because it relies michael@0: // on existence of hidden window, which does not exists until startup. michael@0: let { ready } = require('../addon/window'); michael@0: // Load localization manifest and .properties files. michael@0: // Run the addon even in case of error (best effort approach) michael@0: require('../l10n/loader'). michael@0: load(rootURI). michael@0: then(null, function failure(error) { michael@0: console.info("Error while loading localization: " + error.message); michael@0: }). michael@0: then(function onLocalizationReady(data) { michael@0: // Exports data to a pseudo module so that api-utils/l10n/core michael@0: // can get access to it michael@0: definePseudo(options.loader, '@l10n/data', data ? data : null); michael@0: return ready; michael@0: }).then(function() { michael@0: run(options); michael@0: }).then(null, console.exception); michael@0: return void 0; // otherwise we raise a warning, see bug 910304 michael@0: } michael@0: michael@0: function run(options) { michael@0: try { michael@0: // Try initializing HTML localization before running main module. Just print michael@0: // an exception in case of error, instead of preventing addon to be run. michael@0: try { michael@0: // Do not enable HTML localization while running test as it is hard to michael@0: // disable. Because unit tests are evaluated in a another Loader who michael@0: // doesn't have access to this current loader. michael@0: if (options.main !== 'test-harness/run-tests') michael@0: require('../l10n/html').enable(); michael@0: } michael@0: catch(error) { michael@0: console.exception(error); michael@0: } michael@0: // Initialize inline options localization, without preventing addon to be michael@0: // run in case of error michael@0: try { michael@0: require('../l10n/prefs'); michael@0: } michael@0: catch(error) { michael@0: console.exception(error); michael@0: } michael@0: michael@0: // TODO: When bug 564675 is implemented this will no longer be needed michael@0: // Always set the default prefs, because they disappear on restart michael@0: if (options.prefsURI) { michael@0: // Only set if `prefsURI` specified michael@0: setDefaultPrefs(options.prefsURI); michael@0: } michael@0: michael@0: // this is where the addon's main.js finally run. michael@0: let program = main(options.loader, options.main); michael@0: michael@0: if (typeof(program.onUnload) === 'function') michael@0: unload(program.onUnload); michael@0: michael@0: if (typeof(program.main) === 'function') { michael@0: michael@0: program.main({ michael@0: loadReason: loadReason, michael@0: staticArgs: staticArgs michael@0: }, { michael@0: print: function print(_) { dump(_ + '\n') }, michael@0: quit: exit michael@0: }); michael@0: } michael@0: } catch (error) { michael@0: console.exception(error); michael@0: throw error; michael@0: } michael@0: } michael@0: exports.startup = startup; michael@0: michael@0: // If add-on is lunched via `cfx run` we need to use `system.exit` to let michael@0: // cfx know we're done (`cfx test` will take care of exit so we don't do michael@0: // anything here). michael@0: if (env.CFX_COMMAND === 'run') { michael@0: unload(function(reason) { michael@0: if (reason === 'shutdown') michael@0: exit(0); michael@0: }); michael@0: }