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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
9 FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
10 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
13 // from https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager/AddonManager#AddonInstall_errors
15 [-1]: "ERROR_NETWORK_FAILURE: A network error occured.",
16 [-2]: "ERROR_INCORRECT_HASH: The downloaded file did not match the expected hash.",
17 [-3]: "ERROR_CORRUPT_FILE: The file appears to be corrupt.",
18 [-4]: "ERROR_FILE_ACCESS: There was an error accessing the filesystem.",
19 [-5]: "ERROR_SIGNEDSTATE_REQUIRED: The addon must be signed and isn't.",
20 [-6]: "ERROR_UNEXPECTED_ADDON_TYPE: The downloaded add-on had a different type than expected (during an update).",
21 [-7]: "ERROR_INCORRECT_ID: The addon did not have the expected ID (during an update).",
22 [-8]: "ERROR_INVALID_DOMAIN: The addon install_origins does not list the 3rd party domain.",
23 [-9]: "ERROR_UNEXPECTED_ADDON_VERSION: The downloaded add-on had a different version than expected (during an update).",
24 [-10]: "ERROR_BLOCKLISTED: The add-on is blocklisted.",
26 "ERROR_INCOMPATIBLE: The add-on is incompatible (w.r.t. the compatibility range).",
29 async function installAddon(file) {
30 let install = await lazy.AddonManager.getInstallForFile(file, null, {
35 throw new lazy.error.UnknownError(ERRORS[install.error]);
38 return install.install().catch(err => {
39 throw new lazy.error.UnknownError(ERRORS[install.error]);
43 /** Installs addons by path and uninstalls by ID. */
46 * Install a Firefox addon.
48 * If the addon is restartless, it can be used right away. Otherwise a
49 * restart is required.
51 * Temporary addons will automatically be uninstalled on shutdown and
52 * do not need to be signed, though they must be restartless.
54 * @param {string} path
55 * Full path to the extension package archive.
56 * @param {boolean=} temporary
57 * True to install the addon temporarily, false (default) otherwise.
59 * @returns {Promise.<string>}
62 * @throws {UnknownError}
63 * If there is a problem installing the addon.
65 static async install(path, temporary = false) {
70 file = new lazy.FileUtils.File(path);
72 throw new lazy.error.UnknownError(`Expected absolute path: ${e}`, e);
76 throw new lazy.error.UnknownError(`No such file or directory: ${path}`);
81 addon = await lazy.AddonManager.installTemporaryAddon(file);
83 addon = await installAddon(file);
86 throw new lazy.error.UnknownError(
87 `Could not install add-on: ${path}: ${e.message}`,
96 * Uninstall a Firefox addon.
98 * If the addon is restartless it will be uninstalled right away.
99 * Otherwise, Firefox must be restarted for the change to take effect.
102 * ID of the addon to uninstall.
106 * @throws {UnknownError}
107 * If there is a problem uninstalling the addon.
109 static async uninstall(id) {
110 let candidate = await lazy.AddonManager.getAddonByID(id);
111 if (candidate === null) {
112 // `AddonManager.getAddonByID` never rejects but instead
113 // returns `null` if the requested addon cannot be found.
114 throw new lazy.error.UnknownError(`Addon ${id} is not installed`);
117 return new Promise(resolve => {
119 onOperationCancelled: addon => {
120 if (addon.id === candidate.id) {
121 lazy.AddonManager.removeAddonListener(listener);
122 throw new lazy.error.UnknownError(
123 `Uninstall of ${candidate.id} has been canceled`
128 onUninstalled: addon => {
129 if (addon.id === candidate.id) {
130 lazy.AddonManager.removeAddonListener(listener);
136 lazy.AddonManager.addAddonListener(listener);
137 candidate.uninstall();