runner.js 5.6 KB

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