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 5var EXPORTED_SYMBOLS = ["UpdateUtils"]; 6 7ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); 8ChromeUtils.import("resource://gre/modules/Services.jsm"); 9ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); 10ChromeUtils.import("resource://gre/modules/ctypes.jsm"); 11Cu.importGlobalProperties(["fetch"]); /* globals fetch */ 12 13ChromeUtils.defineModuleGetter(this, "WindowsRegistry", 14 "resource://gre/modules/WindowsRegistry.jsm"); 15 16const FILE_UPDATE_LOCALE = "update.locale"; 17const PREF_APP_DISTRIBUTION = "distribution.id"; 18const PREF_APP_DISTRIBUTION_VERSION = "distribution.version"; 19const PREF_APP_UPDATE_CUSTOM = "app.update.custom"; 20 21/** 22 * Pref that stores the time of the last use of OLDJAWS screen reader. 23 */ 24const PREF_ACCESSIBILITY_CLIENTS_OLDJAWS_TIMESTAMP = "accessibility.clients.oldjaws.timestamp"; 25 26const TIMESPAN_WEEK = 7 * 24 * 60 * 60 * 1000; 27 28var UpdateUtils = { 29 _locale: undefined, 30 31 /** 32 * Read the update channel from defaults only. We do this to ensure that 33 * the channel is tightly coupled with the application and does not apply 34 * to other instances of the application that may use the same profile. 35 * 36 * @param [optional] aIncludePartners 37 * Whether or not to include the partner bits. Default: true. 38 */ 39 getUpdateChannel(aIncludePartners = true) { 40 let defaults = Services.prefs.getDefaultBranch(null); 41 let channel = defaults.getCharPref("app.update.channel", 42 AppConstants.MOZ_UPDATE_CHANNEL); 43 44 if (aIncludePartners) { 45 try { 46 let partners = Services.prefs.getChildList("app.partner.").sort(); 47 if (partners.length) { 48 channel += "-cck"; 49 partners.forEach(function(prefName) { 50 channel += "-" + Services.prefs.getCharPref(prefName); 51 }); 52 } 53 } catch (e) { 54 Cu.reportError(e); 55 } 56 } 57 58 return channel; 59 }, 60 61 get UpdateChannel() { 62 return this.getUpdateChannel(); 63 }, 64 65 /** 66 * Formats a URL by replacing %...% values with OS, build and locale specific 67 * values. 68 * 69 * @param url 70 * The URL to format. 71 * @return The formatted URL. 72 */ 73 async formatUpdateURL(url) { 74 const locale = await this.getLocale(); 75 76 return url.replace(/%(\w+)%/g, (match, name) => { 77 switch (name) { 78 case "PRODUCT": 79 return Services.appinfo.name; 80 case "VERSION": 81 return Services.appinfo.version; 82 case "BUILD_ID": 83 return Services.appinfo.appBuildID; 84 case "BUILD_TARGET": 85 return Services.appinfo.OS + "_" + this.ABI; 86 case "OS_VERSION": 87 return this.OSVersion; 88 case "LOCALE": 89 return locale; 90 case "CHANNEL": 91 return this.UpdateChannel; 92 case "PLATFORM_VERSION": 93 return Services.appinfo.platformVersion; 94 case "SYSTEM_CAPABILITIES": 95 return getSystemCapabilities(); 96 case "CUSTOM": 97 return Services.prefs.getStringPref(PREF_APP_UPDATE_CUSTOM, ""); 98 case "DISTRIBUTION": 99 return getDistributionPrefValue(PREF_APP_DISTRIBUTION); 100 case "DISTRIBUTION_VERSION": 101 return getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION); 102 } 103 return match; 104 }).replace(/\+/g, "%2B"); 105 }, 106 107 /** 108 * Gets the locale from the update.locale file for replacing %LOCALE% in the 109 * update url. The update.locale file can be located in the application 110 * directory or the GRE directory with preference given to it being located in 111 * the application directory. 112 */ 113 async getLocale() { 114 if (this._locale !== undefined) { 115 return this._locale; 116 } 117 118 for (let res of ["app", "gre"]) { 119 const url = "resource://" + res + "/" + FILE_UPDATE_LOCALE; 120 let data; 121 try { 122 data = await fetch(url); 123 } catch (e) { 124 continue; 125 } 126 const locale = await data.text(); 127 if (locale) { 128 return this._locale = locale.trim(); 129 } 130 } 131 132 Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " + 133 "application or GRE directories"); 134 135 return this._locale = null; 136 } 137}; 138 139/* Get the distribution pref values, from defaults only */ 140function getDistributionPrefValue(aPrefName) { 141 return Services.prefs.getDefaultBranch(null).getCharPref(aPrefName, "default"); 142} 143 144function getSystemCapabilities() { 145 return "ISET:" + gInstructionSet + ",MEM:" + getMemoryMB() + getJAWS(); 146} 147 148/** 149 * Gets the appropriate update url string for whether a JAWS screen reader that 150 * is incompatible with e10s is or was recently present on Windows. For 151 * platforms other than Windows this returns an empty string which is easier for 152 * balrog to detect. 153 */ 154function getJAWS() { 155 if (AppConstants.platform != "win") { 156 return ""; 157 } 158 159 let oldjaws = false; 160 if (Services.appinfo.shouldBlockIncompatJaws) { 161 // User is using old JAWS screen reader right now. 162 oldjaws = true; 163 } else { 164 // We stored seconds in order to fit within int prefs. 165 const timestamp = Services.prefs.getIntPref( 166 PREF_ACCESSIBILITY_CLIENTS_OLDJAWS_TIMESTAMP, 0) * 1000; 167 // User was using old JAWS screen reader less than a week ago. 168 oldjaws = Date.now() - timestamp < TIMESPAN_WEEK; 169 } 170 171 return ",JAWS:" + (oldjaws ? "1" : "0"); 172} 173 174/** 175 * Gets the RAM size in megabytes. This will round the value because sysinfo 176 * doesn't always provide RAM in multiples of 1024. 177 */ 178function getMemoryMB() { 179 let memoryMB = "unknown"; 180 try { 181 memoryMB = Services.sysinfo.getProperty("memsize"); 182 if (memoryMB) { 183 memoryMB = Math.round(memoryMB / 1024 / 1024); 184 } 185 } catch (e) { 186 Cu.reportError("Error getting system info memsize property. " + 187 "Exception: " + e); 188 } 189 return memoryMB; 190} 191 192/** 193 * Gets the supported CPU instruction set. 194 */ 195XPCOMUtils.defineLazyGetter(this, "gInstructionSet", function aus_gIS() { 196 const CPU_EXTENSIONS = ["hasSSE4_2", "hasSSE4_1", "hasSSE4A", "hasSSSE3", 197 "hasSSE3", "hasSSE2", "hasSSE", "hasMMX", 198 "hasNEON", "hasARMv7", "hasARMv6"]; 199 for (let ext of CPU_EXTENSIONS) { 200 if (Services.sysinfo.getProperty(ext)) { 201 return ext.substring(3); 202 } 203 } 204 205 return "unknown"; 206}); 207 208/* Windows only getter that returns the processor architecture. */ 209XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() { 210 // Get processor architecture 211 let arch = "unknown"; 212 213 const WORD = ctypes.uint16_t; 214 const DWORD = ctypes.uint32_t; 215 216 // This structure is described at: 217 // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx 218 const SYSTEM_INFO = new ctypes.StructType("SYSTEM_INFO", 219 [ 220 {wProcessorArchitecture: WORD}, 221 {wReserved: WORD}, 222 {dwPageSize: DWORD}, 223 {lpMinimumApplicationAddress: ctypes.voidptr_t}, 224 {lpMaximumApplicationAddress: ctypes.voidptr_t}, 225 {dwActiveProcessorMask: DWORD.ptr}, 226 {dwNumberOfProcessors: DWORD}, 227 {dwProcessorType: DWORD}, 228 {dwAllocationGranularity: DWORD}, 229 {wProcessorLevel: WORD}, 230 {wProcessorRevision: WORD} 231 ]); 232 233 let kernel32 = false; 234 try { 235 kernel32 = ctypes.open("Kernel32"); 236 } catch (e) { 237 Cu.reportError("Unable to open kernel32! Exception: " + e); 238 } 239 240 if (kernel32) { 241 try { 242 let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo", 243 ctypes.winapi_abi, 244 ctypes.void_t, 245 SYSTEM_INFO.ptr); 246 let winSystemInfo = SYSTEM_INFO(); 247 // Default to unknown 248 winSystemInfo.wProcessorArchitecture = 0xffff; 249 250 GetNativeSystemInfo(winSystemInfo.address()); 251 switch (winSystemInfo.wProcessorArchitecture) { 252 case 9: 253 arch = "x64"; 254 break; 255 case 6: 256 arch = "IA64"; 257 break; 258 case 0: 259 arch = "x86"; 260 break; 261 } 262 } catch (e) { 263 Cu.reportError("Error getting processor architecture. " + 264 "Exception: " + e); 265 } finally { 266 kernel32.close(); 267 } 268 } 269 270 return arch; 271}); 272 273XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() { 274 let abi = null; 275 try { 276 abi = Services.appinfo.XPCOMABI; 277 } catch (e) { 278 Cu.reportError("XPCOM ABI unknown"); 279 } 280 281 if (AppConstants.platform == "macosx") { 282 // Mac universal build should report a different ABI than either macppc 283 // or mactel. 284 let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"]. 285 getService(Ci.nsIMacUtils); 286 287 if (macutils.isUniversalBinary) { 288 abi += "-u-" + macutils.architecturesInBinary; 289 } 290 } else if (AppConstants.platform == "win") { 291 // Windows build should report the CPU architecture that it's running on. 292 abi += "-" + gWinCPUArch; 293 } 294 295 if (AppConstants.ASAN) { 296 // Allow ASan builds to receive their own updates 297 abi += "-asan"; 298 } 299 300 return abi; 301}); 302 303XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function() { 304 let osVersion; 305 try { 306 osVersion = Services.sysinfo.getProperty("name") + " " + 307 Services.sysinfo.getProperty("version"); 308 } catch (e) { 309 Cu.reportError("OS Version unknown."); 310 } 311 312 if (osVersion) { 313 if (AppConstants.platform == "win") { 314 const BYTE = ctypes.uint8_t; 315 const WORD = ctypes.uint16_t; 316 const DWORD = ctypes.uint32_t; 317 const WCHAR = ctypes.char16_t; 318 const BOOL = ctypes.int; 319 320 // This structure is described at: 321 // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx 322 const SZCSDVERSIONLENGTH = 128; 323 const OSVERSIONINFOEXW = new ctypes.StructType("OSVERSIONINFOEXW", 324 [ 325 {dwOSVersionInfoSize: DWORD}, 326 {dwMajorVersion: DWORD}, 327 {dwMinorVersion: DWORD}, 328 {dwBuildNumber: DWORD}, 329 {dwPlatformId: DWORD}, 330 {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)}, 331 {wServicePackMajor: WORD}, 332 {wServicePackMinor: WORD}, 333 {wSuiteMask: WORD}, 334 {wProductType: BYTE}, 335 {wReserved: BYTE} 336 ]); 337 338 let kernel32 = false; 339 try { 340 kernel32 = ctypes.open("Kernel32"); 341 } catch (e) { 342 Cu.reportError("Unable to open kernel32! " + e); 343 osVersion += ".unknown (unknown)"; 344 } 345 346 if (kernel32) { 347 try { 348 // Get Service pack info 349 try { 350 let GetVersionEx = kernel32.declare("GetVersionExW", 351 ctypes.winapi_abi, 352 BOOL, 353 OSVERSIONINFOEXW.ptr); 354 let winVer = OSVERSIONINFOEXW(); 355 winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size; 356 357 if (0 !== GetVersionEx(winVer.address())) { 358 osVersion += "." + winVer.wServicePackMajor + 359 "." + winVer.wServicePackMinor + 360 "." + winVer.dwBuildNumber; 361 } else { 362 Cu.reportError("Unknown failure in GetVersionEX (returned 0)"); 363 osVersion += ".unknown"; 364 } 365 } catch (e) { 366 Cu.reportError("Error getting service pack information. Exception: " + e); 367 osVersion += ".unknown"; 368 } 369 370 if (Services.vc.compare(Services.sysinfo.getProperty("version"), "10") >= 0) { 371 const WINDOWS_UBR_KEY_PATH = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; 372 let ubr = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, 373 WINDOWS_UBR_KEY_PATH, "UBR", 374 Ci.nsIWindowsRegKey.WOW64_64); 375 osVersion += (ubr !== undefined) ? "." + ubr : ".unknown"; 376 } 377 } finally { 378 kernel32.close(); 379 } 380 381 // Add processor architecture 382 osVersion += " (" + gWinCPUArch + ")"; 383 } 384 } 385 386 try { 387 osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")"; 388 } catch (e) { 389 // Not all platforms have a secondary widget library, so an error is nothing to worry about. 390 } 391 osVersion = encodeURIComponent(osVersion); 392 } 393 return osVersion; 394}); 395