|
1 /* -*- Mode: Java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * vim: sw=2 ts=8 et : |
|
3 */ |
|
4 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
6 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
7 |
|
8 const Cc = Components.classes; |
|
9 const Ci = Components.interfaces; |
|
10 const Cu = Components.utils; |
|
11 const Cr = Components.results; |
|
12 |
|
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
14 Cu.import("resource://gre/modules/Services.jsm"); |
|
15 Cu.import("resource://gre/modules/WebappsUpdater.jsm"); |
|
16 |
|
17 const VERBOSE = 1; |
|
18 let log = |
|
19 VERBOSE ? |
|
20 function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } : |
|
21 function log_noop(msg) { }; |
|
22 |
|
23 const PREF_APPLY_PROMPT_TIMEOUT = "b2g.update.apply-prompt-timeout"; |
|
24 const PREF_APPLY_IDLE_TIMEOUT = "b2g.update.apply-idle-timeout"; |
|
25 const PREF_DOWNLOAD_WATCHDOG_TIMEOUT = "b2g.update.download-watchdog-timeout"; |
|
26 const PREF_DOWNLOAD_WATCHDOG_MAX_RETRIES = "b2g.update.download-watchdog-max-retries"; |
|
27 |
|
28 const NETWORK_ERROR_OFFLINE = 111; |
|
29 const HTTP_ERROR_OFFSET = 1000; |
|
30 |
|
31 const STATE_DOWNLOADING = 'downloading'; |
|
32 |
|
33 XPCOMUtils.defineLazyServiceGetter(Services, "aus", |
|
34 "@mozilla.org/updates/update-service;1", |
|
35 "nsIApplicationUpdateService"); |
|
36 |
|
37 XPCOMUtils.defineLazyServiceGetter(Services, "um", |
|
38 "@mozilla.org/updates/update-manager;1", |
|
39 "nsIUpdateManager"); |
|
40 |
|
41 XPCOMUtils.defineLazyServiceGetter(Services, "idle", |
|
42 "@mozilla.org/widget/idleservice;1", |
|
43 "nsIIdleService"); |
|
44 |
|
45 XPCOMUtils.defineLazyServiceGetter(Services, "settings", |
|
46 "@mozilla.org/settingsService;1", |
|
47 "nsISettingsService"); |
|
48 |
|
49 XPCOMUtils.defineLazyServiceGetter(Services, 'env', |
|
50 '@mozilla.org/process/environment;1', |
|
51 'nsIEnvironment'); |
|
52 |
|
53 function useSettings() { |
|
54 // When we're running in the real phone, then we can use settings. |
|
55 // But when we're running as part of xpcshell, there is no settings database |
|
56 // and trying to use settings in this scenario causes lots of weird |
|
57 // assertions at shutdown time. |
|
58 if (typeof useSettings.result === "undefined") { |
|
59 useSettings.result = !Services.env.get("XPCSHELL_TEST_PROFILE_DIR"); |
|
60 } |
|
61 return useSettings.result; |
|
62 } |
|
63 |
|
64 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", |
|
65 "resource://gre/modules/SystemAppProxy.jsm"); |
|
66 |
|
67 function UpdateCheckListener(updatePrompt) { |
|
68 this._updatePrompt = updatePrompt; |
|
69 } |
|
70 |
|
71 UpdateCheckListener.prototype = { |
|
72 QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateCheckListener]), |
|
73 |
|
74 _updatePrompt: null, |
|
75 |
|
76 onCheckComplete: function UCL_onCheckComplete(request, updates, updateCount) { |
|
77 if (Services.um.activeUpdate) { |
|
78 // We're actively downloading an update, that's the update the user should |
|
79 // see, even if a newer update is available. |
|
80 this._updatePrompt.setUpdateStatus("active-update"); |
|
81 this._updatePrompt.showUpdateAvailable(Services.um.activeUpdate); |
|
82 return; |
|
83 } |
|
84 |
|
85 if (updateCount == 0) { |
|
86 this._updatePrompt.setUpdateStatus("no-updates"); |
|
87 return; |
|
88 } |
|
89 |
|
90 let update = Services.aus.selectUpdate(updates, updateCount); |
|
91 if (!update) { |
|
92 this._updatePrompt.setUpdateStatus("already-latest-version"); |
|
93 return; |
|
94 } |
|
95 |
|
96 this._updatePrompt.setUpdateStatus("check-complete"); |
|
97 this._updatePrompt.showUpdateAvailable(update); |
|
98 }, |
|
99 |
|
100 onError: function UCL_onError(request, update) { |
|
101 // nsIUpdate uses a signed integer for errorCode while any platform errors |
|
102 // require all 32 bits. |
|
103 let errorCode = update.errorCode >>> 0; |
|
104 let isNSError = (errorCode >>> 31) == 1; |
|
105 |
|
106 if (errorCode == NETWORK_ERROR_OFFLINE) { |
|
107 this._updatePrompt.setUpdateStatus("retry-when-online"); |
|
108 } else if (isNSError) { |
|
109 this._updatePrompt.setUpdateStatus("check-error-" + errorCode); |
|
110 } else if (errorCode > HTTP_ERROR_OFFSET) { |
|
111 let httpErrorCode = errorCode - HTTP_ERROR_OFFSET; |
|
112 this._updatePrompt.setUpdateStatus("check-error-http-" + httpErrorCode); |
|
113 } |
|
114 |
|
115 Services.aus.QueryInterface(Ci.nsIUpdateCheckListener); |
|
116 Services.aus.onError(request, update); |
|
117 } |
|
118 }; |
|
119 |
|
120 function UpdatePrompt() { |
|
121 this.wrappedJSObject = this; |
|
122 this._updateCheckListener = new UpdateCheckListener(this); |
|
123 Services.obs.addObserver(this, "update-check-start", false); |
|
124 } |
|
125 |
|
126 UpdatePrompt.prototype = { |
|
127 classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"), |
|
128 QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt, |
|
129 Ci.nsIUpdateCheckListener, |
|
130 Ci.nsIRequestObserver, |
|
131 Ci.nsIProgressEventSink, |
|
132 Ci.nsIObserver]), |
|
133 _xpcom_factory: XPCOMUtils.generateSingletonFactory(UpdatePrompt), |
|
134 |
|
135 _update: null, |
|
136 _applyPromptTimer: null, |
|
137 _waitingForIdle: false, |
|
138 _updateCheckListner: null, |
|
139 |
|
140 get applyPromptTimeout() { |
|
141 return Services.prefs.getIntPref(PREF_APPLY_PROMPT_TIMEOUT); |
|
142 }, |
|
143 |
|
144 get applyIdleTimeout() { |
|
145 return Services.prefs.getIntPref(PREF_APPLY_IDLE_TIMEOUT); |
|
146 }, |
|
147 |
|
148 handleContentStart: function UP_handleContentStart() { |
|
149 SystemAppProxy.addEventListener("mozContentEvent", this); |
|
150 }, |
|
151 |
|
152 // nsIUpdatePrompt |
|
153 |
|
154 // FIXME/bug 737601: we should have users opt-in to downloading |
|
155 // updates when on a billed pipe. Initially, opt-in for 3g, but |
|
156 // that doesn't cover all cases. |
|
157 checkForUpdates: function UP_checkForUpdates() { }, |
|
158 |
|
159 showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) { |
|
160 if (!this.sendUpdateEvent("update-available", aUpdate)) { |
|
161 |
|
162 log("Unable to prompt for available update, forcing download"); |
|
163 this.downloadUpdate(aUpdate); |
|
164 } |
|
165 }, |
|
166 |
|
167 showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) { |
|
168 // The update has been downloaded and staged. We send the update-downloaded |
|
169 // event right away. After the user has been idle for a while, we send the |
|
170 // update-prompt-restart event, increasing the chances that we can apply the |
|
171 // update quietly without user intervention. |
|
172 this.sendUpdateEvent("update-downloaded", aUpdate); |
|
173 |
|
174 if (Services.idle.idleTime >= this.applyIdleTimeout) { |
|
175 this.showApplyPrompt(aUpdate); |
|
176 return; |
|
177 } |
|
178 |
|
179 let applyIdleTimeoutSeconds = this.applyIdleTimeout / 1000; |
|
180 // We haven't been idle long enough, so register an observer |
|
181 log("Update is ready to apply, registering idle timeout of " + |
|
182 applyIdleTimeoutSeconds + " seconds before prompting."); |
|
183 |
|
184 this._update = aUpdate; |
|
185 this.waitForIdle(); |
|
186 }, |
|
187 |
|
188 showUpdateError: function UP_showUpdateError(aUpdate) { |
|
189 log("Update error, state: " + aUpdate.state + ", errorCode: " + |
|
190 aUpdate.errorCode); |
|
191 this.sendUpdateEvent("update-error", aUpdate); |
|
192 this.setUpdateStatus(aUpdate.statusText); |
|
193 }, |
|
194 |
|
195 showUpdateHistory: function UP_showUpdateHistory(aParent) { }, |
|
196 showUpdateInstalled: function UP_showUpdateInstalled() { |
|
197 if (useSettings()) { |
|
198 let lock = Services.settings.createLock(); |
|
199 lock.set("deviceinfo.last_updated", Date.now(), null, null); |
|
200 } |
|
201 }, |
|
202 |
|
203 // Custom functions |
|
204 |
|
205 waitForIdle: function UP_waitForIdle() { |
|
206 if (this._waitingForIdle) { |
|
207 return; |
|
208 } |
|
209 |
|
210 this._waitingForIdle = true; |
|
211 Services.idle.addIdleObserver(this, this.applyIdleTimeout / 1000); |
|
212 Services.obs.addObserver(this, "quit-application", false); |
|
213 }, |
|
214 |
|
215 setUpdateStatus: function UP_setUpdateStatus(aStatus) { |
|
216 if (useSettings()) { |
|
217 log("Setting gecko.updateStatus: " + aStatus); |
|
218 |
|
219 let lock = Services.settings.createLock(); |
|
220 lock.set("gecko.updateStatus", aStatus, null); |
|
221 } |
|
222 }, |
|
223 |
|
224 showApplyPrompt: function UP_showApplyPrompt(aUpdate) { |
|
225 if (!this.sendUpdateEvent("update-prompt-apply", aUpdate)) { |
|
226 log("Unable to prompt, forcing restart"); |
|
227 this.restartProcess(); |
|
228 return; |
|
229 } |
|
230 |
|
231 #ifdef MOZ_B2G_RIL |
|
232 let window = Services.wm.getMostRecentWindow("navigator:browser"); |
|
233 let pinReq = window.navigator.mozIccManager.getCardLock("pin"); |
|
234 pinReq.onsuccess = function(e) { |
|
235 if (e.target.result.enabled) { |
|
236 // The SIM is pin locked. Don't use a fallback timer. This means that |
|
237 // the user has to press Install to apply the update. If we use the |
|
238 // timer, and the timer reboots the phone, then the phone will be |
|
239 // unusable until the SIM is unlocked. |
|
240 log("SIM is pin locked. Not starting fallback timer."); |
|
241 } else { |
|
242 // This means that no pin lock is enabled, so we go ahead and start |
|
243 // the fallback timer. |
|
244 this._applyPromptTimer = this.createTimer(this.applyPromptTimeout); |
|
245 } |
|
246 }.bind(this); |
|
247 pinReq.onerror = function(e) { |
|
248 this._applyPromptTimer = this.createTimer(this.applyPromptTimeout); |
|
249 }.bind(this); |
|
250 #else |
|
251 // Schedule a fallback timeout in case the UI is unable to respond or show |
|
252 // a prompt for some reason. |
|
253 this._applyPromptTimer = this.createTimer(this.applyPromptTimeout); |
|
254 #endif |
|
255 }, |
|
256 |
|
257 _copyProperties: ["appVersion", "buildID", "detailsURL", "displayVersion", |
|
258 "errorCode", "isOSUpdate", "platformVersion", |
|
259 "previousAppVersion", "state", "statusText"], |
|
260 |
|
261 sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate) { |
|
262 let detail = {}; |
|
263 for each (let property in this._copyProperties) { |
|
264 detail[property] = aUpdate[property]; |
|
265 } |
|
266 |
|
267 let patch = aUpdate.selectedPatch; |
|
268 if (!patch && aUpdate.patchCount > 0) { |
|
269 // For now we just check the first patch to get size information if a |
|
270 // patch hasn't been selected yet. |
|
271 patch = aUpdate.getPatchAt(0); |
|
272 } |
|
273 |
|
274 if (patch) { |
|
275 detail.size = patch.size; |
|
276 detail.updateType = patch.type; |
|
277 } else { |
|
278 log("Warning: no patches available in update"); |
|
279 } |
|
280 |
|
281 this._update = aUpdate; |
|
282 return this.sendChromeEvent(aType, detail); |
|
283 }, |
|
284 |
|
285 sendChromeEvent: function UP_sendChromeEvent(aType, aDetail) { |
|
286 let detail = aDetail || {}; |
|
287 detail.type = aType; |
|
288 |
|
289 let sent = SystemAppProxy.dispatchEvent(detail); |
|
290 if (!sent) { |
|
291 log("Warning: Couldn't send update event " + aType + |
|
292 ": no content browser. Will send again when content becomes available."); |
|
293 return false; |
|
294 } |
|
295 return true; |
|
296 }, |
|
297 |
|
298 handleAvailableResult: function UP_handleAvailableResult(aDetail) { |
|
299 // If the user doesn't choose "download", the updater will implicitly call |
|
300 // showUpdateAvailable again after a certain period of time |
|
301 switch (aDetail.result) { |
|
302 case "download": |
|
303 this.downloadUpdate(this._update); |
|
304 break; |
|
305 } |
|
306 }, |
|
307 |
|
308 handleApplyPromptResult: function UP_handleApplyPromptResult(aDetail) { |
|
309 if (this._applyPromptTimer) { |
|
310 this._applyPromptTimer.cancel(); |
|
311 this._applyPromptTimer = null; |
|
312 } |
|
313 |
|
314 switch (aDetail.result) { |
|
315 case "wait": |
|
316 // Wait until the user is idle before prompting to apply the update |
|
317 this.waitForIdle(); |
|
318 break; |
|
319 case "restart": |
|
320 this.finishUpdate(); |
|
321 this._update = null; |
|
322 break; |
|
323 } |
|
324 }, |
|
325 |
|
326 downloadUpdate: function UP_downloadUpdate(aUpdate) { |
|
327 if (!aUpdate) { |
|
328 aUpdate = Services.um.activeUpdate; |
|
329 if (!aUpdate) { |
|
330 log("No active update found to download"); |
|
331 return; |
|
332 } |
|
333 } |
|
334 |
|
335 let status = Services.aus.downloadUpdate(aUpdate, true); |
|
336 if (status == STATE_DOWNLOADING) { |
|
337 Services.aus.addDownloadListener(this); |
|
338 return; |
|
339 } |
|
340 |
|
341 // If the update has already been downloaded and applied, then |
|
342 // Services.aus.downloadUpdate will return immediately and not |
|
343 // call showUpdateDownloaded, so we detect this. |
|
344 if (aUpdate.state == "applied" && aUpdate.errorCode == 0) { |
|
345 this.showUpdateDownloaded(aUpdate, true); |
|
346 return; |
|
347 } |
|
348 |
|
349 log("Error downloading update " + aUpdate.name + ": " + aUpdate.errorCode); |
|
350 let errorCode = aUpdate.errorCode >>> 0; |
|
351 if (errorCode == Cr.NS_ERROR_FILE_TOO_BIG) { |
|
352 aUpdate.statusText = "file-too-big"; |
|
353 } |
|
354 this.showUpdateError(aUpdate); |
|
355 }, |
|
356 |
|
357 handleDownloadCancel: function UP_handleDownloadCancel() { |
|
358 log("Pausing download"); |
|
359 Services.aus.pauseDownload(); |
|
360 }, |
|
361 |
|
362 finishUpdate: function UP_finishUpdate() { |
|
363 if (!this._update.isOSUpdate) { |
|
364 // Standard gecko+gaia updates will just need to restart the process |
|
365 this.restartProcess(); |
|
366 return; |
|
367 } |
|
368 |
|
369 try { |
|
370 Services.aus.applyOsUpdate(this._update); |
|
371 } |
|
372 catch (e) { |
|
373 this._update.errorCode = Cr.NS_ERROR_FAILURE; |
|
374 this.showUpdateError(this._update); |
|
375 } |
|
376 }, |
|
377 |
|
378 restartProcess: function UP_restartProcess() { |
|
379 log("Update downloaded, restarting to apply it"); |
|
380 |
|
381 let callbackAfterSet = function() { |
|
382 #ifndef MOZ_WIDGET_GONK |
|
383 let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"] |
|
384 .getService(Ci.nsIAppStartup); |
|
385 appStartup.quit(appStartup.eForceQuit | appStartup.eRestart); |
|
386 #else |
|
387 // NB: on Gonk, we rely on the system process manager to restart us. |
|
388 let pmService = Cc["@mozilla.org/power/powermanagerservice;1"] |
|
389 .getService(Ci.nsIPowerManagerService); |
|
390 pmService.restart(); |
|
391 #endif |
|
392 } |
|
393 |
|
394 if (useSettings()) { |
|
395 // Save current os version in deviceinfo.previous_os |
|
396 let lock = Services.settings.createLock({ |
|
397 handle: callbackAfterSet, |
|
398 handleAbort: function(error) { |
|
399 log("Abort callback when trying to set previous_os: " + error); |
|
400 callbackAfterSet(); |
|
401 } |
|
402 }); |
|
403 lock.get("deviceinfo.os", { |
|
404 handle: function(name, value) { |
|
405 log("Set previous_os to: " + value); |
|
406 lock.set("deviceinfo.previous_os", value, null, null); |
|
407 } |
|
408 }); |
|
409 } |
|
410 }, |
|
411 |
|
412 forceUpdateCheck: function UP_forceUpdateCheck() { |
|
413 log("Forcing update check"); |
|
414 |
|
415 let checker = Cc["@mozilla.org/updates/update-checker;1"] |
|
416 .createInstance(Ci.nsIUpdateChecker); |
|
417 checker.checkForUpdates(this._updateCheckListener, true); |
|
418 }, |
|
419 |
|
420 handleEvent: function UP_handleEvent(evt) { |
|
421 if (evt.type !== "mozContentEvent") { |
|
422 return; |
|
423 } |
|
424 |
|
425 let detail = evt.detail; |
|
426 if (!detail) { |
|
427 return; |
|
428 } |
|
429 |
|
430 switch (detail.type) { |
|
431 case "force-update-check": |
|
432 this.forceUpdateCheck(); |
|
433 break; |
|
434 case "update-available-result": |
|
435 this.handleAvailableResult(detail); |
|
436 // If we started the apply prompt timer, this means that we're waiting |
|
437 // for the user to press Later or Install Now. In this situation we |
|
438 // don't want to clear this._update, becuase handleApplyPromptResult |
|
439 // needs it. |
|
440 if (this._applyPromptTimer == null && !this._waitingForIdle) { |
|
441 this._update = null; |
|
442 } |
|
443 break; |
|
444 case "update-download-cancel": |
|
445 this.handleDownloadCancel(); |
|
446 break; |
|
447 case "update-prompt-apply-result": |
|
448 this.handleApplyPromptResult(detail); |
|
449 break; |
|
450 } |
|
451 }, |
|
452 |
|
453 // nsIObserver |
|
454 |
|
455 observe: function UP_observe(aSubject, aTopic, aData) { |
|
456 switch (aTopic) { |
|
457 case "idle": |
|
458 this._waitingForIdle = false; |
|
459 this.showApplyPrompt(this._update); |
|
460 // Fall through |
|
461 case "quit-application": |
|
462 Services.idle.removeIdleObserver(this, this.applyIdleTimeout / 1000); |
|
463 Services.obs.removeObserver(this, "quit-application"); |
|
464 break; |
|
465 case "update-check-start": |
|
466 WebappsUpdater.updateApps(); |
|
467 break; |
|
468 } |
|
469 }, |
|
470 |
|
471 // nsITimerCallback |
|
472 |
|
473 notify: function UP_notify(aTimer) { |
|
474 if (aTimer == this._applyPromptTimer) { |
|
475 log("Timed out waiting for result, restarting"); |
|
476 this._applyPromptTimer = null; |
|
477 this.finishUpdate(); |
|
478 this._update = null; |
|
479 return; |
|
480 } |
|
481 if (aTimer == this._watchdogTimer) { |
|
482 log("Download watchdog fired"); |
|
483 this._watchdogTimer = null; |
|
484 this._autoRestartDownload = true; |
|
485 Services.aus.pauseDownload(); |
|
486 return; |
|
487 } |
|
488 }, |
|
489 |
|
490 createTimer: function UP_createTimer(aTimeoutMs) { |
|
491 let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); |
|
492 timer.initWithCallback(this, aTimeoutMs, timer.TYPE_ONE_SHOT); |
|
493 return timer; |
|
494 }, |
|
495 |
|
496 // nsIRequestObserver |
|
497 |
|
498 _startedSent: false, |
|
499 |
|
500 _watchdogTimer: null, |
|
501 |
|
502 _autoRestartDownload: false, |
|
503 _autoRestartCount: 0, |
|
504 |
|
505 startWatchdogTimer: function UP_startWatchdogTimer() { |
|
506 let watchdogTimeout = 120000; // 120 seconds |
|
507 try { |
|
508 watchdogTimeout = Services.prefs.getIntPref(PREF_DOWNLOAD_WATCHDOG_TIMEOUT); |
|
509 } catch (e) { |
|
510 // This means that the preference doesn't exist. watchdogTimeout will |
|
511 // retain its default assigned above. |
|
512 } |
|
513 if (watchdogTimeout <= 0) { |
|
514 // 0 implies don't bother using the watchdog timer at all. |
|
515 this._watchdogTimer = null; |
|
516 return; |
|
517 } |
|
518 if (this._watchdogTimer) { |
|
519 this._watchdogTimer.cancel(); |
|
520 } else { |
|
521 this._watchdogTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); |
|
522 } |
|
523 this._watchdogTimer.initWithCallback(this, watchdogTimeout, |
|
524 Ci.nsITimer.TYPE_ONE_SHOT); |
|
525 }, |
|
526 |
|
527 stopWatchdogTimer: function UP_stopWatchdogTimer() { |
|
528 if (this._watchdogTimer) { |
|
529 this._watchdogTimer.cancel(); |
|
530 this._watchdogTimer = null; |
|
531 } |
|
532 }, |
|
533 |
|
534 touchWatchdogTimer: function UP_touchWatchdogTimer() { |
|
535 this.startWatchdogTimer(); |
|
536 }, |
|
537 |
|
538 onStartRequest: function UP_onStartRequest(aRequest, aContext) { |
|
539 // Wait until onProgress to send the update-download-started event, in case |
|
540 // this request turns out to fail for some reason |
|
541 this._startedSent = false; |
|
542 this.startWatchdogTimer(); |
|
543 }, |
|
544 |
|
545 onStopRequest: function UP_onStopRequest(aRequest, aContext, aStatusCode) { |
|
546 this.stopWatchdogTimer(); |
|
547 Services.aus.removeDownloadListener(this); |
|
548 let paused = !Components.isSuccessCode(aStatusCode); |
|
549 if (!paused) { |
|
550 // The download was successful, no need to restart |
|
551 this._autoRestartDownload = false; |
|
552 } |
|
553 if (this._autoRestartDownload) { |
|
554 this._autoRestartDownload = false; |
|
555 let watchdogMaxRetries = Services.prefs.getIntPref(PREF_DOWNLOAD_WATCHDOG_MAX_RETRIES); |
|
556 this._autoRestartCount++; |
|
557 if (this._autoRestartCount > watchdogMaxRetries) { |
|
558 log("Download - retry count exceeded - error"); |
|
559 // We exceeded the max retries. Treat the download like an error, |
|
560 // which will give the user a chance to restart manually later. |
|
561 this._autoRestartCount = 0; |
|
562 if (Services.um.activeUpdate) { |
|
563 this.showUpdateError(Services.um.activeUpdate); |
|
564 } |
|
565 return; |
|
566 } |
|
567 log("Download - restarting download - attempt " + this._autoRestartCount); |
|
568 this.downloadUpdate(null); |
|
569 return; |
|
570 } |
|
571 this._autoRestartCount = 0; |
|
572 this.sendChromeEvent("update-download-stopped", { |
|
573 paused: paused |
|
574 }); |
|
575 }, |
|
576 |
|
577 // nsIProgressEventSink |
|
578 |
|
579 onProgress: function UP_onProgress(aRequest, aContext, aProgress, |
|
580 aProgressMax) { |
|
581 if (aProgress == aProgressMax) { |
|
582 // The update.mar validation done by onStopRequest may take |
|
583 // a while before the onStopRequest callback is made, so stop |
|
584 // the timer now. |
|
585 this.stopWatchdogTimer(); |
|
586 } else { |
|
587 this.touchWatchdogTimer(); |
|
588 } |
|
589 if (!this._startedSent) { |
|
590 this.sendChromeEvent("update-download-started", { |
|
591 total: aProgressMax |
|
592 }); |
|
593 this._startedSent = true; |
|
594 } |
|
595 |
|
596 this.sendChromeEvent("update-download-progress", { |
|
597 progress: aProgress, |
|
598 total: aProgressMax |
|
599 }); |
|
600 }, |
|
601 |
|
602 onStatus: function UP_onStatus(aRequest, aUpdate, aStatus, aStatusArg) { } |
|
603 }; |
|
604 |
|
605 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]); |