summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/remote-debugging/version-checker.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/remote-debugging/version-checker.js')
-rw-r--r--devtools/client/shared/remote-debugging/version-checker.js154
1 files changed, 154 insertions, 0 deletions
diff --git a/devtools/client/shared/remote-debugging/version-checker.js b/devtools/client/shared/remote-debugging/version-checker.js
new file mode 100644
index 0000000000..7023a5bcd3
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/version-checker.js
@@ -0,0 +1,154 @@
+/* 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 { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+const MS_PER_DAY = 1000 * 60 * 60 * 24;
+
+const COMPATIBILITY_STATUS = {
+ COMPATIBLE: "compatible",
+ TOO_OLD: "too-old",
+ TOO_OLD_FENNEC: "too-old-fennec",
+ TOO_RECENT: "too-recent",
+};
+exports.COMPATIBILITY_STATUS = COMPATIBILITY_STATUS;
+
+function getDateFromBuildID(buildID) {
+ // Build IDs are a timestamp in the yyyyMMddHHmmss format.
+ // Extract the year, month and day information.
+ const fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
+ // Date expects 0 - 11 for months
+ const month = Number.parseInt(fields[2], 10) - 1;
+ return new Date(fields[1], month, fields[3]);
+}
+
+function getMajorVersion(platformVersion) {
+ // Retrieve the major platform version, i.e. if we are on Firefox 64.0a1, it will be 64.
+ return Number.parseInt(platformVersion.match(/\d+/)[0], 10);
+}
+
+/**
+ * Compute the minimum and maximum supported version for remote debugging for the provided
+ * version of Firefox. Backward compatibility policy for devtools supports at most 2
+ * versions older than the current version.
+ *
+ * @param {String} localVersion
+ * The version of the local Firefox instance, eg "67.0"
+ * @return {Object}
+ * - minVersion {String} the minimum supported version, eg "65.0a1"
+ * - maxVersion {String} the first unsupported version, eg "68.0a1"
+ */
+function computeMinMaxVersion(localVersion) {
+ // Retrieve the major platform version, i.e. if we are on Firefox 64.0a1, it will be 64.
+ const localMajorVersion = getMajorVersion(localVersion);
+
+ return {
+ // Define the minimum officially supported version of Firefox when connecting to a
+ // remote runtime. (Use ".0a1" to support the very first nightly version)
+ // This matches the release channel's version when we are on nightly,
+ // or 2 versions before when we are on other channels.
+ minVersion: localMajorVersion - 2 + ".0a1",
+ // The maximum version is the first excluded from the support range. That's why we
+ // increase the current version by 1 and use ".0a1" to point to the first Nightly.
+ // We do not support forward compatibility at all.
+ maxVersion: localMajorVersion + 1 + ".0a1",
+ };
+}
+
+/**
+ * Tells if the remote device is using a supported version of Firefox.
+ *
+ * @param {DevToolsClient} devToolsClient
+ * DevToolsClient instance connected to the target remote Firefox.
+ * @return Object with the following attributes:
+ * * String status, one of COMPATIBILITY_STATUS
+ * COMPATIBLE if the runtime is compatible,
+ * TOO_RECENT if the runtime uses a too recent version,
+ * TOO_OLD if the runtime uses a too old version.
+ * * String minVersion
+ * The minimum supported version.
+ * * String runtimeVersion
+ * The remote runtime version.
+ * * String localID
+ * Build ID of local runtime. A date with like this: YYYYMMDD.
+ * * String deviceID
+ * Build ID of remote runtime. A date with like this: YYYYMMDD.
+ */
+async function checkVersionCompatibility(devToolsClient) {
+ const localDescription = {
+ appbuildid: Services.appinfo.appBuildID,
+ platformversion: AppConstants.MOZ_APP_VERSION,
+ };
+
+ try {
+ const deviceFront = await devToolsClient.mainRoot.getFront("device");
+ const description = await deviceFront.getDescription();
+ return _compareVersionCompatibility(localDescription, description);
+ } catch (e) {
+ // If we failed to retrieve the device description, assume we are trying to connect to
+ // a really old version of Firefox.
+ const localVersion = localDescription.platformversion;
+ const { minVersion } = computeMinMaxVersion(localVersion);
+ return {
+ minVersion,
+ runtimeVersion: "<55",
+ status: COMPATIBILITY_STATUS.TOO_OLD,
+ };
+ }
+}
+exports.checkVersionCompatibility = checkVersionCompatibility;
+
+function _compareVersionCompatibility(localDescription, deviceDescription) {
+ const runtimeID = deviceDescription.appbuildid.substr(0, 8);
+ const localID = localDescription.appbuildid.substr(0, 8);
+
+ const runtimeDate = getDateFromBuildID(runtimeID);
+ const localDate = getDateFromBuildID(localID);
+
+ const runtimeVersion = deviceDescription.platformversion;
+ const localVersion = localDescription.platformversion;
+
+ const { minVersion, maxVersion } = computeMinMaxVersion(localVersion);
+ const isTooOld = Services.vc.compare(runtimeVersion, minVersion) < 0;
+ const isTooRecent = Services.vc.compare(runtimeVersion, maxVersion) >= 0;
+
+ const runtimeMajorVersion = getMajorVersion(runtimeVersion);
+ const localMajorVersion = getMajorVersion(localVersion);
+ const isSameMajorVersion = runtimeMajorVersion === localMajorVersion;
+
+ let status;
+ if (isTooOld) {
+ if (runtimeMajorVersion === 68 && deviceDescription.os === "Android") {
+ status = COMPATIBILITY_STATUS.TOO_OLD_FENNEC;
+ } else {
+ status = COMPATIBILITY_STATUS.TOO_OLD;
+ }
+ } else if (isTooRecent) {
+ status = COMPATIBILITY_STATUS.TOO_RECENT;
+ } else if (isSameMajorVersion && runtimeDate - localDate > 7 * MS_PER_DAY) {
+ // If both local and remote runtimes have the same major version, compare build dates.
+ // This check is useful for Gecko developers as we might introduce breaking changes
+ // within a Nightly cycle.
+ // Still allow devices to be newer by up to a week. This accommodates those with local
+ // device builds, since their devices will almost always be newer than the client.
+ status = COMPATIBILITY_STATUS.TOO_RECENT;
+ } else {
+ status = COMPATIBILITY_STATUS.COMPATIBLE;
+ }
+
+ return {
+ localID,
+ localVersion,
+ minVersion,
+ runtimeID,
+ runtimeVersion,
+ status,
+ };
+}
+// Exported for tests.
+exports._compareVersionCompatibility = _compareVersionCompatibility;