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;