240 lines
6.5 KiB
JavaScript
240 lines
6.5 KiB
JavaScript
/* 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 { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
|
|
FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
|
|
NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
|
|
});
|
|
loader.lazyGetter(this, "UNPACKED_ROOT_PATH", () => {
|
|
return PathUtils.join(PathUtils.localProfileDir, "adb");
|
|
});
|
|
loader.lazyGetter(this, "EXTENSION_ID", () => {
|
|
return Services.prefs.getCharPref("devtools.remote.adb.extensionID");
|
|
});
|
|
loader.lazyGetter(this, "ADB_BINARY_PATH", () => {
|
|
let adbBinaryPath = PathUtils.join(UNPACKED_ROOT_PATH, "adb");
|
|
if (Services.appinfo.OS === "WINNT") {
|
|
adbBinaryPath += ".exe";
|
|
}
|
|
return adbBinaryPath;
|
|
});
|
|
|
|
const MANIFEST = "manifest.json";
|
|
|
|
/**
|
|
* Read contents from a given uri in the devtools-adb-extension and parse the
|
|
* contents as JSON.
|
|
*/
|
|
async function readFromExtension(fileUri) {
|
|
return new Promise(resolve => {
|
|
lazy.NetUtil.asyncFetch(
|
|
{
|
|
uri: fileUri,
|
|
loadUsingSystemPrincipal: true,
|
|
},
|
|
input => {
|
|
try {
|
|
const string = lazy.NetUtil.readInputStreamToString(
|
|
input,
|
|
input.available()
|
|
);
|
|
resolve(JSON.parse(string));
|
|
} catch (e) {
|
|
dumpn(`Could not read ${fileUri} in the extension: ${e}`);
|
|
resolve(null);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Unpack file from the extension.
|
|
* Uses NetUtil to read and write, since it's required for reading.
|
|
*
|
|
* @param {string} file
|
|
* The path name of the file in the extension.
|
|
*/
|
|
async function unpackFile(file) {
|
|
const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
|
|
if (!policy) {
|
|
return;
|
|
}
|
|
|
|
// Assumes that destination dir already exists.
|
|
const basePath = file.substring(file.lastIndexOf("/") + 1);
|
|
const filePath = PathUtils.join(UNPACKED_ROOT_PATH, basePath);
|
|
await new Promise((resolve, reject) => {
|
|
lazy.NetUtil.asyncFetch(
|
|
{
|
|
uri: policy.getURL(file),
|
|
loadUsingSystemPrincipal: true,
|
|
},
|
|
input => {
|
|
try {
|
|
// Since we have to use NetUtil to read, probably it's okay to use for
|
|
// writing, rather than bouncing to IOUtils...?
|
|
const outputFile = new lazy.FileUtils.File(filePath);
|
|
const output = lazy.FileUtils.openAtomicFileOutputStream(outputFile);
|
|
lazy.NetUtil.asyncCopy(input, output, resolve);
|
|
} catch (e) {
|
|
dumpn(`Could not unpack file ${file} in the extension: ${e}`);
|
|
reject(e);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
// Mark binaries as executable.
|
|
await IOUtils.setPermissions(filePath, 0o744);
|
|
}
|
|
|
|
/**
|
|
* Extract files in the extension into local profile directory and returns
|
|
* if it fails.
|
|
*/
|
|
async function extractFiles() {
|
|
const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
|
|
if (!policy) {
|
|
return false;
|
|
}
|
|
const uri = policy.getURL("adb.json");
|
|
const adbInfo = await readFromExtension(uri);
|
|
if (!adbInfo) {
|
|
return false;
|
|
}
|
|
|
|
let filesForAdb;
|
|
try {
|
|
// The adbInfo is an object looks like this;
|
|
//
|
|
// {
|
|
// "Linux": {
|
|
// "x86": [
|
|
// "linux/adb"
|
|
// ],
|
|
// "x86_64": [
|
|
// "linux64/adb"
|
|
// ]
|
|
// },
|
|
// ...
|
|
|
|
// XPCOMABI looks this; x86_64-gcc3, so drop the compiler name.
|
|
let architecture = Services.appinfo.XPCOMABI.split("-")[0];
|
|
if (architecture === "aarch64") {
|
|
// Fallback on x86 or x86_64 binaries for aarch64 - See Bug 1522149
|
|
const hasX86Binary = !!adbInfo[Services.appinfo.OS].x86;
|
|
architecture = hasX86Binary ? "x86" : "x86_64";
|
|
}
|
|
filesForAdb = adbInfo[Services.appinfo.OS][architecture];
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
|
|
// manifest.json isn't in adb.json but has to be unpacked for version
|
|
// comparison
|
|
filesForAdb.push(MANIFEST);
|
|
|
|
await IOUtils.makeDirectory(UNPACKED_ROOT_PATH);
|
|
|
|
for (const file of filesForAdb) {
|
|
try {
|
|
await unpackFile(file);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Read the manifest from inside the devtools-adb-extension.
|
|
* Uses NetUtil since data is packed inside the extension, not a local file.
|
|
*/
|
|
async function getManifestFromExtension() {
|
|
const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
|
|
if (!policy) {
|
|
return null;
|
|
}
|
|
|
|
const manifestUri = policy.getURL(MANIFEST);
|
|
return readFromExtension(manifestUri);
|
|
}
|
|
|
|
/**
|
|
* Returns whether manifest.json has already been unpacked.
|
|
*/
|
|
async function isManifestUnpacked() {
|
|
const manifestPath = PathUtils.join(UNPACKED_ROOT_PATH, MANIFEST);
|
|
return IOUtils.exists(manifestPath);
|
|
}
|
|
|
|
/**
|
|
* Read the manifest from the unpacked binary directory.
|
|
* Uses IOUtils since this is a local file.
|
|
*/
|
|
async function getManifestFromUnpacked() {
|
|
if (!(await isManifestUnpacked())) {
|
|
throw new Error("Manifest doesn't exist at unpacked path");
|
|
}
|
|
|
|
const manifestPath = PathUtils.join(UNPACKED_ROOT_PATH, MANIFEST);
|
|
const binary = await IOUtils.read(manifestPath);
|
|
const json = new TextDecoder().decode(binary);
|
|
let data;
|
|
try {
|
|
data = JSON.parse(json);
|
|
} catch (e) {}
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Check state of binary unpacking, including the location and manifest.
|
|
*/
|
|
async function isUnpacked() {
|
|
if (!(await isManifestUnpacked())) {
|
|
dumpn("Needs unpacking, no manifest found");
|
|
return false;
|
|
}
|
|
|
|
const manifestInExtension = await getManifestFromExtension();
|
|
const unpackedManifest = await getManifestFromUnpacked();
|
|
if (manifestInExtension.version != unpackedManifest.version) {
|
|
dumpn(
|
|
`Needs unpacking, extension version ${manifestInExtension.version} != ` +
|
|
`unpacked version ${unpackedManifest.version}`
|
|
);
|
|
return false;
|
|
}
|
|
dumpn("Already unpacked");
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get a file object for the adb binary from the 'adb@mozilla.org' extension
|
|
* which has been already installed.
|
|
*
|
|
* @return {nsIFile}
|
|
* File object for the binary.
|
|
*/
|
|
async function getFileForBinary() {
|
|
if (!(await isUnpacked()) && !(await extractFiles())) {
|
|
return null;
|
|
}
|
|
|
|
const file = new lazy.FileUtils.File(ADB_BINARY_PATH);
|
|
if (!file.exists()) {
|
|
return null;
|
|
}
|
|
return file;
|
|
}
|
|
|
|
exports.getFileForBinary = getFileForBinary;
|