installer.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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, Cu } = require("chrome");
  8. const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm");
  9. const { defer } = require("../core/promise");
  10. const { setTimeout } = require("../timers");
  11. /**
  12. * `install` method error codes:
  13. *
  14. * https://developer.mozilla.org/en/Addons/Add-on_Manager/AddonManager#AddonInstall_errors
  15. */
  16. exports.ERROR_NETWORK_FAILURE = AddonManager.ERROR_NETWORK_FAILURE;
  17. exports.ERROR_INCORRECT_HASH = AddonManager.ERROR_INCORRECT_HASH;
  18. exports.ERROR_CORRUPT_FILE = AddonManager.ERROR_CORRUPT_FILE;
  19. exports.ERROR_FILE_ACCESS = AddonManager.ERROR_FILE_ACCESS;
  20. /**
  21. * Immediatly install an addon.
  22. *
  23. * @param {String} xpiPath
  24. * file path to an xpi file to install
  25. * @return {Promise}
  26. * A promise resolved when the addon is finally installed.
  27. * Resolved with addon id as value or rejected with an error code.
  28. */
  29. exports.install = function install(xpiPath) {
  30. let { promise, resolve, reject } = defer();
  31. // Create nsIFile for the xpi file
  32. let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
  33. try {
  34. file.initWithPath(xpiPath);
  35. }
  36. catch(e) {
  37. reject(exports.ERROR_FILE_ACCESS);
  38. return promise;
  39. }
  40. // Listen for installation end
  41. let listener = {
  42. onInstallEnded: function(aInstall, aAddon) {
  43. aInstall.removeListener(listener);
  44. // Bug 749745: on FF14+, onInstallEnded is called just before `startup()`
  45. // is called, but we expect to resolve the promise only after it.
  46. // As startup is called synchronously just after onInstallEnded,
  47. // a simple setTimeout(0) is enough
  48. setTimeout(resolve, 0, aAddon.id);
  49. },
  50. onInstallFailed: function (aInstall) {
  51. console.log("failed");
  52. aInstall.removeListener(listener);
  53. reject(aInstall.error);
  54. },
  55. onDownloadFailed: function(aInstall) {
  56. this.onInstallFailed(aInstall);
  57. }
  58. };
  59. // Order AddonManager to install the addon
  60. AddonManager.getInstallForFile(file, function(install) {
  61. install.addListener(listener);
  62. install.install();
  63. });
  64. return promise;
  65. };
  66. exports.uninstall = function uninstall(addonId) {
  67. let { promise, resolve, reject } = defer();
  68. // Listen for uninstallation end
  69. let listener = {
  70. onUninstalled: function onUninstalled(aAddon) {
  71. if (aAddon.id != addonId)
  72. return;
  73. AddonManager.removeAddonListener(listener);
  74. resolve();
  75. }
  76. };
  77. AddonManager.addAddonListener(listener);
  78. // Order Addonmanager to uninstall the addon
  79. getAddon(addonId).then(addon => addon.uninstall(), reject);
  80. return promise;
  81. };
  82. exports.disable = function disable(addonId) {
  83. return getAddon(addonId).then(addon => {
  84. addon.userDisabled = true;
  85. return addonId;
  86. });
  87. };
  88. exports.enable = function enabled(addonId) {
  89. return getAddon(addonId).then(addon => {
  90. addon.userDisabled = false;
  91. return addonId;
  92. });
  93. };
  94. exports.isActive = function isActive(addonId) {
  95. return getAddon(addonId).then(addon => addon.isActive && !addon.appDisabled);
  96. };
  97. function getAddon (id) {
  98. let { promise, resolve, reject } = defer();
  99. AddonManager.getAddonByID(id, addon => addon ? resolve(addon) : reject());
  100. return promise;
  101. }