tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

version-checker.js (6038B)


      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 { AppConstants } = ChromeUtils.importESModule(
      8  "resource://gre/modules/AppConstants.sys.mjs"
      9 );
     10 
     11 const MS_PER_DAY = 1000 * 60 * 60 * 24;
     12 
     13 const COMPATIBILITY_STATUS = {
     14  COMPATIBLE: "compatible",
     15  TOO_OLD: "too-old",
     16  TOO_OLD_FENNEC: "too-old-fennec",
     17  TOO_RECENT: "too-recent",
     18 };
     19 exports.COMPATIBILITY_STATUS = COMPATIBILITY_STATUS;
     20 
     21 function getDateFromBuildID(buildID) {
     22  // Build IDs are a timestamp in the yyyyMMddHHmmss format.
     23  // Extract the year, month and day information.
     24  const fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
     25  // Date expects 0 - 11 for months
     26  const month = Number.parseInt(fields[2], 10) - 1;
     27  return new Date(fields[1], month, fields[3]);
     28 }
     29 
     30 function getMajorVersion(platformVersion) {
     31  // Retrieve the major platform version, i.e. if we are on Firefox 64.0a1, it will be 64.
     32  return Number.parseInt(platformVersion.match(/\d+/)[0], 10);
     33 }
     34 
     35 /**
     36 * Compute the minimum and maximum supported version for remote debugging for the provided
     37 * version of Firefox. Backward compatibility policy for devtools supports at most 2
     38 * versions older than the current version.
     39 *
     40 * @param {string} localVersion
     41 *        The version of the local Firefox instance, eg "67.0"
     42 * @return {object}
     43 *         - minVersion {String} the minimum supported version, eg "65.0a1"
     44 *         - maxVersion {String} the first unsupported version, eg "68.0a1"
     45 */
     46 function computeMinMaxVersion(localVersion) {
     47  // Retrieve the major platform version, i.e. if we are on Firefox 64.0a1, it will be 64.
     48  const localMajorVersion = getMajorVersion(localVersion);
     49 
     50  return {
     51    // Define the minimum officially supported version of Firefox when connecting to a
     52    // remote runtime. (Use ".0a1" to support the very first nightly version)
     53    // This matches the release channel's version when we are on nightly,
     54    // or 2 versions before when we are on other channels.
     55    minVersion: localMajorVersion - 2 + ".0a1",
     56    // The maximum version is the first excluded from the support range. That's why we
     57    // increase the current version by 1 and use ".0a1" to point to the first Nightly.
     58    // We do not support forward compatibility at all.
     59    maxVersion: localMajorVersion + 1 + ".0a1",
     60  };
     61 }
     62 
     63 /**
     64 * Tells if the remote device is using a supported version of Firefox.
     65 *
     66 * @param {DevToolsClient} devToolsClient
     67 *        DevToolsClient instance connected to the target remote Firefox.
     68 * @return Object with the following attributes:
     69 *   * String status, one of COMPATIBILITY_STATUS
     70 *            COMPATIBLE if the runtime is compatible,
     71 *            TOO_RECENT if the runtime uses a too recent version,
     72 *            TOO_OLD if the runtime uses a too old version.
     73 *   * String minVersion
     74 *            The minimum supported version.
     75 *   * String runtimeVersion
     76 *            The remote runtime version.
     77 *   * String localID
     78 *            Build ID of local runtime. A date with like this: YYYYMMDD.
     79 *   * String deviceID
     80 *            Build ID of remote runtime. A date with like this: YYYYMMDD.
     81 */
     82 async function checkVersionCompatibility(devToolsClient) {
     83  const localDescription = {
     84    appbuildid: Services.appinfo.appBuildID,
     85    platformversion: AppConstants.MOZ_APP_VERSION,
     86  };
     87 
     88  try {
     89    const deviceFront = await devToolsClient.mainRoot.getFront("device");
     90    const description = await deviceFront.getDescription();
     91    return _compareVersionCompatibility(localDescription, description);
     92  } catch (e) {
     93    // If we failed to retrieve the device description, assume we are trying to connect to
     94    // a really old version of Firefox.
     95    const localVersion = localDescription.platformversion;
     96    const { minVersion } = computeMinMaxVersion(localVersion);
     97    return {
     98      minVersion,
     99      runtimeVersion: "<55",
    100      status: COMPATIBILITY_STATUS.TOO_OLD,
    101    };
    102  }
    103 }
    104 exports.checkVersionCompatibility = checkVersionCompatibility;
    105 
    106 function _compareVersionCompatibility(localDescription, deviceDescription) {
    107  const runtimeID = deviceDescription.appbuildid.substr(0, 8);
    108  const localID = localDescription.appbuildid.substr(0, 8);
    109 
    110  const runtimeDate = getDateFromBuildID(runtimeID);
    111  const localDate = getDateFromBuildID(localID);
    112 
    113  const runtimeVersion = deviceDescription.platformversion;
    114  const localVersion = localDescription.platformversion;
    115 
    116  const { minVersion, maxVersion } = computeMinMaxVersion(localVersion);
    117  const isTooOld = Services.vc.compare(runtimeVersion, minVersion) < 0;
    118  const isTooRecent = Services.vc.compare(runtimeVersion, maxVersion) >= 0;
    119 
    120  const runtimeMajorVersion = getMajorVersion(runtimeVersion);
    121  const localMajorVersion = getMajorVersion(localVersion);
    122  const isSameMajorVersion = runtimeMajorVersion === localMajorVersion;
    123 
    124  let status;
    125  if (isTooOld) {
    126    if (runtimeMajorVersion === 68 && deviceDescription.os === "Android") {
    127      status = COMPATIBILITY_STATUS.TOO_OLD_FENNEC;
    128    } else {
    129      status = COMPATIBILITY_STATUS.TOO_OLD;
    130    }
    131  } else if (isTooRecent) {
    132    status = COMPATIBILITY_STATUS.TOO_RECENT;
    133  } else if (isSameMajorVersion && runtimeDate - localDate > 7 * MS_PER_DAY) {
    134    // If both local and remote runtimes have the same major version, compare build dates.
    135    // This check is useful for Gecko developers as we might introduce breaking changes
    136    // within a Nightly cycle.
    137    // Still allow devices to be newer by up to a week. This accommodates those with local
    138    // device builds, since their devices will almost always be newer than the client.
    139    status = COMPATIBILITY_STATUS.TOO_RECENT;
    140  } else {
    141    status = COMPATIBILITY_STATUS.COMPATIBLE;
    142  }
    143 
    144  return {
    145    localID,
    146    localVersion,
    147    minVersion,
    148    runtimeID,
    149    runtimeVersion,
    150    status,
    151  };
    152 }
    153 // Exported for tests.
    154 exports._compareVersionCompatibility = _compareVersionCompatibility;