forked from mirrors/gecko-dev
		
	MozReview-Commit-ID: FuVu8skcqOe --HG-- extra : rebase_source : 8ab34c4e46a7c3075b459bf44786ec184d10d203
		
			
				
	
	
		
			425 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
// Note: this file is included in aboutDialog.xul if MOZ_UPDATER is defined.
 | 
						|
 | 
						|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 | 
						|
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 | 
						|
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
 | 
						|
                                  "resource://gre/modules/UpdateUtils.jsm");
 | 
						|
 | 
						|
const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
 | 
						|
const PREF_APP_UPDATE_ELEVATE_NEVER    = "app.update.elevate.never";
 | 
						|
 | 
						|
var gAppUpdater;
 | 
						|
 | 
						|
function onUnload(aEvent) {
 | 
						|
  if (gAppUpdater.isChecking)
 | 
						|
    gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
 | 
						|
  // Safe to call even when there isn't a download in progress.
 | 
						|
  gAppUpdater.removeDownloadListener();
 | 
						|
  gAppUpdater = null;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
function appUpdater() {
 | 
						|
  XPCOMUtils.defineLazyServiceGetter(this, "aus",
 | 
						|
                                     "@mozilla.org/updates/update-service;1",
 | 
						|
                                     "nsIApplicationUpdateService");
 | 
						|
  XPCOMUtils.defineLazyServiceGetter(this, "checker",
 | 
						|
                                     "@mozilla.org/updates/update-checker;1",
 | 
						|
                                     "nsIUpdateChecker");
 | 
						|
  XPCOMUtils.defineLazyServiceGetter(this, "um",
 | 
						|
                                     "@mozilla.org/updates/update-manager;1",
 | 
						|
                                     "nsIUpdateManager");
 | 
						|
 | 
						|
  this.updateDeck = document.getElementById("updateDeck");
 | 
						|
 | 
						|
  // Hide the update deck when the update window is already open and it's not
 | 
						|
  // already applied, to avoid syncing issues between them. Applied updates
 | 
						|
  // don't have any information to sync between the windows as they both just
 | 
						|
  // show the "Restart to continue"-type button.
 | 
						|
  if (Services.wm.getMostRecentWindow("Update:Wizard") &&
 | 
						|
      !this.isApplied) {
 | 
						|
    this.updateDeck.hidden = true;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  this.bundle = Services.strings.
 | 
						|
                createBundle("chrome://browser/locale/browser.properties");
 | 
						|
 | 
						|
  let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
 | 
						|
  let manualLink = document.getElementById("manualLink");
 | 
						|
  manualLink.value = manualURL;
 | 
						|
  manualLink.href = manualURL;
 | 
						|
  document.getElementById("failedLink").href = manualURL;
 | 
						|
 | 
						|
  if (this.updateDisabledAndLocked) {
 | 
						|
    this.selectPanel("adminDisabled");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (this.isPending || this.isApplied) {
 | 
						|
    this.selectPanel("apply");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (this.aus.isOtherInstanceHandlingUpdates) {
 | 
						|
    this.selectPanel("otherInstanceHandlingUpdates");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (this.isDownloading) {
 | 
						|
    this.startDownload();
 | 
						|
    // selectPanel("downloading") is called from setupDownloadingUI().
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Honor the "Never check for updates" option by not only disabling background
 | 
						|
  // update checks, but also in the About dialog, by presenting a
 | 
						|
  // "Check for updates" button.
 | 
						|
  // If updates are found, the user is then asked if he wants to "Update to <version>".
 | 
						|
  if (!this.updateEnabled ||
 | 
						|
      Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
 | 
						|
    this.selectPanel("checkForUpdates");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // That leaves the options
 | 
						|
  // "Check for updates, but let me choose whether to install them", and
 | 
						|
  // "Automatically install updates".
 | 
						|
  // In both cases, we check for updates without asking.
 | 
						|
  // In the "let me choose" case, we ask before downloading though, in onCheckComplete.
 | 
						|
  this.checkForUpdates();
 | 
						|
}
 | 
						|
 | 
						|
appUpdater.prototype =
 | 
						|
{
 | 
						|
  // true when there is an update check in progress.
 | 
						|
  isChecking: false,
 | 
						|
 | 
						|
  // true when there is an update already staged / ready to be applied.
 | 
						|
  get isPending() {
 | 
						|
    if (this.update) {
 | 
						|
      return this.update.state == "pending" ||
 | 
						|
             this.update.state == "pending-service" ||
 | 
						|
             this.update.state == "pending-elevate";
 | 
						|
    }
 | 
						|
    return this.um.activeUpdate &&
 | 
						|
           (this.um.activeUpdate.state == "pending" ||
 | 
						|
            this.um.activeUpdate.state == "pending-service" ||
 | 
						|
            this.um.activeUpdate.state == "pending-elevate");
 | 
						|
  },
 | 
						|
 | 
						|
  // true when there is an update already installed in the background.
 | 
						|
  get isApplied() {
 | 
						|
    if (this.update)
 | 
						|
      return this.update.state == "applied" ||
 | 
						|
             this.update.state == "applied-service";
 | 
						|
    return this.um.activeUpdate &&
 | 
						|
           (this.um.activeUpdate.state == "applied" ||
 | 
						|
            this.um.activeUpdate.state == "applied-service");
 | 
						|
  },
 | 
						|
 | 
						|
  // true when there is an update download in progress.
 | 
						|
  get isDownloading() {
 | 
						|
    if (this.update)
 | 
						|
      return this.update.state == "downloading";
 | 
						|
    return this.um.activeUpdate &&
 | 
						|
           this.um.activeUpdate.state == "downloading";
 | 
						|
  },
 | 
						|
 | 
						|
  // true when updating is disabled by an administrator.
 | 
						|
  get updateDisabledAndLocked() {
 | 
						|
    return !this.updateEnabled &&
 | 
						|
           Services.prefs.prefIsLocked("app.update.enabled");
 | 
						|
  },
 | 
						|
 | 
						|
  // true when updating is enabled.
 | 
						|
  get updateEnabled() {
 | 
						|
    try {
 | 
						|
      return Services.prefs.getBoolPref("app.update.enabled");
 | 
						|
    } catch (e) { }
 | 
						|
    return true; // Firefox default is true
 | 
						|
  },
 | 
						|
 | 
						|
  // true when updating in background is enabled.
 | 
						|
  get backgroundUpdateEnabled() {
 | 
						|
    return this.updateEnabled &&
 | 
						|
           gAppUpdater.aus.canStageUpdates;
 | 
						|
  },
 | 
						|
 | 
						|
  // true when updating is automatic.
 | 
						|
  get updateAuto() {
 | 
						|
    try {
 | 
						|
      return Services.prefs.getBoolPref("app.update.auto");
 | 
						|
    } catch (e) { }
 | 
						|
    return true; // Firefox default is true
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the panel of the updateDeck.
 | 
						|
   *
 | 
						|
   * @param  aChildID
 | 
						|
   *         The id of the deck's child to select, e.g. "apply".
 | 
						|
   */
 | 
						|
  selectPanel(aChildID) {
 | 
						|
    let panel = document.getElementById(aChildID);
 | 
						|
 | 
						|
    let button = panel.querySelector("button");
 | 
						|
    if (button) {
 | 
						|
      if (aChildID == "downloadAndInstall") {
 | 
						|
        let updateVersion = gAppUpdater.update.displayVersion;
 | 
						|
        button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
 | 
						|
        button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
 | 
						|
      }
 | 
						|
      this.updateDeck.selectedPanel = panel;
 | 
						|
      if (!document.commandDispatcher.focusedElement || // don't steal the focus
 | 
						|
          document.commandDispatcher.focusedElement.localName == "button") // except from the other buttons
 | 
						|
        button.focus();
 | 
						|
 | 
						|
    } else {
 | 
						|
      this.updateDeck.selectedPanel = panel;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Check for updates
 | 
						|
   */
 | 
						|
  checkForUpdates() {
 | 
						|
    // Clear prefs that could prevent a user from discovering available updates.
 | 
						|
    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
 | 
						|
      Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
 | 
						|
    }
 | 
						|
    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
 | 
						|
      Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
 | 
						|
    }
 | 
						|
    this.selectPanel("checkingForUpdates");
 | 
						|
    this.isChecking = true;
 | 
						|
    this.checker.checkForUpdates(this.updateCheckListener, true);
 | 
						|
    // after checking, onCheckComplete() is called
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handles oncommand for the "Restart to Update" button
 | 
						|
   * which is presented after the download has been downloaded.
 | 
						|
   */
 | 
						|
  buttonRestartAfterDownload() {
 | 
						|
    if (!this.isPending && !this.isApplied) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    gAppUpdater.selectPanel("restarting");
 | 
						|
 | 
						|
    // Notify all windows that an application quit has been requested.
 | 
						|
    let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
 | 
						|
                     createInstance(Components.interfaces.nsISupportsPRBool);
 | 
						|
    Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
 | 
						|
 | 
						|
    // Something aborted the quit process.
 | 
						|
    if (cancelQuit.data) {
 | 
						|
      gAppUpdater.selectPanel("apply");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
 | 
						|
                     getService(Components.interfaces.nsIAppStartup);
 | 
						|
 | 
						|
    // If already in safe mode restart in safe mode (bug 327119)
 | 
						|
    if (Services.appinfo.inSafeMode) {
 | 
						|
      appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
 | 
						|
                    Components.interfaces.nsIAppStartup.eRestart);
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Implements nsIUpdateCheckListener. The methods implemented by
 | 
						|
   * nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload
 | 
						|
   * to make it clear which are used by each interface.
 | 
						|
   */
 | 
						|
  updateCheckListener: {
 | 
						|
    /**
 | 
						|
     * See nsIUpdateService.idl
 | 
						|
     */
 | 
						|
    onCheckComplete(aRequest, aUpdates, aUpdateCount) {
 | 
						|
      gAppUpdater.isChecking = false;
 | 
						|
      gAppUpdater.update = gAppUpdater.aus.
 | 
						|
                           selectUpdate(aUpdates, aUpdates.length);
 | 
						|
      if (!gAppUpdater.update) {
 | 
						|
        gAppUpdater.selectPanel("noUpdatesFound");
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      if (gAppUpdater.update.unsupported) {
 | 
						|
        if (gAppUpdater.update.detailsURL) {
 | 
						|
          let unsupportedLink = document.getElementById("unsupportedLink");
 | 
						|
          unsupportedLink.href = gAppUpdater.update.detailsURL;
 | 
						|
        }
 | 
						|
        gAppUpdater.selectPanel("unsupportedSystem");
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      if (!gAppUpdater.aus.canApplyUpdates) {
 | 
						|
        gAppUpdater.selectPanel("manualUpdate");
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      if (gAppUpdater.updateAuto) // automatically download and install
 | 
						|
        gAppUpdater.startDownload();
 | 
						|
      else // ask
 | 
						|
        gAppUpdater.selectPanel("downloadAndInstall");
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * See nsIUpdateService.idl
 | 
						|
     */
 | 
						|
    onError(aRequest, aUpdate) {
 | 
						|
      // Errors in the update check are treated as no updates found. If the
 | 
						|
      // update check fails repeatedly without a success the user will be
 | 
						|
      // notified with the normal app update user interface so this is safe.
 | 
						|
      gAppUpdater.isChecking = false;
 | 
						|
      gAppUpdater.selectPanel("noUpdatesFound");
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * See nsISupports.idl
 | 
						|
     */
 | 
						|
    QueryInterface(aIID) {
 | 
						|
      if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) &&
 | 
						|
          !aIID.equals(Components.interfaces.nsISupports))
 | 
						|
        throw Components.results.NS_ERROR_NO_INTERFACE;
 | 
						|
      return this;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Starts the download of an update mar.
 | 
						|
   */
 | 
						|
  startDownload() {
 | 
						|
    if (!this.update)
 | 
						|
      this.update = this.um.activeUpdate;
 | 
						|
    this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
 | 
						|
    this.update.setProperty("foregroundDownload", "true");
 | 
						|
 | 
						|
    this.aus.pauseDownload();
 | 
						|
    let state = this.aus.downloadUpdate(this.update, false);
 | 
						|
    if (state == "failed") {
 | 
						|
      this.selectPanel("downloadFailed");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this.setupDownloadingUI();
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Switches to the UI responsible for tracking the download.
 | 
						|
   */
 | 
						|
  setupDownloadingUI() {
 | 
						|
    this.downloadStatus = document.getElementById("downloadStatus");
 | 
						|
    this.downloadStatus.value =
 | 
						|
      DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
 | 
						|
    this.selectPanel("downloading");
 | 
						|
    this.aus.addDownloadListener(this);
 | 
						|
  },
 | 
						|
 | 
						|
  removeDownloadListener() {
 | 
						|
    if (this.aus) {
 | 
						|
      this.aus.removeDownloadListener(this);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * See nsIRequestObserver.idl
 | 
						|
   */
 | 
						|
  onStartRequest(aRequest, aContext) {
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * See nsIRequestObserver.idl
 | 
						|
   */
 | 
						|
  onStopRequest(aRequest, aContext, aStatusCode) {
 | 
						|
    switch (aStatusCode) {
 | 
						|
    case Components.results.NS_ERROR_UNEXPECTED:
 | 
						|
      if (this.update.selectedPatch.state == "download-failed" &&
 | 
						|
          (this.update.isCompleteUpdate || this.update.patchCount != 2)) {
 | 
						|
        // Verification error of complete patch, informational text is held in
 | 
						|
        // the update object.
 | 
						|
        this.removeDownloadListener();
 | 
						|
        this.selectPanel("downloadFailed");
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      // Verification failed for a partial patch, complete patch is now
 | 
						|
      // downloading so return early and do NOT remove the download listener!
 | 
						|
      break;
 | 
						|
    case Components.results.NS_BINDING_ABORTED:
 | 
						|
      // Do not remove UI listener since the user may resume downloading again.
 | 
						|
      break;
 | 
						|
    case Components.results.NS_OK:
 | 
						|
      this.removeDownloadListener();
 | 
						|
      if (this.backgroundUpdateEnabled) {
 | 
						|
        this.selectPanel("applying");
 | 
						|
        let self = this;
 | 
						|
        Services.obs.addObserver(function(aSubject, aTopic, aData) {
 | 
						|
          // Update the UI when the background updater is finished
 | 
						|
          let status = aData;
 | 
						|
          if (status == "applied" || status == "applied-service" ||
 | 
						|
              status == "pending" || status == "pending-service" ||
 | 
						|
              status == "pending-elevate") {
 | 
						|
            // If the update is successfully applied, or if the updater has
 | 
						|
            // fallen back to non-staged updates, show the "Restart to Update"
 | 
						|
            // button.
 | 
						|
            self.selectPanel("apply");
 | 
						|
          } else if (status == "failed") {
 | 
						|
            // Background update has failed, let's show the UI responsible for
 | 
						|
            // prompting the user to update manually.
 | 
						|
            self.selectPanel("downloadFailed");
 | 
						|
          } else if (status == "downloading") {
 | 
						|
            // We've fallen back to downloading the full update because the
 | 
						|
            // partial update failed to get staged in the background.
 | 
						|
            // Therefore we need to keep our observer.
 | 
						|
            self.setupDownloadingUI();
 | 
						|
            return;
 | 
						|
          }
 | 
						|
          Services.obs.removeObserver(arguments.callee, "update-staged");
 | 
						|
        }, "update-staged", false);
 | 
						|
      } else {
 | 
						|
        this.selectPanel("apply");
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      this.removeDownloadListener();
 | 
						|
      this.selectPanel("downloadFailed");
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * See nsIProgressEventSink.idl
 | 
						|
   */
 | 
						|
  onStatus(aRequest, aContext, aStatus, aStatusArg) {
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * See nsIProgressEventSink.idl
 | 
						|
   */
 | 
						|
  onProgress(aRequest, aContext, aProgress, aProgressMax) {
 | 
						|
    this.downloadStatus.value =
 | 
						|
      DownloadUtils.getTransferTotal(aProgress, aProgressMax);
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * See nsISupports.idl
 | 
						|
   */
 | 
						|
  QueryInterface(aIID) {
 | 
						|
    if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
 | 
						|
        !aIID.equals(Components.interfaces.nsIRequestObserver) &&
 | 
						|
        !aIID.equals(Components.interfaces.nsISupports))
 | 
						|
      throw Components.results.NS_ERROR_NO_INTERFACE;
 | 
						|
    return this;
 | 
						|
  }
 | 
						|
};
 |