Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { AddonManager } = ChromeUtils.importESModule(
"resource://gre/modules/AddonManager.sys.mjs",
// AddonManager is a singleton, never create two instances of it.
{ global: "shared" }
);
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const PREF_ADB_EXTENSION_URL = "devtools.remote.adb.extensionURL";
const PREF_ADB_EXTENSION_ID = "devtools.remote.adb.extensionID";
const ADB_ADDON_STATES = {
DOWNLOADING: "downloading",
INSTALLED: "installed",
INSTALLING: "installing",
PREPARING: "preparing",
UNINSTALLED: "uninstalled",
UNKNOWN: "unknown",
};
exports.ADB_ADDON_STATES = ADB_ADDON_STATES;
/**
* Wrapper around the ADB Extension providing ADB binaries for devtools remote debugging.
* Fires the following events:
* - "update": the status of the addon was updated
* - "failure": addon installation failed
* - "progress": addon download in progress
*
* AdbAddon::state can take any of the values from ADB_ADDON_STATES.
*/
class ADBAddon extends EventEmitter {
constructor() {
super();
this._status = ADB_ADDON_STATES.UNKNOWN;
const addonsListener = {};
addonsListener.onEnabled =
addonsListener.onDisabled =
addonsListener.onInstalled =
addonsListener.onUninstalled =
() => this.updateInstallStatus();
AddonManager.addAddonListener(addonsListener);
this.updateInstallStatus();
}
set status(value) {
if (this._status != value) {
this._status = value;
this.emit("update");
}
}
get status() {
return this._status;
}
async _getAddon() {
const addonId = Services.prefs.getCharPref(PREF_ADB_EXTENSION_ID);
return AddonManager.getAddonByID(addonId);
}
async updateInstallStatus() {
const addon = await this._getAddon();
if (addon && !addon.userDisabled) {
this.status = ADB_ADDON_STATES.INSTALLED;
} else {
this.status = ADB_ADDON_STATES.UNINSTALLED;
}
}
/**
* Returns the platform specific download link for the ADB extension.
*/
_getXpiLink() {
let OS = "";
switch (AppConstants.platform) {
case "linux": {
const cpuArch = Services.sysinfo.get("arch");
if (cpuArch === "x86-64") {
OS = "linux64";
} else {
OS = "linux";
}
break;
}
case "macosx":
OS = "mac64";
break;
case "win":
OS = "win32";
break;
}
const xpiLink = Services.prefs.getCharPref(PREF_ADB_EXTENSION_URL);
return xpiLink.replace(/#OS#/g, OS);
}
/**
* Install and enable the adb extension. Returns a promise that resolves when ADB is
* enabled.
*
* @param {String} source
* String passed to the AddonManager for telemetry.
*/
async install(source) {
if (!source) {
throw new Error(
"Missing mandatory `source` parameter for adb-addon.install"
);
}
const addon = await this._getAddon();
if (addon && !addon.userDisabled) {
this.status = ADB_ADDON_STATES.INSTALLED;
return;
}
this.status = ADB_ADDON_STATES.PREPARING;
if (addon?.userDisabled) {
await addon.enable();
} else {
const install = await AddonManager.getInstallForURL(this._getXpiLink(), {
telemetryInfo: { source },
});
install.addListener(this);
install.install();
}
}
async uninstall() {
const addon = await this._getAddon();
addon.uninstall();
}
installFailureHandler(install, message) {
this.status = ADB_ADDON_STATES.UNINSTALLED;
this.emit("failure", message);
}
// Expected AddonManager install listener.
onDownloadStarted() {
this.status = ADB_ADDON_STATES.DOWNLOADING;
}
// Expected AddonManager install listener.
onDownloadProgress(install) {
if (install.maxProgress == -1) {
this.emit("progress", -1);
} else {
this.emit("progress", install.progress / install.maxProgress);
}
}
// Expected AddonManager install listener.
onDownloadCancelled(install) {
this.installFailureHandler(install, "Download cancelled");
}
// Expected AddonManager install listener.
onDownloadFailed(install) {
this.installFailureHandler(install, "Download failed");
}
// Expected AddonManager install listener.
onInstallStarted() {
this.status = ADB_ADDON_STATES.INSTALLING;
}
// Expected AddonManager install listener.
onInstallCancelled(install) {
this.installFailureHandler(install, "Install cancelled");
}
// Expected AddonManager install listener.
onInstallFailed(install) {
this.installFailureHandler(install, "Install failed");
}
// Expected AddonManager install listener.
onInstallEnded({ addon }) {
addon.enable();
}
}
exports.adbAddon = new ADBAddon();