1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/b2g/components/UpdatePrompt.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,605 @@ 1.4 +/* -*- Mode: Java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=8 et : 1.6 + */ 1.7 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.9 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +const Cc = Components.classes; 1.12 +const Ci = Components.interfaces; 1.13 +const Cu = Components.utils; 1.14 +const Cr = Components.results; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 +Cu.import("resource://gre/modules/WebappsUpdater.jsm"); 1.19 + 1.20 +const VERBOSE = 1; 1.21 +let log = 1.22 + VERBOSE ? 1.23 + function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } : 1.24 + function log_noop(msg) { }; 1.25 + 1.26 +const PREF_APPLY_PROMPT_TIMEOUT = "b2g.update.apply-prompt-timeout"; 1.27 +const PREF_APPLY_IDLE_TIMEOUT = "b2g.update.apply-idle-timeout"; 1.28 +const PREF_DOWNLOAD_WATCHDOG_TIMEOUT = "b2g.update.download-watchdog-timeout"; 1.29 +const PREF_DOWNLOAD_WATCHDOG_MAX_RETRIES = "b2g.update.download-watchdog-max-retries"; 1.30 + 1.31 +const NETWORK_ERROR_OFFLINE = 111; 1.32 +const HTTP_ERROR_OFFSET = 1000; 1.33 + 1.34 +const STATE_DOWNLOADING = 'downloading'; 1.35 + 1.36 +XPCOMUtils.defineLazyServiceGetter(Services, "aus", 1.37 + "@mozilla.org/updates/update-service;1", 1.38 + "nsIApplicationUpdateService"); 1.39 + 1.40 +XPCOMUtils.defineLazyServiceGetter(Services, "um", 1.41 + "@mozilla.org/updates/update-manager;1", 1.42 + "nsIUpdateManager"); 1.43 + 1.44 +XPCOMUtils.defineLazyServiceGetter(Services, "idle", 1.45 + "@mozilla.org/widget/idleservice;1", 1.46 + "nsIIdleService"); 1.47 + 1.48 +XPCOMUtils.defineLazyServiceGetter(Services, "settings", 1.49 + "@mozilla.org/settingsService;1", 1.50 + "nsISettingsService"); 1.51 + 1.52 +XPCOMUtils.defineLazyServiceGetter(Services, 'env', 1.53 + '@mozilla.org/process/environment;1', 1.54 + 'nsIEnvironment'); 1.55 + 1.56 +function useSettings() { 1.57 + // When we're running in the real phone, then we can use settings. 1.58 + // But when we're running as part of xpcshell, there is no settings database 1.59 + // and trying to use settings in this scenario causes lots of weird 1.60 + // assertions at shutdown time. 1.61 + if (typeof useSettings.result === "undefined") { 1.62 + useSettings.result = !Services.env.get("XPCSHELL_TEST_PROFILE_DIR"); 1.63 + } 1.64 + return useSettings.result; 1.65 +} 1.66 + 1.67 +XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", 1.68 + "resource://gre/modules/SystemAppProxy.jsm"); 1.69 + 1.70 +function UpdateCheckListener(updatePrompt) { 1.71 + this._updatePrompt = updatePrompt; 1.72 +} 1.73 + 1.74 +UpdateCheckListener.prototype = { 1.75 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateCheckListener]), 1.76 + 1.77 + _updatePrompt: null, 1.78 + 1.79 + onCheckComplete: function UCL_onCheckComplete(request, updates, updateCount) { 1.80 + if (Services.um.activeUpdate) { 1.81 + // We're actively downloading an update, that's the update the user should 1.82 + // see, even if a newer update is available. 1.83 + this._updatePrompt.setUpdateStatus("active-update"); 1.84 + this._updatePrompt.showUpdateAvailable(Services.um.activeUpdate); 1.85 + return; 1.86 + } 1.87 + 1.88 + if (updateCount == 0) { 1.89 + this._updatePrompt.setUpdateStatus("no-updates"); 1.90 + return; 1.91 + } 1.92 + 1.93 + let update = Services.aus.selectUpdate(updates, updateCount); 1.94 + if (!update) { 1.95 + this._updatePrompt.setUpdateStatus("already-latest-version"); 1.96 + return; 1.97 + } 1.98 + 1.99 + this._updatePrompt.setUpdateStatus("check-complete"); 1.100 + this._updatePrompt.showUpdateAvailable(update); 1.101 + }, 1.102 + 1.103 + onError: function UCL_onError(request, update) { 1.104 + // nsIUpdate uses a signed integer for errorCode while any platform errors 1.105 + // require all 32 bits. 1.106 + let errorCode = update.errorCode >>> 0; 1.107 + let isNSError = (errorCode >>> 31) == 1; 1.108 + 1.109 + if (errorCode == NETWORK_ERROR_OFFLINE) { 1.110 + this._updatePrompt.setUpdateStatus("retry-when-online"); 1.111 + } else if (isNSError) { 1.112 + this._updatePrompt.setUpdateStatus("check-error-" + errorCode); 1.113 + } else if (errorCode > HTTP_ERROR_OFFSET) { 1.114 + let httpErrorCode = errorCode - HTTP_ERROR_OFFSET; 1.115 + this._updatePrompt.setUpdateStatus("check-error-http-" + httpErrorCode); 1.116 + } 1.117 + 1.118 + Services.aus.QueryInterface(Ci.nsIUpdateCheckListener); 1.119 + Services.aus.onError(request, update); 1.120 + } 1.121 +}; 1.122 + 1.123 +function UpdatePrompt() { 1.124 + this.wrappedJSObject = this; 1.125 + this._updateCheckListener = new UpdateCheckListener(this); 1.126 + Services.obs.addObserver(this, "update-check-start", false); 1.127 +} 1.128 + 1.129 +UpdatePrompt.prototype = { 1.130 + classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"), 1.131 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt, 1.132 + Ci.nsIUpdateCheckListener, 1.133 + Ci.nsIRequestObserver, 1.134 + Ci.nsIProgressEventSink, 1.135 + Ci.nsIObserver]), 1.136 + _xpcom_factory: XPCOMUtils.generateSingletonFactory(UpdatePrompt), 1.137 + 1.138 + _update: null, 1.139 + _applyPromptTimer: null, 1.140 + _waitingForIdle: false, 1.141 + _updateCheckListner: null, 1.142 + 1.143 + get applyPromptTimeout() { 1.144 + return Services.prefs.getIntPref(PREF_APPLY_PROMPT_TIMEOUT); 1.145 + }, 1.146 + 1.147 + get applyIdleTimeout() { 1.148 + return Services.prefs.getIntPref(PREF_APPLY_IDLE_TIMEOUT); 1.149 + }, 1.150 + 1.151 + handleContentStart: function UP_handleContentStart() { 1.152 + SystemAppProxy.addEventListener("mozContentEvent", this); 1.153 + }, 1.154 + 1.155 + // nsIUpdatePrompt 1.156 + 1.157 + // FIXME/bug 737601: we should have users opt-in to downloading 1.158 + // updates when on a billed pipe. Initially, opt-in for 3g, but 1.159 + // that doesn't cover all cases. 1.160 + checkForUpdates: function UP_checkForUpdates() { }, 1.161 + 1.162 + showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) { 1.163 + if (!this.sendUpdateEvent("update-available", aUpdate)) { 1.164 + 1.165 + log("Unable to prompt for available update, forcing download"); 1.166 + this.downloadUpdate(aUpdate); 1.167 + } 1.168 + }, 1.169 + 1.170 + showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) { 1.171 + // The update has been downloaded and staged. We send the update-downloaded 1.172 + // event right away. After the user has been idle for a while, we send the 1.173 + // update-prompt-restart event, increasing the chances that we can apply the 1.174 + // update quietly without user intervention. 1.175 + this.sendUpdateEvent("update-downloaded", aUpdate); 1.176 + 1.177 + if (Services.idle.idleTime >= this.applyIdleTimeout) { 1.178 + this.showApplyPrompt(aUpdate); 1.179 + return; 1.180 + } 1.181 + 1.182 + let applyIdleTimeoutSeconds = this.applyIdleTimeout / 1000; 1.183 + // We haven't been idle long enough, so register an observer 1.184 + log("Update is ready to apply, registering idle timeout of " + 1.185 + applyIdleTimeoutSeconds + " seconds before prompting."); 1.186 + 1.187 + this._update = aUpdate; 1.188 + this.waitForIdle(); 1.189 + }, 1.190 + 1.191 + showUpdateError: function UP_showUpdateError(aUpdate) { 1.192 + log("Update error, state: " + aUpdate.state + ", errorCode: " + 1.193 + aUpdate.errorCode); 1.194 + this.sendUpdateEvent("update-error", aUpdate); 1.195 + this.setUpdateStatus(aUpdate.statusText); 1.196 + }, 1.197 + 1.198 + showUpdateHistory: function UP_showUpdateHistory(aParent) { }, 1.199 + showUpdateInstalled: function UP_showUpdateInstalled() { 1.200 + if (useSettings()) { 1.201 + let lock = Services.settings.createLock(); 1.202 + lock.set("deviceinfo.last_updated", Date.now(), null, null); 1.203 + } 1.204 + }, 1.205 + 1.206 + // Custom functions 1.207 + 1.208 + waitForIdle: function UP_waitForIdle() { 1.209 + if (this._waitingForIdle) { 1.210 + return; 1.211 + } 1.212 + 1.213 + this._waitingForIdle = true; 1.214 + Services.idle.addIdleObserver(this, this.applyIdleTimeout / 1000); 1.215 + Services.obs.addObserver(this, "quit-application", false); 1.216 + }, 1.217 + 1.218 + setUpdateStatus: function UP_setUpdateStatus(aStatus) { 1.219 + if (useSettings()) { 1.220 + log("Setting gecko.updateStatus: " + aStatus); 1.221 + 1.222 + let lock = Services.settings.createLock(); 1.223 + lock.set("gecko.updateStatus", aStatus, null); 1.224 + } 1.225 + }, 1.226 + 1.227 + showApplyPrompt: function UP_showApplyPrompt(aUpdate) { 1.228 + if (!this.sendUpdateEvent("update-prompt-apply", aUpdate)) { 1.229 + log("Unable to prompt, forcing restart"); 1.230 + this.restartProcess(); 1.231 + return; 1.232 + } 1.233 + 1.234 +#ifdef MOZ_B2G_RIL 1.235 + let window = Services.wm.getMostRecentWindow("navigator:browser"); 1.236 + let pinReq = window.navigator.mozIccManager.getCardLock("pin"); 1.237 + pinReq.onsuccess = function(e) { 1.238 + if (e.target.result.enabled) { 1.239 + // The SIM is pin locked. Don't use a fallback timer. This means that 1.240 + // the user has to press Install to apply the update. If we use the 1.241 + // timer, and the timer reboots the phone, then the phone will be 1.242 + // unusable until the SIM is unlocked. 1.243 + log("SIM is pin locked. Not starting fallback timer."); 1.244 + } else { 1.245 + // This means that no pin lock is enabled, so we go ahead and start 1.246 + // the fallback timer. 1.247 + this._applyPromptTimer = this.createTimer(this.applyPromptTimeout); 1.248 + } 1.249 + }.bind(this); 1.250 + pinReq.onerror = function(e) { 1.251 + this._applyPromptTimer = this.createTimer(this.applyPromptTimeout); 1.252 + }.bind(this); 1.253 +#else 1.254 + // Schedule a fallback timeout in case the UI is unable to respond or show 1.255 + // a prompt for some reason. 1.256 + this._applyPromptTimer = this.createTimer(this.applyPromptTimeout); 1.257 +#endif 1.258 + }, 1.259 + 1.260 + _copyProperties: ["appVersion", "buildID", "detailsURL", "displayVersion", 1.261 + "errorCode", "isOSUpdate", "platformVersion", 1.262 + "previousAppVersion", "state", "statusText"], 1.263 + 1.264 + sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate) { 1.265 + let detail = {}; 1.266 + for each (let property in this._copyProperties) { 1.267 + detail[property] = aUpdate[property]; 1.268 + } 1.269 + 1.270 + let patch = aUpdate.selectedPatch; 1.271 + if (!patch && aUpdate.patchCount > 0) { 1.272 + // For now we just check the first patch to get size information if a 1.273 + // patch hasn't been selected yet. 1.274 + patch = aUpdate.getPatchAt(0); 1.275 + } 1.276 + 1.277 + if (patch) { 1.278 + detail.size = patch.size; 1.279 + detail.updateType = patch.type; 1.280 + } else { 1.281 + log("Warning: no patches available in update"); 1.282 + } 1.283 + 1.284 + this._update = aUpdate; 1.285 + return this.sendChromeEvent(aType, detail); 1.286 + }, 1.287 + 1.288 + sendChromeEvent: function UP_sendChromeEvent(aType, aDetail) { 1.289 + let detail = aDetail || {}; 1.290 + detail.type = aType; 1.291 + 1.292 + let sent = SystemAppProxy.dispatchEvent(detail); 1.293 + if (!sent) { 1.294 + log("Warning: Couldn't send update event " + aType + 1.295 + ": no content browser. Will send again when content becomes available."); 1.296 + return false; 1.297 + } 1.298 + return true; 1.299 + }, 1.300 + 1.301 + handleAvailableResult: function UP_handleAvailableResult(aDetail) { 1.302 + // If the user doesn't choose "download", the updater will implicitly call 1.303 + // showUpdateAvailable again after a certain period of time 1.304 + switch (aDetail.result) { 1.305 + case "download": 1.306 + this.downloadUpdate(this._update); 1.307 + break; 1.308 + } 1.309 + }, 1.310 + 1.311 + handleApplyPromptResult: function UP_handleApplyPromptResult(aDetail) { 1.312 + if (this._applyPromptTimer) { 1.313 + this._applyPromptTimer.cancel(); 1.314 + this._applyPromptTimer = null; 1.315 + } 1.316 + 1.317 + switch (aDetail.result) { 1.318 + case "wait": 1.319 + // Wait until the user is idle before prompting to apply the update 1.320 + this.waitForIdle(); 1.321 + break; 1.322 + case "restart": 1.323 + this.finishUpdate(); 1.324 + this._update = null; 1.325 + break; 1.326 + } 1.327 + }, 1.328 + 1.329 + downloadUpdate: function UP_downloadUpdate(aUpdate) { 1.330 + if (!aUpdate) { 1.331 + aUpdate = Services.um.activeUpdate; 1.332 + if (!aUpdate) { 1.333 + log("No active update found to download"); 1.334 + return; 1.335 + } 1.336 + } 1.337 + 1.338 + let status = Services.aus.downloadUpdate(aUpdate, true); 1.339 + if (status == STATE_DOWNLOADING) { 1.340 + Services.aus.addDownloadListener(this); 1.341 + return; 1.342 + } 1.343 + 1.344 + // If the update has already been downloaded and applied, then 1.345 + // Services.aus.downloadUpdate will return immediately and not 1.346 + // call showUpdateDownloaded, so we detect this. 1.347 + if (aUpdate.state == "applied" && aUpdate.errorCode == 0) { 1.348 + this.showUpdateDownloaded(aUpdate, true); 1.349 + return; 1.350 + } 1.351 + 1.352 + log("Error downloading update " + aUpdate.name + ": " + aUpdate.errorCode); 1.353 + let errorCode = aUpdate.errorCode >>> 0; 1.354 + if (errorCode == Cr.NS_ERROR_FILE_TOO_BIG) { 1.355 + aUpdate.statusText = "file-too-big"; 1.356 + } 1.357 + this.showUpdateError(aUpdate); 1.358 + }, 1.359 + 1.360 + handleDownloadCancel: function UP_handleDownloadCancel() { 1.361 + log("Pausing download"); 1.362 + Services.aus.pauseDownload(); 1.363 + }, 1.364 + 1.365 + finishUpdate: function UP_finishUpdate() { 1.366 + if (!this._update.isOSUpdate) { 1.367 + // Standard gecko+gaia updates will just need to restart the process 1.368 + this.restartProcess(); 1.369 + return; 1.370 + } 1.371 + 1.372 + try { 1.373 + Services.aus.applyOsUpdate(this._update); 1.374 + } 1.375 + catch (e) { 1.376 + this._update.errorCode = Cr.NS_ERROR_FAILURE; 1.377 + this.showUpdateError(this._update); 1.378 + } 1.379 + }, 1.380 + 1.381 + restartProcess: function UP_restartProcess() { 1.382 + log("Update downloaded, restarting to apply it"); 1.383 + 1.384 + let callbackAfterSet = function() { 1.385 +#ifndef MOZ_WIDGET_GONK 1.386 + let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"] 1.387 + .getService(Ci.nsIAppStartup); 1.388 + appStartup.quit(appStartup.eForceQuit | appStartup.eRestart); 1.389 +#else 1.390 + // NB: on Gonk, we rely on the system process manager to restart us. 1.391 + let pmService = Cc["@mozilla.org/power/powermanagerservice;1"] 1.392 + .getService(Ci.nsIPowerManagerService); 1.393 + pmService.restart(); 1.394 +#endif 1.395 + } 1.396 + 1.397 + if (useSettings()) { 1.398 + // Save current os version in deviceinfo.previous_os 1.399 + let lock = Services.settings.createLock({ 1.400 + handle: callbackAfterSet, 1.401 + handleAbort: function(error) { 1.402 + log("Abort callback when trying to set previous_os: " + error); 1.403 + callbackAfterSet(); 1.404 + } 1.405 + }); 1.406 + lock.get("deviceinfo.os", { 1.407 + handle: function(name, value) { 1.408 + log("Set previous_os to: " + value); 1.409 + lock.set("deviceinfo.previous_os", value, null, null); 1.410 + } 1.411 + }); 1.412 + } 1.413 + }, 1.414 + 1.415 + forceUpdateCheck: function UP_forceUpdateCheck() { 1.416 + log("Forcing update check"); 1.417 + 1.418 + let checker = Cc["@mozilla.org/updates/update-checker;1"] 1.419 + .createInstance(Ci.nsIUpdateChecker); 1.420 + checker.checkForUpdates(this._updateCheckListener, true); 1.421 + }, 1.422 + 1.423 + handleEvent: function UP_handleEvent(evt) { 1.424 + if (evt.type !== "mozContentEvent") { 1.425 + return; 1.426 + } 1.427 + 1.428 + let detail = evt.detail; 1.429 + if (!detail) { 1.430 + return; 1.431 + } 1.432 + 1.433 + switch (detail.type) { 1.434 + case "force-update-check": 1.435 + this.forceUpdateCheck(); 1.436 + break; 1.437 + case "update-available-result": 1.438 + this.handleAvailableResult(detail); 1.439 + // If we started the apply prompt timer, this means that we're waiting 1.440 + // for the user to press Later or Install Now. In this situation we 1.441 + // don't want to clear this._update, becuase handleApplyPromptResult 1.442 + // needs it. 1.443 + if (this._applyPromptTimer == null && !this._waitingForIdle) { 1.444 + this._update = null; 1.445 + } 1.446 + break; 1.447 + case "update-download-cancel": 1.448 + this.handleDownloadCancel(); 1.449 + break; 1.450 + case "update-prompt-apply-result": 1.451 + this.handleApplyPromptResult(detail); 1.452 + break; 1.453 + } 1.454 + }, 1.455 + 1.456 + // nsIObserver 1.457 + 1.458 + observe: function UP_observe(aSubject, aTopic, aData) { 1.459 + switch (aTopic) { 1.460 + case "idle": 1.461 + this._waitingForIdle = false; 1.462 + this.showApplyPrompt(this._update); 1.463 + // Fall through 1.464 + case "quit-application": 1.465 + Services.idle.removeIdleObserver(this, this.applyIdleTimeout / 1000); 1.466 + Services.obs.removeObserver(this, "quit-application"); 1.467 + break; 1.468 + case "update-check-start": 1.469 + WebappsUpdater.updateApps(); 1.470 + break; 1.471 + } 1.472 + }, 1.473 + 1.474 + // nsITimerCallback 1.475 + 1.476 + notify: function UP_notify(aTimer) { 1.477 + if (aTimer == this._applyPromptTimer) { 1.478 + log("Timed out waiting for result, restarting"); 1.479 + this._applyPromptTimer = null; 1.480 + this.finishUpdate(); 1.481 + this._update = null; 1.482 + return; 1.483 + } 1.484 + if (aTimer == this._watchdogTimer) { 1.485 + log("Download watchdog fired"); 1.486 + this._watchdogTimer = null; 1.487 + this._autoRestartDownload = true; 1.488 + Services.aus.pauseDownload(); 1.489 + return; 1.490 + } 1.491 + }, 1.492 + 1.493 + createTimer: function UP_createTimer(aTimeoutMs) { 1.494 + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.495 + timer.initWithCallback(this, aTimeoutMs, timer.TYPE_ONE_SHOT); 1.496 + return timer; 1.497 + }, 1.498 + 1.499 + // nsIRequestObserver 1.500 + 1.501 + _startedSent: false, 1.502 + 1.503 + _watchdogTimer: null, 1.504 + 1.505 + _autoRestartDownload: false, 1.506 + _autoRestartCount: 0, 1.507 + 1.508 + startWatchdogTimer: function UP_startWatchdogTimer() { 1.509 + let watchdogTimeout = 120000; // 120 seconds 1.510 + try { 1.511 + watchdogTimeout = Services.prefs.getIntPref(PREF_DOWNLOAD_WATCHDOG_TIMEOUT); 1.512 + } catch (e) { 1.513 + // This means that the preference doesn't exist. watchdogTimeout will 1.514 + // retain its default assigned above. 1.515 + } 1.516 + if (watchdogTimeout <= 0) { 1.517 + // 0 implies don't bother using the watchdog timer at all. 1.518 + this._watchdogTimer = null; 1.519 + return; 1.520 + } 1.521 + if (this._watchdogTimer) { 1.522 + this._watchdogTimer.cancel(); 1.523 + } else { 1.524 + this._watchdogTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.525 + } 1.526 + this._watchdogTimer.initWithCallback(this, watchdogTimeout, 1.527 + Ci.nsITimer.TYPE_ONE_SHOT); 1.528 + }, 1.529 + 1.530 + stopWatchdogTimer: function UP_stopWatchdogTimer() { 1.531 + if (this._watchdogTimer) { 1.532 + this._watchdogTimer.cancel(); 1.533 + this._watchdogTimer = null; 1.534 + } 1.535 + }, 1.536 + 1.537 + touchWatchdogTimer: function UP_touchWatchdogTimer() { 1.538 + this.startWatchdogTimer(); 1.539 + }, 1.540 + 1.541 + onStartRequest: function UP_onStartRequest(aRequest, aContext) { 1.542 + // Wait until onProgress to send the update-download-started event, in case 1.543 + // this request turns out to fail for some reason 1.544 + this._startedSent = false; 1.545 + this.startWatchdogTimer(); 1.546 + }, 1.547 + 1.548 + onStopRequest: function UP_onStopRequest(aRequest, aContext, aStatusCode) { 1.549 + this.stopWatchdogTimer(); 1.550 + Services.aus.removeDownloadListener(this); 1.551 + let paused = !Components.isSuccessCode(aStatusCode); 1.552 + if (!paused) { 1.553 + // The download was successful, no need to restart 1.554 + this._autoRestartDownload = false; 1.555 + } 1.556 + if (this._autoRestartDownload) { 1.557 + this._autoRestartDownload = false; 1.558 + let watchdogMaxRetries = Services.prefs.getIntPref(PREF_DOWNLOAD_WATCHDOG_MAX_RETRIES); 1.559 + this._autoRestartCount++; 1.560 + if (this._autoRestartCount > watchdogMaxRetries) { 1.561 + log("Download - retry count exceeded - error"); 1.562 + // We exceeded the max retries. Treat the download like an error, 1.563 + // which will give the user a chance to restart manually later. 1.564 + this._autoRestartCount = 0; 1.565 + if (Services.um.activeUpdate) { 1.566 + this.showUpdateError(Services.um.activeUpdate); 1.567 + } 1.568 + return; 1.569 + } 1.570 + log("Download - restarting download - attempt " + this._autoRestartCount); 1.571 + this.downloadUpdate(null); 1.572 + return; 1.573 + } 1.574 + this._autoRestartCount = 0; 1.575 + this.sendChromeEvent("update-download-stopped", { 1.576 + paused: paused 1.577 + }); 1.578 + }, 1.579 + 1.580 + // nsIProgressEventSink 1.581 + 1.582 + onProgress: function UP_onProgress(aRequest, aContext, aProgress, 1.583 + aProgressMax) { 1.584 + if (aProgress == aProgressMax) { 1.585 + // The update.mar validation done by onStopRequest may take 1.586 + // a while before the onStopRequest callback is made, so stop 1.587 + // the timer now. 1.588 + this.stopWatchdogTimer(); 1.589 + } else { 1.590 + this.touchWatchdogTimer(); 1.591 + } 1.592 + if (!this._startedSent) { 1.593 + this.sendChromeEvent("update-download-started", { 1.594 + total: aProgressMax 1.595 + }); 1.596 + this._startedSent = true; 1.597 + } 1.598 + 1.599 + this.sendChromeEvent("update-download-progress", { 1.600 + progress: aProgress, 1.601 + total: aProgressMax 1.602 + }); 1.603 + }, 1.604 + 1.605 + onStatus: function UP_onStatus(aRequest, aUpdate, aStatus, aStatusArg) { } 1.606 +}; 1.607 + 1.608 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]);