adb-addon.js (5112B)
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 5 "use strict"; 6 7 const { AddonManager } = ChromeUtils.importESModule( 8 "resource://gre/modules/AddonManager.sys.mjs", 9 // AddonManager is a singleton, never create two instances of it. 10 { global: "shared" } 11 ); 12 const { AppConstants } = ChromeUtils.importESModule( 13 "resource://gre/modules/AppConstants.sys.mjs" 14 ); 15 const EventEmitter = require("resource://devtools/shared/event-emitter.js"); 16 17 const PREF_ADB_EXTENSION_URL = "devtools.remote.adb.extensionURL"; 18 const PREF_ADB_EXTENSION_ID = "devtools.remote.adb.extensionID"; 19 20 const ADB_ADDON_STATES = { 21 DOWNLOADING: "downloading", 22 INSTALLED: "installed", 23 INSTALLING: "installing", 24 PREPARING: "preparing", 25 UNINSTALLED: "uninstalled", 26 UNKNOWN: "unknown", 27 }; 28 exports.ADB_ADDON_STATES = ADB_ADDON_STATES; 29 30 /** 31 * Wrapper around the ADB Extension providing ADB binaries for devtools remote debugging. 32 * Fires the following events: 33 * - "update": the status of the addon was updated 34 * - "failure": addon installation failed 35 * - "progress": addon download in progress 36 * 37 * AdbAddon::state can take any of the values from ADB_ADDON_STATES. 38 */ 39 class ADBAddon extends EventEmitter { 40 constructor() { 41 super(); 42 43 this._status = ADB_ADDON_STATES.UNKNOWN; 44 45 const addonsListener = {}; 46 addonsListener.onEnabled = 47 addonsListener.onDisabled = 48 addonsListener.onInstalled = 49 addonsListener.onUninstalled = 50 () => this.updateInstallStatus(); 51 AddonManager.addAddonListener(addonsListener); 52 53 this.updateInstallStatus(); 54 } 55 56 set status(value) { 57 if (this._status != value) { 58 this._status = value; 59 this.emit("update"); 60 } 61 } 62 63 get status() { 64 return this._status; 65 } 66 67 async _getAddon() { 68 const addonId = Services.prefs.getCharPref(PREF_ADB_EXTENSION_ID); 69 return AddonManager.getAddonByID(addonId); 70 } 71 72 async updateInstallStatus() { 73 const addon = await this._getAddon(); 74 if (addon && !addon.userDisabled) { 75 this.status = ADB_ADDON_STATES.INSTALLED; 76 } else { 77 this.status = ADB_ADDON_STATES.UNINSTALLED; 78 } 79 } 80 81 /** 82 * Returns the platform specific download link for the ADB extension. 83 */ 84 _getXpiLink() { 85 let OS = ""; 86 87 switch (AppConstants.platform) { 88 case "linux": { 89 const cpuArch = Services.sysinfo.get("arch"); 90 if (cpuArch === "x86-64") { 91 OS = "linux64"; 92 } else { 93 OS = "linux"; 94 } 95 break; 96 } 97 case "macosx": 98 OS = "mac64"; 99 break; 100 case "win": 101 OS = "win32"; 102 break; 103 } 104 105 const xpiLink = Services.prefs.getCharPref(PREF_ADB_EXTENSION_URL); 106 return xpiLink.replace(/#OS#/g, OS); 107 } 108 109 /** 110 * Install and enable the adb extension. Returns a promise that resolves when ADB is 111 * enabled. 112 * 113 * @param {string} source 114 * String passed to the AddonManager for telemetry. 115 */ 116 async install(source) { 117 if (!source) { 118 throw new Error( 119 "Missing mandatory `source` parameter for adb-addon.install" 120 ); 121 } 122 123 const addon = await this._getAddon(); 124 if (addon && !addon.userDisabled) { 125 this.status = ADB_ADDON_STATES.INSTALLED; 126 return; 127 } 128 this.status = ADB_ADDON_STATES.PREPARING; 129 if (addon?.userDisabled) { 130 await addon.enable(); 131 } else { 132 const install = await AddonManager.getInstallForURL(this._getXpiLink(), { 133 telemetryInfo: { source }, 134 }); 135 install.addListener(this); 136 install.install(); 137 } 138 } 139 140 async uninstall() { 141 const addon = await this._getAddon(); 142 addon.uninstall(); 143 } 144 145 installFailureHandler(install, message) { 146 this.status = ADB_ADDON_STATES.UNINSTALLED; 147 this.emit("failure", message); 148 } 149 150 // Expected AddonManager install listener. 151 onDownloadStarted() { 152 this.status = ADB_ADDON_STATES.DOWNLOADING; 153 } 154 155 // Expected AddonManager install listener. 156 onDownloadProgress(install) { 157 if (install.maxProgress == -1) { 158 this.emit("progress", -1); 159 } else { 160 this.emit("progress", install.progress / install.maxProgress); 161 } 162 } 163 164 // Expected AddonManager install listener. 165 onDownloadCancelled(install) { 166 this.installFailureHandler(install, "Download cancelled"); 167 } 168 169 // Expected AddonManager install listener. 170 onDownloadFailed(install) { 171 this.installFailureHandler(install, "Download failed"); 172 } 173 174 // Expected AddonManager install listener. 175 onInstallStarted() { 176 this.status = ADB_ADDON_STATES.INSTALLING; 177 } 178 179 // Expected AddonManager install listener. 180 onInstallCancelled(install) { 181 this.installFailureHandler(install, "Install cancelled"); 182 } 183 184 // Expected AddonManager install listener. 185 onInstallFailed(install) { 186 this.installFailureHandler(install, "Install failed"); 187 } 188 189 // Expected AddonManager install listener. 190 onInstallEnded({ addon }) { 191 addon.enable(); 192 } 193 } 194 195 exports.adbAddon = new ADBAddon();