|
1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 'use strict'; |
|
6 |
|
7 Cu.import("resource://gre/modules/Services.jsm"); |
|
8 let gAppUpdater; |
|
9 |
|
10 let AboutFlyoutPanel = { |
|
11 init: function() { |
|
12 if (this._isInitialized) { |
|
13 Cu.reportError("Attempted to initialize AboutFlyoutPanel more than once"); |
|
14 } |
|
15 |
|
16 this._isInitialized = true; |
|
17 |
|
18 let self = this; |
|
19 this._elements = {}; |
|
20 [ |
|
21 ['versionLabel', 'about-version-label'], |
|
22 ['AboutFlyoutPanel', 'about-flyoutpanel'], |
|
23 ].forEach(function(aElement) { |
|
24 let [name, id] = aElement; |
|
25 XPCOMUtils.defineLazyGetter(self._elements, name, function() { |
|
26 return document.getElementById(id); |
|
27 }); |
|
28 }); |
|
29 |
|
30 this._topmostElement = this._elements.AboutFlyoutPanel; |
|
31 |
|
32 // Include the build ID if this is an "a#" (nightly or aurora) build |
|
33 let version = Services.appinfo.version; |
|
34 if (/a\d+$/.test(version)) { |
|
35 let buildID = Services.appinfo.appBuildID; |
|
36 let buildDate = buildID.slice(0,4) + "-" + buildID.slice(4,6) + |
|
37 "-" + buildID.slice(6,8); |
|
38 this._elements.versionLabel.textContent +=" (" + buildDate + ")"; |
|
39 } |
|
40 |
|
41 window.addEventListener('MozFlyoutPanelShowing', this, false); |
|
42 window.addEventListener('MozFlyoutPanelHiding', this, false); |
|
43 |
|
44 #if MOZ_UPDATE_CHANNEL != release |
|
45 let defaults = Services.prefs.getDefaultBranch(""); |
|
46 let channelLabel = document.getElementById("currentChannel"); |
|
47 channelLabel.value = defaults.getCharPref("app.update.channel"); |
|
48 #endif |
|
49 }, |
|
50 |
|
51 onPolicyClick: function(aEvent) { |
|
52 if (aEvent.button != 0) { |
|
53 return; |
|
54 } |
|
55 let url = Services.urlFormatter.formatURLPref("app.privacyURL"); |
|
56 BrowserUI.addAndShowTab(url, Browser.selectedTab); |
|
57 }, |
|
58 |
|
59 handleEvent: function(aEvent) { |
|
60 switch (aEvent.type) { |
|
61 case 'MozFlyoutPanelShowing': |
|
62 #ifdef MOZ_UPDATER |
|
63 this.appUpdater = new appUpdater(); |
|
64 gAppUpdater = this.appUpdater; |
|
65 #endif |
|
66 break; |
|
67 case 'MozFlyoutPanelHiding': |
|
68 #ifdef MOZ_UPDATER |
|
69 onUnload(); |
|
70 #endif |
|
71 break; |
|
72 } |
|
73 } |
|
74 }; |
|
75 |
|
76 #ifdef MOZ_UPDATER |
|
77 function onUnload(aEvent) { |
|
78 if (!gAppUpdater) { |
|
79 return; |
|
80 } |
|
81 |
|
82 if (gAppUpdater.isChecking) |
|
83 gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK); |
|
84 // Safe to call even when there isn't a download in progress. |
|
85 gAppUpdater.removeDownloadListener(); |
|
86 gAppUpdater = null; |
|
87 AboutFlyoutPanel.appUpdater = null; |
|
88 } |
|
89 |
|
90 function appUpdater() |
|
91 { |
|
92 this.updateDeck = document.getElementById("updateDeck"); |
|
93 |
|
94 XPCOMUtils.defineLazyServiceGetter(this, "aus", |
|
95 "@mozilla.org/updates/update-service;1", |
|
96 "nsIApplicationUpdateService"); |
|
97 XPCOMUtils.defineLazyServiceGetter(this, "checker", |
|
98 "@mozilla.org/updates/update-checker;1", |
|
99 "nsIUpdateChecker"); |
|
100 XPCOMUtils.defineLazyServiceGetter(this, "um", |
|
101 "@mozilla.org/updates/update-manager;1", |
|
102 "nsIUpdateManager"); |
|
103 |
|
104 this.bundle = Services.strings. |
|
105 createBundle("chrome://browser/locale/browser.properties"); |
|
106 |
|
107 this.updateBtn = document.getElementById("updateButton"); |
|
108 |
|
109 // The button label value must be set so its height is correct. |
|
110 this.setupUpdateButton("update.checkInsideButton"); |
|
111 |
|
112 let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual"); |
|
113 let manualLink = document.getElementById("manualLink"); |
|
114 manualLink.value = manualURL; |
|
115 manualLink.href = manualURL; |
|
116 document.getElementById("failedLink").href = manualURL; |
|
117 |
|
118 if (this.updateDisabledAndLocked) { |
|
119 this.selectPanel("adminDisabled"); |
|
120 return; |
|
121 } |
|
122 |
|
123 if (this.isPending || this.isApplied) { |
|
124 this.setupUpdateButton("update.restart." + |
|
125 (this.isMajor ? "upgradeButton" : "updateButton")); |
|
126 return; |
|
127 } |
|
128 |
|
129 if (this.aus.isOtherInstanceHandlingUpdates) { |
|
130 this.selectPanel("otherInstanceHandlingUpdates"); |
|
131 return; |
|
132 } |
|
133 |
|
134 if (this.isDownloading) { |
|
135 this.startDownload(); |
|
136 return; |
|
137 } |
|
138 |
|
139 if (this.updateEnabled && this.updateAuto) { |
|
140 this.selectPanel("checkingForUpdates"); |
|
141 this.isChecking = true; |
|
142 this.checker.checkForUpdates(this.updateCheckListener, true); |
|
143 return; |
|
144 } |
|
145 } |
|
146 |
|
147 appUpdater.prototype = |
|
148 { |
|
149 // true when there is an update check in progress. |
|
150 isChecking: false, |
|
151 |
|
152 // true when there is an update already staged / ready to be applied. |
|
153 get isPending() { |
|
154 if (this.update) { |
|
155 return this.update.state == "pending" || |
|
156 this.update.state == "pending-service"; |
|
157 } |
|
158 return this.um.activeUpdate && |
|
159 (this.um.activeUpdate.state == "pending" || |
|
160 this.um.activeUpdate.state == "pending-service"); |
|
161 }, |
|
162 |
|
163 // true when there is an update already installed in the background. |
|
164 get isApplied() { |
|
165 if (this.update) |
|
166 return this.update.state == "applied" || |
|
167 this.update.state == "applied-service"; |
|
168 return this.um.activeUpdate && |
|
169 (this.um.activeUpdate.state == "applied" || |
|
170 this.um.activeUpdate.state == "applied-service"); |
|
171 }, |
|
172 |
|
173 // true when there is an update download in progress. |
|
174 get isDownloading() { |
|
175 if (this.update) |
|
176 return this.update.state == "downloading"; |
|
177 return this.um.activeUpdate && |
|
178 this.um.activeUpdate.state == "downloading"; |
|
179 }, |
|
180 |
|
181 // true when the update type is major. |
|
182 get isMajor() { |
|
183 if (this.update) |
|
184 return this.update.type == "major"; |
|
185 return this.um.activeUpdate.type == "major"; |
|
186 }, |
|
187 |
|
188 // true when updating is disabled by an administrator. |
|
189 get updateDisabledAndLocked() { |
|
190 return !this.updateEnabled && |
|
191 Services.prefs.prefIsLocked("app.update.enabled"); |
|
192 }, |
|
193 |
|
194 // true when updating is enabled. |
|
195 get updateEnabled() { |
|
196 let updatesEnabled = true; |
|
197 try { |
|
198 updatesEnabled = Services.prefs.getBoolPref("app.update.metro.enabled"); |
|
199 } |
|
200 catch (e) { } |
|
201 if (!updatesEnabled) { |
|
202 return false; |
|
203 } |
|
204 |
|
205 try { |
|
206 updatesEnabled = Services.prefs.getBoolPref("app.update.enabled") |
|
207 } |
|
208 catch (e) { } |
|
209 |
|
210 return updatesEnabled; |
|
211 }, |
|
212 |
|
213 // true when updating in background is enabled. |
|
214 get backgroundUpdateEnabled() { |
|
215 return this.updateEnabled && |
|
216 gAppUpdater.aus.canStageUpdates; |
|
217 }, |
|
218 |
|
219 // true when updating is automatic. |
|
220 get updateAuto() { |
|
221 try { |
|
222 return Services.prefs.getBoolPref("app.update.auto"); |
|
223 } |
|
224 catch (e) { } |
|
225 return true; // Firefox default is true |
|
226 }, |
|
227 |
|
228 /** |
|
229 * Sets the deck's selected panel. |
|
230 * |
|
231 * @param aChildID |
|
232 * The id of the deck's child to select. |
|
233 */ |
|
234 selectPanel: function(aChildID) { |
|
235 this.updateDeck.selectedPanel = document.getElementById(aChildID); |
|
236 this.updateBtn.disabled = (aChildID != "updateButtonBox"); |
|
237 }, |
|
238 |
|
239 /** |
|
240 * Sets the update button's label and accesskey. |
|
241 * |
|
242 * @param aKeyPrefix |
|
243 * The prefix for the properties file entry to use for setting the |
|
244 * label and accesskey. |
|
245 */ |
|
246 setupUpdateButton: function(aKeyPrefix) { |
|
247 this.updateBtn.label = this.bundle.GetStringFromName(aKeyPrefix + ".label"); |
|
248 this.updateBtn.accessKey = this.bundle.GetStringFromName(aKeyPrefix + ".accesskey"); |
|
249 if (!document.commandDispatcher.focusedElement || |
|
250 document.commandDispatcher.focusedElement == this.updateBtn) |
|
251 this.updateBtn.focus(); |
|
252 }, |
|
253 |
|
254 /** |
|
255 * Handles oncommand for the update button. |
|
256 */ |
|
257 buttonOnCommand: function() { |
|
258 if (this.isPending || this.isApplied) { |
|
259 // Notify all windows that an application quit has been requested. |
|
260 let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]. |
|
261 createInstance(Components.interfaces.nsISupportsPRBool); |
|
262 Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart"); |
|
263 |
|
264 // Something aborted the quit process. |
|
265 if (cancelQuit.data) |
|
266 return; |
|
267 |
|
268 let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]. |
|
269 getService(Components.interfaces.nsIAppStartup); |
|
270 |
|
271 // If already in safe mode restart in safe mode (bug 327119) |
|
272 if (Services.appinfo.inSafeMode) { |
|
273 appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit); |
|
274 return; |
|
275 } |
|
276 |
|
277 Services.metro.updatePending = true; |
|
278 appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit | |
|
279 Components.interfaces.nsIAppStartup.eRestartTouchEnvironment); |
|
280 return; |
|
281 } |
|
282 |
|
283 // XXX We can't create dialogs in metro, and we currently don't support addons, so |
|
284 // commenting this out for now. |
|
285 /* const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul"; |
|
286 // Firefox no longer displays a license for updates and the licenseURL check |
|
287 // is just in case a distibution does. |
|
288 if (this.update && (this.update.billboardURL || this.update.licenseURL || |
|
289 this.addons.length != 0)) { |
|
290 var ary = null; |
|
291 ary = Components.classes["@mozilla.org/supports-array;1"]. |
|
292 createInstance(Components.interfaces.nsISupportsArray); |
|
293 ary.AppendElement(this.update); |
|
294 var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no"; |
|
295 Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary); |
|
296 window.close(); |
|
297 return; |
|
298 }*/ |
|
299 |
|
300 this.selectPanel("checkingForUpdates"); |
|
301 this.isChecking = true; |
|
302 this.checker.checkForUpdates(this.updateCheckListener, true); |
|
303 }, |
|
304 |
|
305 /** |
|
306 * Implements nsIUpdateCheckListener. The methods implemented by |
|
307 * nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload |
|
308 * to make it clear which are used by each interface. |
|
309 */ |
|
310 updateCheckListener: { |
|
311 /** |
|
312 * See nsIUpdateService.idl |
|
313 */ |
|
314 onCheckComplete: function(aRequest, aUpdates, aUpdateCount) { |
|
315 gAppUpdater.isChecking = false; |
|
316 gAppUpdater.update = gAppUpdater.aus. |
|
317 selectUpdate(aUpdates, aUpdates.length); |
|
318 if (!gAppUpdater.update) { |
|
319 gAppUpdater.selectPanel("noUpdatesFound"); |
|
320 return; |
|
321 } |
|
322 |
|
323 if (!gAppUpdater.aus.canApplyUpdates) { |
|
324 gAppUpdater.selectPanel("manualUpdate"); |
|
325 return; |
|
326 } |
|
327 |
|
328 // Firefox no longer displays a license for updates and the licenseURL |
|
329 // check is just in case a distibution does. |
|
330 if (gAppUpdater.update.billboardURL || gAppUpdater.update.licenseURL) { |
|
331 gAppUpdater.selectPanel("updateButtonBox"); |
|
332 gAppUpdater.setupUpdateButton("update.openUpdateUI." + |
|
333 (this.isMajor ? "upgradeButton" |
|
334 : "applyButton")); |
|
335 return; |
|
336 } |
|
337 |
|
338 if (!gAppUpdater.update.appVersion || |
|
339 Services.vc.compare(gAppUpdater.update.appVersion, |
|
340 Services.appinfo.version) == 0) { |
|
341 gAppUpdater.startDownload(); |
|
342 return; |
|
343 } |
|
344 |
|
345 gAppUpdater.checkAddonCompatibility(); |
|
346 }, |
|
347 |
|
348 /** |
|
349 * See nsIUpdateService.idl |
|
350 */ |
|
351 onError: function(aRequest, aUpdate) { |
|
352 // Errors in the update check are treated as no updates found. If the |
|
353 // update check fails repeatedly without a success the user will be |
|
354 // notified with the normal app update user interface so this is safe. |
|
355 gAppUpdater.isChecking = false; |
|
356 gAppUpdater.selectPanel("noUpdatesFound"); |
|
357 }, |
|
358 |
|
359 /** |
|
360 * See nsISupports.idl |
|
361 */ |
|
362 QueryInterface: function(aIID) { |
|
363 if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) && |
|
364 !aIID.equals(Components.interfaces.nsISupports)) |
|
365 throw Components.results.NS_ERROR_NO_INTERFACE; |
|
366 return this; |
|
367 } |
|
368 }, |
|
369 |
|
370 /** |
|
371 * Checks the compatibility of add-ons for the application update. |
|
372 */ |
|
373 checkAddonCompatibility: function() { |
|
374 try { |
|
375 var hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID); |
|
376 } |
|
377 catch (e) { } |
|
378 |
|
379 var self = this; |
|
380 AddonManager.getAllAddons(function(aAddons) { |
|
381 self.addons = []; |
|
382 self.addonsCheckedCount = 0; |
|
383 aAddons.forEach(function(aAddon) { |
|
384 // Protect against code that overrides the add-ons manager and doesn't |
|
385 // implement the isCompatibleWith or the findUpdates method. |
|
386 if (!("isCompatibleWith" in aAddon) || !("findUpdates" in aAddon)) { |
|
387 let errMsg = "Add-on doesn't implement either the isCompatibleWith " + |
|
388 "or the findUpdates method!"; |
|
389 if (aAddon.id) |
|
390 errMsg += " Add-on ID: " + aAddon.id; |
|
391 Components.utils.reportError(errMsg); |
|
392 return; |
|
393 } |
|
394 |
|
395 // If an add-on isn't appDisabled and isn't userDisabled then it is |
|
396 // either active now or the user expects it to be active after the |
|
397 // restart. If that is the case and the add-on is not installed by the |
|
398 // application and is not compatible with the new application version |
|
399 // then the user should be warned that the add-on will become |
|
400 // incompatible. If an addon's type equals plugin it is skipped since |
|
401 // checking plugins compatibility information isn't supported and |
|
402 // getting the scope property of a plugin breaks in some environments |
|
403 // (see bug 566787). The hotfix add-on is also ignored as it shouldn't |
|
404 // block the user from upgrading. |
|
405 try { |
|
406 if (aAddon.type != "plugin" && aAddon.id != hotfixID && |
|
407 !aAddon.appDisabled && !aAddon.userDisabled && |
|
408 aAddon.scope != AddonManager.SCOPE_APPLICATION && |
|
409 aAddon.isCompatible && |
|
410 !aAddon.isCompatibleWith(self.update.appVersion, |
|
411 self.update.platformVersion)) |
|
412 self.addons.push(aAddon); |
|
413 } |
|
414 catch (e) { |
|
415 Components.utils.reportError(e); |
|
416 } |
|
417 }); |
|
418 self.addonsTotalCount = self.addons.length; |
|
419 if (self.addonsTotalCount == 0) { |
|
420 self.startDownload(); |
|
421 return; |
|
422 } |
|
423 |
|
424 self.checkAddonsForUpdates(); |
|
425 }); |
|
426 }, |
|
427 |
|
428 /** |
|
429 * Checks if there are updates for add-ons that are incompatible with the |
|
430 * application update. |
|
431 */ |
|
432 checkAddonsForUpdates: function() { |
|
433 this.addons.forEach(function(aAddon) { |
|
434 aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, |
|
435 this.update.appVersion, |
|
436 this.update.platformVersion); |
|
437 }, this); |
|
438 }, |
|
439 |
|
440 /** |
|
441 * See XPIProvider.jsm |
|
442 */ |
|
443 onCompatibilityUpdateAvailable: function(aAddon) { |
|
444 for (var i = 0; i < this.addons.length; ++i) { |
|
445 if (this.addons[i].id == aAddon.id) { |
|
446 this.addons.splice(i, 1); |
|
447 break; |
|
448 } |
|
449 } |
|
450 }, |
|
451 |
|
452 /** |
|
453 * See XPIProvider.jsm |
|
454 */ |
|
455 onUpdateAvailable: function(aAddon, aInstall) { |
|
456 if (!Services.blocklist.isAddonBlocklisted(aAddon, |
|
457 this.update.appVersion, |
|
458 this.update.platformVersion)) { |
|
459 // Compatibility or new version updates mean the same thing here. |
|
460 this.onCompatibilityUpdateAvailable(aAddon); |
|
461 } |
|
462 }, |
|
463 |
|
464 /** |
|
465 * See XPIProvider.jsm |
|
466 */ |
|
467 onUpdateFinished: function(aAddon) { |
|
468 ++this.addonsCheckedCount; |
|
469 |
|
470 if (this.addonsCheckedCount < this.addonsTotalCount) |
|
471 return; |
|
472 |
|
473 if (this.addons.length == 0) { |
|
474 // Compatibility updates or new version updates were found for all add-ons |
|
475 this.startDownload(); |
|
476 return; |
|
477 } |
|
478 |
|
479 this.selectPanel("updateButtonBox"); |
|
480 this.setupUpdateButton("update.openUpdateUI." + |
|
481 (this.isMajor ? "upgradeButton" : "applyButton")); |
|
482 }, |
|
483 |
|
484 /** |
|
485 * Starts the download of an update mar. |
|
486 */ |
|
487 startDownload: function() { |
|
488 if (!this.update) |
|
489 this.update = this.um.activeUpdate; |
|
490 this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag); |
|
491 this.update.setProperty("foregroundDownload", "true"); |
|
492 |
|
493 this.aus.pauseDownload(); |
|
494 let state = this.aus.downloadUpdate(this.update, false); |
|
495 if (state == "failed") { |
|
496 this.selectPanel("downloadFailed"); |
|
497 return; |
|
498 } |
|
499 |
|
500 this.setupDownloadingUI(); |
|
501 }, |
|
502 |
|
503 /** |
|
504 * Switches to the UI responsible for tracking the download. |
|
505 */ |
|
506 setupDownloadingUI: function() { |
|
507 this.downloadStatus = document.getElementById("downloadStatus"); |
|
508 this.downloadStatus.textContent = |
|
509 DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size); |
|
510 this.selectPanel("downloading"); |
|
511 this.aus.addDownloadListener(this); |
|
512 }, |
|
513 |
|
514 removeDownloadListener: function() { |
|
515 if (this.aus) { |
|
516 this.aus.removeDownloadListener(this); |
|
517 } |
|
518 }, |
|
519 |
|
520 /** |
|
521 * See nsIRequestObserver.idl |
|
522 */ |
|
523 onStartRequest: function(aRequest, aContext) { |
|
524 }, |
|
525 |
|
526 /** |
|
527 * See nsIRequestObserver.idl |
|
528 */ |
|
529 onStopRequest: function(aRequest, aContext, aStatusCode) { |
|
530 switch (aStatusCode) { |
|
531 case Components.results.NS_ERROR_UNEXPECTED: |
|
532 if (this.update.selectedPatch.state == "download-failed" && |
|
533 (this.update.isCompleteUpdate || this.update.patchCount != 2)) { |
|
534 // Verification error of complete patch, informational text is held in |
|
535 // the update object. |
|
536 this.removeDownloadListener(); |
|
537 this.selectPanel("downloadFailed"); |
|
538 break; |
|
539 } |
|
540 // Verification failed for a partial patch, complete patch is now |
|
541 // downloading so return early and do NOT remove the download listener! |
|
542 break; |
|
543 case Components.results.NS_BINDING_ABORTED: |
|
544 // Do not remove UI listener since the user may resume downloading again. |
|
545 break; |
|
546 case Components.results.NS_OK: |
|
547 this.removeDownloadListener(); |
|
548 if (this.backgroundUpdateEnabled) { |
|
549 this.selectPanel("applying"); |
|
550 let update = this.um.activeUpdate; |
|
551 let self = this; |
|
552 Services.obs.addObserver(function updateStaged(aSubject, aTopic, aData) { |
|
553 // Update the UI when the background updater is finished |
|
554 let status = aData; |
|
555 if (status == "applied" || status == "applied-service" || |
|
556 status == "pending" || status == "pending-service") { |
|
557 // If the update is successfully applied, or if the updater has |
|
558 // fallen back to non-staged updates, show the Restart to Update |
|
559 // button. |
|
560 self.selectPanel("updateButtonBox"); |
|
561 self.setupUpdateButton("update.restart." + |
|
562 (self.isMajor ? "upgradeButton" : "updateButton")); |
|
563 } else if (status == "failed") { |
|
564 // Background update has failed, let's show the UI responsible for |
|
565 // prompting the user to update manually. |
|
566 self.selectPanel("downloadFailed"); |
|
567 } else if (status == "downloading") { |
|
568 // We've fallen back to downloading the full update because the |
|
569 // partial update failed to get staged in the background. |
|
570 // Therefore we need to keep our observer. |
|
571 self.setupDownloadingUI(); |
|
572 return; |
|
573 } |
|
574 Services.obs.removeObserver(updateStaged, "update-staged"); |
|
575 }, "update-staged", false); |
|
576 } else { |
|
577 this.selectPanel("updateButtonBox"); |
|
578 this.setupUpdateButton("update.restart." + |
|
579 (this.isMajor ? "upgradeButton" : "updateButton")); |
|
580 } |
|
581 break; |
|
582 default: |
|
583 this.removeDownloadListener(); |
|
584 this.selectPanel("downloadFailed"); |
|
585 break; |
|
586 } |
|
587 |
|
588 }, |
|
589 |
|
590 /** |
|
591 * See nsIProgressEventSink.idl |
|
592 */ |
|
593 onStatus: function(aRequest, aContext, aStatus, aStatusArg) { |
|
594 }, |
|
595 |
|
596 /** |
|
597 * See nsIProgressEventSink.idl |
|
598 */ |
|
599 onProgress: function(aRequest, aContext, aProgress, aProgressMax) { |
|
600 this.downloadStatus.textContent = |
|
601 DownloadUtils.getTransferTotal(aProgress, aProgressMax); |
|
602 }, |
|
603 |
|
604 /** |
|
605 * See nsISupports.idl |
|
606 */ |
|
607 QueryInterface: function(aIID) { |
|
608 if (!aIID.equals(Components.interfaces.nsIProgressEventSink) && |
|
609 !aIID.equals(Components.interfaces.nsIRequestObserver) && |
|
610 !aIID.equals(Components.interfaces.nsISupports)) |
|
611 throw Components.results.NS_ERROR_NO_INTERFACE; |
|
612 return this; |
|
613 } |
|
614 }; |
|
615 #endif |