michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: // Services = object with smart getters for common XPCOM services michael@0: Components.utils.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: const PREF_EM_HOTFIX_ID = "extensions.hotfix.id"; michael@0: michael@0: #ifdef TOR_BROWSER_VERSION michael@0: # Add double-quotes back on (stripped by JarMaker.py). michael@0: #expand const TOR_BROWSER_VERSION = "__TOR_BROWSER_VERSION__"; michael@0: #endif michael@0: michael@0: function init(aEvent) michael@0: { michael@0: if (aEvent.target != document) michael@0: return; michael@0: michael@0: try { michael@0: var distroId = Services.prefs.getCharPref("distribution.id"); michael@0: if (distroId) { michael@0: var distroVersion = Services.prefs.getCharPref("distribution.version"); michael@0: michael@0: var distroIdField = document.getElementById("distributionId"); michael@0: distroIdField.value = distroId + " - " + distroVersion; michael@0: distroIdField.style.display = "block"; michael@0: michael@0: try { michael@0: // This is in its own try catch due to bug 895473 and bug 900925. michael@0: var distroAbout = Services.prefs.getComplexValue("distribution.about", michael@0: Components.interfaces.nsISupportsString); michael@0: var distroField = document.getElementById("distribution"); michael@0: distroField.value = distroAbout; michael@0: distroField.style.display = "block"; michael@0: } michael@0: catch (ex) { michael@0: // Pref is unset michael@0: Components.utils.reportError(ex); michael@0: } michael@0: } michael@0: } michael@0: catch (e) { michael@0: // Pref is unset michael@0: } michael@0: michael@0: // Include the build ID and display warning if this is an "a#" (nightly or aurora) build michael@0: let version = Services.appinfo.version; michael@0: if (/a\d+$/.test(version)) { michael@0: document.getElementById("experimental").hidden = false; michael@0: document.getElementById("communityDesc").hidden = true; michael@0: } michael@0: michael@0: #ifdef TOR_BROWSER_VERSION michael@0: let versionElem = document.getElementById("version"); michael@0: if (versionElem) michael@0: versionElem.textContent += " (Tor Browser " + TOR_BROWSER_VERSION + ")"; michael@0: #endif michael@0: michael@0: #ifdef MOZ_UPDATER michael@0: gAppUpdater = new appUpdater(); michael@0: michael@0: #if MOZ_UPDATE_CHANNEL != release michael@0: let defaults = Services.prefs.getDefaultBranch(""); michael@0: let channelLabel = document.getElementById("currentChannel"); michael@0: channelLabel.value = defaults.getCharPref("app.update.channel"); michael@0: #endif michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: // it may not be sized at this point, and we need its width to calculate its position michael@0: window.sizeToContent(); michael@0: window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef MOZ_UPDATER michael@0: Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Components.utils.import("resource://gre/modules/DownloadUtils.jsm"); michael@0: Components.utils.import("resource://gre/modules/AddonManager.jsm"); michael@0: michael@0: var gAppUpdater; michael@0: michael@0: function onUnload(aEvent) { michael@0: if (gAppUpdater.isChecking) michael@0: gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK); michael@0: // Safe to call even when there isn't a download in progress. michael@0: gAppUpdater.removeDownloadListener(); michael@0: gAppUpdater = null; michael@0: } michael@0: michael@0: michael@0: function appUpdater() michael@0: { michael@0: this.updateDeck = document.getElementById("updateDeck"); michael@0: michael@0: // Hide the update deck when there is already an update window open to avoid michael@0: // syncing issues between them. michael@0: if (Services.wm.getMostRecentWindow("Update:Wizard")) { michael@0: this.updateDeck.hidden = true; michael@0: return; michael@0: } michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "aus", michael@0: "@mozilla.org/updates/update-service;1", michael@0: "nsIApplicationUpdateService"); michael@0: XPCOMUtils.defineLazyServiceGetter(this, "checker", michael@0: "@mozilla.org/updates/update-checker;1", michael@0: "nsIUpdateChecker"); michael@0: XPCOMUtils.defineLazyServiceGetter(this, "um", michael@0: "@mozilla.org/updates/update-manager;1", michael@0: "nsIUpdateManager"); michael@0: michael@0: this.bundle = Services.strings. michael@0: createBundle("chrome://browser/locale/browser.properties"); michael@0: michael@0: let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual"); michael@0: let manualLink = document.getElementById("manualLink"); michael@0: manualLink.value = manualURL; michael@0: manualLink.href = manualURL; michael@0: document.getElementById("failedLink").href = manualURL; michael@0: michael@0: if (this.updateDisabledAndLocked) { michael@0: this.selectPanel("adminDisabled"); michael@0: return; michael@0: } michael@0: michael@0: if (this.isPending || this.isApplied) { michael@0: this.selectPanel("apply"); michael@0: return; michael@0: } michael@0: michael@0: if (this.aus.isOtherInstanceHandlingUpdates) { michael@0: this.selectPanel("otherInstanceHandlingUpdates"); michael@0: return; michael@0: } michael@0: michael@0: if (this.isDownloading) { michael@0: this.startDownload(); michael@0: // selectPanel("downloading") is called from setupDownloadingUI(). michael@0: return; michael@0: } michael@0: michael@0: // Honor the "Never check for updates" option by not only disabling background michael@0: // update checks, but also in the About dialog, by presenting a michael@0: // "Check for updates" button. michael@0: // If updates are found, the user is then asked if he wants to "Update to ". michael@0: if (!this.updateEnabled) { michael@0: this.selectPanel("checkForUpdates"); michael@0: return; michael@0: } michael@0: michael@0: // That leaves the options michael@0: // "Check for updates, but let me choose whether to install them", and michael@0: // "Automatically install updates". michael@0: // In both cases, we check for updates without asking. michael@0: // In the "let me choose" case, we ask before downloading though, in onCheckComplete. michael@0: this.checkForUpdates(); michael@0: } michael@0: michael@0: appUpdater.prototype = michael@0: { michael@0: // true when there is an update check in progress. michael@0: isChecking: false, michael@0: michael@0: // true when there is an update already staged / ready to be applied. michael@0: get isPending() { michael@0: if (this.update) { michael@0: return this.update.state == "pending" || michael@0: this.update.state == "pending-service"; michael@0: } michael@0: return this.um.activeUpdate && michael@0: (this.um.activeUpdate.state == "pending" || michael@0: this.um.activeUpdate.state == "pending-service"); michael@0: }, michael@0: michael@0: // true when there is an update already installed in the background. michael@0: get isApplied() { michael@0: if (this.update) michael@0: return this.update.state == "applied" || michael@0: this.update.state == "applied-service"; michael@0: return this.um.activeUpdate && michael@0: (this.um.activeUpdate.state == "applied" || michael@0: this.um.activeUpdate.state == "applied-service"); michael@0: }, michael@0: michael@0: // true when there is an update download in progress. michael@0: get isDownloading() { michael@0: if (this.update) michael@0: return this.update.state == "downloading"; michael@0: return this.um.activeUpdate && michael@0: this.um.activeUpdate.state == "downloading"; michael@0: }, michael@0: michael@0: // true when updating is disabled by an administrator. michael@0: get updateDisabledAndLocked() { michael@0: return !this.updateEnabled && michael@0: Services.prefs.prefIsLocked("app.update.enabled"); michael@0: }, michael@0: michael@0: // true when updating is enabled. michael@0: get updateEnabled() { michael@0: try { michael@0: return Services.prefs.getBoolPref("app.update.enabled"); michael@0: } michael@0: catch (e) { } michael@0: return true; // Firefox default is true michael@0: }, michael@0: michael@0: // true when updating in background is enabled. michael@0: get backgroundUpdateEnabled() { michael@0: return this.updateEnabled && michael@0: gAppUpdater.aus.canStageUpdates; michael@0: }, michael@0: michael@0: // true when updating is automatic. michael@0: get updateAuto() { michael@0: try { michael@0: return Services.prefs.getBoolPref("app.update.auto"); michael@0: } michael@0: catch (e) { } michael@0: return true; // Firefox default is true michael@0: }, michael@0: michael@0: /** michael@0: * Sets the panel of the updateDeck. michael@0: * michael@0: * @param aChildID michael@0: * The id of the deck's child to select, e.g. "apply". michael@0: */ michael@0: selectPanel: function(aChildID) { michael@0: let panel = document.getElementById(aChildID); michael@0: michael@0: let button = panel.querySelector("button"); michael@0: if (button) { michael@0: if (aChildID == "downloadAndInstall") { michael@0: let updateVersion = gAppUpdater.update.displayVersion; michael@0: button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1); michael@0: button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey"); michael@0: } michael@0: this.updateDeck.selectedPanel = panel; michael@0: if (!document.commandDispatcher.focusedElement || // don't steal the focus michael@0: document.commandDispatcher.focusedElement.localName == "button") // except from the other buttons michael@0: button.focus(); michael@0: michael@0: } else { michael@0: this.updateDeck.selectedPanel = panel; michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Check for updates michael@0: */ michael@0: checkForUpdates: function() { michael@0: this.selectPanel("checkingForUpdates"); michael@0: this.isChecking = true; michael@0: this.checker.checkForUpdates(this.updateCheckListener, true); michael@0: // after checking, onCheckComplete() is called michael@0: }, michael@0: michael@0: /** michael@0: * Check for addon compat, or start the download right away michael@0: */ michael@0: doUpdate: function() { michael@0: // skip the compatibility check if the update doesn't provide appVersion, michael@0: // or the appVersion is unchanged, e.g. nightly update michael@0: #ifdef TOR_BROWSER_UPDATE michael@0: let pkgVersion = TOR_BROWSER_VERSION; michael@0: #else michael@0: let pkgVersion = Services.appinfo.version; michael@0: #endif michael@0: if (!this.update.appVersion || michael@0: Services.vc.compare(gAppUpdater.update.appVersion, pkgVersion) == 0) { michael@0: this.startDownload(); michael@0: } else { michael@0: this.checkAddonCompatibility(); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Handles oncommand for the "Restart to Update" button michael@0: * which is presented after the download has been downloaded. michael@0: */ michael@0: buttonRestartAfterDownload: function() { michael@0: if (!this.isPending && !this.isApplied) michael@0: return; michael@0: michael@0: // Notify all windows that an application quit has been requested. michael@0: let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]. michael@0: createInstance(Components.interfaces.nsISupportsPRBool); michael@0: Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart"); michael@0: michael@0: // Something aborted the quit process. michael@0: if (cancelQuit.data) michael@0: return; michael@0: michael@0: let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]. michael@0: getService(Components.interfaces.nsIAppStartup); michael@0: michael@0: // If already in safe mode restart in safe mode (bug 327119) michael@0: if (Services.appinfo.inSafeMode) { michael@0: appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit); michael@0: return; michael@0: } michael@0: michael@0: appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit | michael@0: Components.interfaces.nsIAppStartup.eRestart); michael@0: }, michael@0: michael@0: /** michael@0: * Handles oncommand for the "Apply Update…" button michael@0: * which is presented if we need to show the billboard or license. michael@0: */ michael@0: buttonApplyBillboard: function() { michael@0: const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul"; michael@0: var ary = null; michael@0: ary = Components.classes["@mozilla.org/supports-array;1"]. michael@0: createInstance(Components.interfaces.nsISupportsArray); michael@0: ary.AppendElement(this.update); michael@0: var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no"; michael@0: Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary); michael@0: window.close(); // close the "About" window; updates.xul takes over. michael@0: }, michael@0: michael@0: /** michael@0: * Implements nsIUpdateCheckListener. The methods implemented by michael@0: * nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload michael@0: * to make it clear which are used by each interface. michael@0: */ michael@0: updateCheckListener: { michael@0: /** michael@0: * See nsIUpdateService.idl michael@0: */ michael@0: onCheckComplete: function(aRequest, aUpdates, aUpdateCount) { michael@0: gAppUpdater.isChecking = false; michael@0: gAppUpdater.update = gAppUpdater.aus. michael@0: selectUpdate(aUpdates, aUpdates.length); michael@0: if (!gAppUpdater.update) { michael@0: gAppUpdater.selectPanel("noUpdatesFound"); michael@0: return; michael@0: } michael@0: michael@0: if (gAppUpdater.update.unsupported) { michael@0: if (gAppUpdater.update.detailsURL) { michael@0: let unsupportedLink = document.getElementById("unsupportedLink"); michael@0: unsupportedLink.href = gAppUpdater.update.detailsURL; michael@0: } michael@0: gAppUpdater.selectPanel("unsupportedSystem"); michael@0: return; michael@0: } michael@0: michael@0: if (!gAppUpdater.aus.canApplyUpdates) { michael@0: gAppUpdater.selectPanel("manualUpdate"); michael@0: return; michael@0: } michael@0: michael@0: // Firefox no longer displays a license for updates and the licenseURL michael@0: // check is just in case a distibution does. michael@0: if (gAppUpdater.update.billboardURL || gAppUpdater.update.licenseURL) { michael@0: gAppUpdater.selectPanel("applyBillboard"); michael@0: return; michael@0: } michael@0: michael@0: if (gAppUpdater.updateAuto) // automatically download and install michael@0: gAppUpdater.doUpdate(); michael@0: else // ask michael@0: gAppUpdater.selectPanel("downloadAndInstall"); michael@0: }, michael@0: michael@0: /** michael@0: * See nsIUpdateService.idl michael@0: */ michael@0: onError: function(aRequest, aUpdate) { michael@0: // Errors in the update check are treated as no updates found. If the michael@0: // update check fails repeatedly without a success the user will be michael@0: // notified with the normal app update user interface so this is safe. michael@0: gAppUpdater.isChecking = false; michael@0: gAppUpdater.selectPanel("noUpdatesFound"); michael@0: }, michael@0: michael@0: /** michael@0: * See nsISupports.idl michael@0: */ michael@0: QueryInterface: function(aIID) { michael@0: if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) && michael@0: !aIID.equals(Components.interfaces.nsISupports)) michael@0: throw Components.results.NS_ERROR_NO_INTERFACE; michael@0: return this; michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Checks the compatibility of add-ons for the application update. michael@0: */ michael@0: checkAddonCompatibility: function() { michael@0: try { michael@0: var hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID); michael@0: } michael@0: catch (e) { } michael@0: michael@0: var self = this; michael@0: AddonManager.getAllAddons(function(aAddons) { michael@0: self.addons = []; michael@0: self.addonsCheckedCount = 0; michael@0: aAddons.forEach(function(aAddon) { michael@0: // Protect against code that overrides the add-ons manager and doesn't michael@0: // implement the isCompatibleWith or the findUpdates method. michael@0: if (!("isCompatibleWith" in aAddon) || !("findUpdates" in aAddon)) { michael@0: let errMsg = "Add-on doesn't implement either the isCompatibleWith " + michael@0: "or the findUpdates method!"; michael@0: if (aAddon.id) michael@0: errMsg += " Add-on ID: " + aAddon.id; michael@0: Components.utils.reportError(errMsg); michael@0: return; michael@0: } michael@0: michael@0: // If an add-on isn't appDisabled and isn't userDisabled then it is michael@0: // either active now or the user expects it to be active after the michael@0: // restart. If that is the case and the add-on is not installed by the michael@0: // application and is not compatible with the new application version michael@0: // then the user should be warned that the add-on will become michael@0: // incompatible. If an addon's type equals plugin it is skipped since michael@0: // checking plugins compatibility information isn't supported and michael@0: // getting the scope property of a plugin breaks in some environments michael@0: // (see bug 566787). The hotfix add-on is also ignored as it shouldn't michael@0: // block the user from upgrading. michael@0: try { michael@0: #ifdef TOR_BROWSER_UPDATE michael@0: let compatVersion = self.update.platformVersion; michael@0: #else michael@0: let compatVersion = self.update.appVersion; michael@0: #endif michael@0: if (aAddon.type != "plugin" && aAddon.id != hotfixID && michael@0: !aAddon.appDisabled && !aAddon.userDisabled && michael@0: aAddon.scope != AddonManager.SCOPE_APPLICATION && michael@0: aAddon.isCompatible && michael@0: !aAddon.isCompatibleWith(compatVersion, michael@0: self.update.platformVersion)) michael@0: self.addons.push(aAddon); michael@0: } michael@0: catch (e) { michael@0: Components.utils.reportError(e); michael@0: } michael@0: }); michael@0: self.addonsTotalCount = self.addons.length; michael@0: if (self.addonsTotalCount == 0) { michael@0: self.startDownload(); michael@0: return; michael@0: } michael@0: michael@0: self.checkAddonsForUpdates(); michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Checks if there are updates for add-ons that are incompatible with the michael@0: * application update. michael@0: */ michael@0: checkAddonsForUpdates: function() { michael@0: this.addons.forEach(function(aAddon) { michael@0: #ifdef TOR_BROWSER_UPDATE michael@0: let compatVersion = this.update.platformVersion; michael@0: #else michael@0: let compatVersion = this.update.appVersion; michael@0: #endif michael@0: aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, michael@0: compatVersion, michael@0: this.update.platformVersion); michael@0: }, this); michael@0: }, michael@0: michael@0: /** michael@0: * See XPIProvider.jsm michael@0: */ michael@0: onCompatibilityUpdateAvailable: function(aAddon) { michael@0: for (var i = 0; i < this.addons.length; ++i) { michael@0: if (this.addons[i].id == aAddon.id) { michael@0: this.addons.splice(i, 1); michael@0: break; michael@0: } michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * See XPIProvider.jsm michael@0: */ michael@0: onUpdateAvailable: function(aAddon, aInstall) { michael@0: #ifdef TOR_BROWSER_UPDATE michael@0: let compatVersion = this.update.platformVersion; michael@0: #else michael@0: let compatVersion = this.update.appVersion; michael@0: #endif michael@0: if (!Services.blocklist.isAddonBlocklisted(aAddon, michael@0: compatVersion, michael@0: this.update.platformVersion)) { michael@0: // Compatibility or new version updates mean the same thing here. michael@0: this.onCompatibilityUpdateAvailable(aAddon); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * See XPIProvider.jsm michael@0: */ michael@0: onUpdateFinished: function(aAddon) { michael@0: ++this.addonsCheckedCount; michael@0: michael@0: if (this.addonsCheckedCount < this.addonsTotalCount) michael@0: return; michael@0: michael@0: if (this.addons.length == 0) { michael@0: // Compatibility updates or new version updates were found for all add-ons michael@0: this.startDownload(); michael@0: return; michael@0: } michael@0: michael@0: this.selectPanel("apply"); michael@0: }, michael@0: michael@0: /** michael@0: * Starts the download of an update mar. michael@0: */ michael@0: startDownload: function() { michael@0: if (!this.update) michael@0: this.update = this.um.activeUpdate; michael@0: this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag); michael@0: this.update.setProperty("foregroundDownload", "true"); michael@0: michael@0: this.aus.pauseDownload(); michael@0: let state = this.aus.downloadUpdate(this.update, false); michael@0: if (state == "failed") { michael@0: this.selectPanel("downloadFailed"); michael@0: return; michael@0: } michael@0: michael@0: this.setupDownloadingUI(); michael@0: }, michael@0: michael@0: /** michael@0: * Switches to the UI responsible for tracking the download. michael@0: */ michael@0: setupDownloadingUI: function() { michael@0: this.downloadStatus = document.getElementById("downloadStatus"); michael@0: this.downloadStatus.value = michael@0: DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size); michael@0: this.selectPanel("downloading"); michael@0: this.aus.addDownloadListener(this); michael@0: }, michael@0: michael@0: removeDownloadListener: function() { michael@0: if (this.aus) { michael@0: this.aus.removeDownloadListener(this); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * See nsIRequestObserver.idl michael@0: */ michael@0: onStartRequest: function(aRequest, aContext) { michael@0: }, michael@0: michael@0: /** michael@0: * See nsIRequestObserver.idl michael@0: */ michael@0: onStopRequest: function(aRequest, aContext, aStatusCode) { michael@0: switch (aStatusCode) { michael@0: case Components.results.NS_ERROR_UNEXPECTED: michael@0: if (this.update.selectedPatch.state == "download-failed" && michael@0: (this.update.isCompleteUpdate || this.update.patchCount != 2)) { michael@0: // Verification error of complete patch, informational text is held in michael@0: // the update object. michael@0: this.removeDownloadListener(); michael@0: this.selectPanel("downloadFailed"); michael@0: break; michael@0: } michael@0: // Verification failed for a partial patch, complete patch is now michael@0: // downloading so return early and do NOT remove the download listener! michael@0: break; michael@0: case Components.results.NS_BINDING_ABORTED: michael@0: // Do not remove UI listener since the user may resume downloading again. michael@0: break; michael@0: case Components.results.NS_OK: michael@0: this.removeDownloadListener(); michael@0: if (this.backgroundUpdateEnabled) { michael@0: this.selectPanel("applying"); michael@0: let update = this.um.activeUpdate; michael@0: let self = this; michael@0: Services.obs.addObserver(function (aSubject, aTopic, aData) { michael@0: // Update the UI when the background updater is finished michael@0: let status = aData; michael@0: if (status == "applied" || status == "applied-service" || michael@0: status == "pending" || status == "pending-service") { michael@0: // If the update is successfully applied, or if the updater has michael@0: // fallen back to non-staged updates, show the "Restart to Update" michael@0: // button. michael@0: self.selectPanel("apply"); michael@0: } else if (status == "failed") { michael@0: // Background update has failed, let's show the UI responsible for michael@0: // prompting the user to update manually. michael@0: self.selectPanel("downloadFailed"); michael@0: } else if (status == "downloading") { michael@0: // We've fallen back to downloading the full update because the michael@0: // partial update failed to get staged in the background. michael@0: // Therefore we need to keep our observer. michael@0: self.setupDownloadingUI(); michael@0: return; michael@0: } michael@0: Services.obs.removeObserver(arguments.callee, "update-staged"); michael@0: }, "update-staged", false); michael@0: } else { michael@0: this.selectPanel("apply"); michael@0: } michael@0: break; michael@0: default: michael@0: this.removeDownloadListener(); michael@0: this.selectPanel("downloadFailed"); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * See nsIProgressEventSink.idl michael@0: */ michael@0: onStatus: function(aRequest, aContext, aStatus, aStatusArg) { michael@0: }, michael@0: michael@0: /** michael@0: * See nsIProgressEventSink.idl michael@0: */ michael@0: onProgress: function(aRequest, aContext, aProgress, aProgressMax) { michael@0: this.downloadStatus.value = michael@0: DownloadUtils.getTransferTotal(aProgress, aProgressMax); michael@0: }, michael@0: michael@0: /** michael@0: * See nsISupports.idl michael@0: */ michael@0: QueryInterface: function(aIID) { michael@0: if (!aIID.equals(Components.interfaces.nsIProgressEventSink) && michael@0: !aIID.equals(Components.interfaces.nsIRequestObserver) && michael@0: !aIID.equals(Components.interfaces.nsISupports)) michael@0: throw Components.results.NS_ERROR_NO_INTERFACE; michael@0: return this; michael@0: } michael@0: }; michael@0: #endif