toolkit/mozapps/update/content/updates.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/mozapps/update/content/updates.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1941 @@
     1.4 +#filter substitution
     1.5 +
     1.6 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     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
     1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    1.10 +
    1.11 +Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
    1.12 +Components.utils.import("resource://gre/modules/AddonManager.jsm");
    1.13 +Components.utils.import("resource://gre/modules/Services.jsm");
    1.14 +
    1.15 +// Firefox's macBrowserOverlay.xul includes scripts that define Cc, Ci, and Cr
    1.16 +// so we have to use different names.
    1.17 +const CoC = Components.classes;
    1.18 +const CoI = Components.interfaces;
    1.19 +const CoR = Components.results;
    1.20 +
    1.21 +const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    1.22 +
    1.23 +const PREF_APP_UPDATE_BACKGROUNDERRORS    = "app.update.backgroundErrors";
    1.24 +const PREF_APP_UPDATE_BILLBOARD_TEST_URL  = "app.update.billboard.test_url";
    1.25 +const PREF_APP_UPDATE_CERT_ERRORS         = "app.update.cert.errors";
    1.26 +const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
    1.27 +const PREF_APP_UPDATE_LOG                 = "app.update.log";
    1.28 +const PREF_APP_UPDATE_MANUAL_URL          = "app.update.url.manual";
    1.29 +const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
    1.30 +const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
    1.31 +const PREF_APP_UPDATE_TEST_LOOP           = "app.update.test.loop";
    1.32 +const PREF_PLUGINS_UPDATEURL              = "plugins.update.url";
    1.33 +
    1.34 +const PREF_EM_HOTFIX_ID                   = "extensions.hotfix.id";
    1.35 +
    1.36 +const UPDATE_TEST_LOOP_INTERVAL     = 2000;
    1.37 +
    1.38 +const URI_UPDATES_PROPERTIES  = "chrome://mozapps/locale/update/updates.properties";
    1.39 +
    1.40 +const STATE_DOWNLOADING       = "downloading";
    1.41 +const STATE_PENDING           = "pending";
    1.42 +const STATE_PENDING_SVC       = "pending-service";
    1.43 +const STATE_APPLYING          = "applying";
    1.44 +const STATE_APPLIED           = "applied";
    1.45 +const STATE_APPLIED_SVC       = "applied-service";
    1.46 +const STATE_SUCCEEDED         = "succeeded";
    1.47 +const STATE_DOWNLOAD_FAILED   = "download-failed";
    1.48 +const STATE_FAILED            = "failed";
    1.49 +
    1.50 +const SRCEVT_FOREGROUND       = 1;
    1.51 +const SRCEVT_BACKGROUND       = 2;
    1.52 +
    1.53 +const CERT_ATTR_CHECK_FAILED_NO_UPDATE  = 100;
    1.54 +const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
    1.55 +const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
    1.56 +
    1.57 +#ifdef TOR_BROWSER_VERSION
    1.58 +# Add double-quotes back on (stripped by JarMaker.py).
    1.59 +#expand const TOR_BROWSER_VERSION = "__TOR_BROWSER_VERSION__";
    1.60 +#endif
    1.61 +
    1.62 +var gLogEnabled = false;
    1.63 +var gUpdatesFoundPageId;
    1.64 +
    1.65 +// Notes:
    1.66 +// 1. use the wizard's goTo method whenever possible to change the wizard
    1.67 +//    page since it is simpler than most other methods and behaves nicely with
    1.68 +//    mochitests.
    1.69 +// 2. using a page's onPageShow method to then change to a different page will
    1.70 +//    of course call that page's onPageShow method which can make mochitests
    1.71 +//    overly complicated and fragile so avoid doing this if at all possible.
    1.72 +//    This is why a page's next attribute is set prior to the page being shown
    1.73 +//    whenever possible.
    1.74 +
    1.75 +/**
    1.76 + * Logs a string to the error console.
    1.77 + * @param   string
    1.78 + *          The string to write to the error console..
    1.79 + */
    1.80 +function LOG(module, string) {
    1.81 +  if (gLogEnabled) {
    1.82 +    dump("*** AUS:UI " + module + ":" + string + "\n");
    1.83 +    Services.console.logStringMessage("AUS:UI " + module + ":" + string);
    1.84 +  }
    1.85 +}
    1.86 +
    1.87 +/**
    1.88 + * Opens a URL using the event target's url attribute for the URL. This is a
    1.89 + * workaround for Bug 263433 which prevents respecting tab browser preferences
    1.90 + * for where to open a URL.
    1.91 + */
    1.92 +function openUpdateURL(event) {
    1.93 +  if (event.button == 0)
    1.94 +    openURL(event.target.getAttribute("url"));
    1.95 +}
    1.96 +
    1.97 +/**
    1.98 + * Gets a preference value, handling the case where there is no default.
    1.99 + * @param   func
   1.100 + *          The name of the preference function to call, on nsIPrefBranch
   1.101 + * @param   preference
   1.102 + *          The name of the preference
   1.103 + * @param   defaultValue
   1.104 + *          The default value to return in the event the preference has
   1.105 + *          no setting
   1.106 + * @returns The value of the preference, or undefined if there was no
   1.107 + *          user or default value.
   1.108 + */
   1.109 +function getPref(func, preference, defaultValue) {
   1.110 +  try {
   1.111 +    return Services.prefs[func](preference);
   1.112 +  }
   1.113 +  catch (e) {
   1.114 +    LOG("General", "getPref - failed to get preference: " + preference);
   1.115 +  }
   1.116 +  return defaultValue;
   1.117 +}
   1.118 +
   1.119 +/**
   1.120 + * A set of shared data and control functions for the wizard as a whole.
   1.121 + */
   1.122 +var gUpdates = {
   1.123 +  /**
   1.124 +   * The nsIUpdate object being used by this window (either for downloading,
   1.125 +   * notification or both).
   1.126 +   */
   1.127 +  update: null,
   1.128 +
   1.129 +  /**
   1.130 +   * List of incompatible add-ons
   1.131 +   */
   1.132 +  addons: [],
   1.133 +
   1.134 +  /**
   1.135 +   * The updates.properties <stringbundle> element.
   1.136 +   */
   1.137 +  strings: null,
   1.138 +
   1.139 +  /**
   1.140 +   * The Application brandShortName (e.g. "Firefox")
   1.141 +   */
   1.142 +  brandName: null,
   1.143 +
   1.144 +  /**
   1.145 +   * The <wizard> element
   1.146 +   */
   1.147 +  wiz: null,
   1.148 +
   1.149 +  /**
   1.150 +   * Whether to run the unload handler. This will be set to false when the user
   1.151 +   * exits the wizard via onWizardCancel or onWizardFinish.
   1.152 +   */
   1.153 +  _runUnload: true,
   1.154 +
   1.155 +  /**
   1.156 +   * Submit the last page code when the wizard exited. The pageid is used to map
   1.157 +   * to an integer instead of using the pageindex since pages can be added and
   1.158 +   * removed which would change the page's pageindex.
   1.159 +   * @param   pageID
   1.160 +   */
   1.161 +  _sendLastPageCodePing: function(pageID) {
   1.162 +    var pageMap = { invalid: 0,
   1.163 +                    dummy: 1,
   1.164 +                    checking: 2,
   1.165 +                    pluginupdatesfound: 3,
   1.166 +                    noupdatesfound: 4,
   1.167 +                    manualUpdate: 5,
   1.168 +                    unsupported: 6,
   1.169 +                    incompatibleCheck: 7,
   1.170 +                    updatesfoundbasic: 8,
   1.171 +                    updatesfoundbillboard: 9,
   1.172 +                    license: 10,
   1.173 +                    incompatibleList: 11,
   1.174 +                    downloading: 12,
   1.175 +                    errors: 13,
   1.176 +                    errorextra: 14,
   1.177 +                    errorpatching: 15,
   1.178 +                    finished: 16,
   1.179 +                    finishedBackground: 17,
   1.180 +                    installed: 18 };
   1.181 +    try {
   1.182 +      Services.telemetry.getHistogramById("UPDATER_WIZ_LAST_PAGE_CODE").
   1.183 +        add(pageMap[pageID] || pageMap.invalid);
   1.184 +    }
   1.185 +    catch (e) {
   1.186 +      Components.utils.reportError(e);
   1.187 +    }
   1.188 +  },
   1.189 +
   1.190 +  /**
   1.191 +   * Helper function for setButtons
   1.192 +   * Resets button to original label & accesskey if string is null.
   1.193 +   */
   1.194 +  _setButton: function(button, string) {
   1.195 +    if (string) {
   1.196 +      var label = this.getAUSString(string);
   1.197 +      if (label.indexOf("%S") != -1)
   1.198 +        label = label.replace(/%S/, this.brandName);
   1.199 +      button.label = label;
   1.200 +      button.setAttribute("accesskey",
   1.201 +                          this.getAUSString(string + ".accesskey"));
   1.202 +    } else {
   1.203 +      button.label = button.defaultLabel;
   1.204 +      button.setAttribute("accesskey", button.defaultAccesskey);
   1.205 +    }
   1.206 +  },
   1.207 +
   1.208 +  /**
   1.209 +   * Sets the attributes needed for this Wizard's control buttons (labels,
   1.210 +   * disabled, hidden, etc.)
   1.211 +   * @param   extra1ButtonString
   1.212 +   *          The property in the stringbundle containing the label to put on
   1.213 +   *          the first extra button, or null to hide the first extra button.
   1.214 +   * @param   extra2ButtonString
   1.215 +   *          The property in the stringbundle containing the label to put on
   1.216 +   *          the second extra button, or null to hide the second extra button.
   1.217 +   * @param   nextFinishButtonString
   1.218 +   *          The property in the stringbundle containing the label to put on
   1.219 +   *          the Next / Finish button, or null to hide the button. The Next and
   1.220 +   *          Finish buttons are never displayed at the same time in a wizard
   1.221 +   *          with the the Finish button only being displayed when there are no
   1.222 +   *          additional pages to display in the wizard.
   1.223 +   * @param   canAdvance
   1.224 +   *          true if the wizard can be advanced (e.g. the next / finish button
   1.225 +   *          should be enabled), false otherwise.
   1.226 +   * @param   showCancel
   1.227 +   *          true if the wizard's cancel button should be shown, false
   1.228 +   *          otherwise. If not specified this will default to false.
   1.229 +   *
   1.230 +   * Note:
   1.231 +   * Per Bug 324121 the wizard should not look like a wizard and to accomplish
   1.232 +   * this the back button is never displayed and the cancel button is only
   1.233 +   * displayed for the checking and the incompatibleCheck pages. This causes the
   1.234 +   * wizard buttons to be arranged as follows on Windows with the next and
   1.235 +   * finish buttons never being displayed at the same time.
   1.236 +   * +--------------------------------------------------------------+
   1.237 +   * | [ extra1 ] [ extra2 ]                     [ next or finish ] |
   1.238 +   * +--------------------------------------------------------------+
   1.239 +   */
   1.240 +  setButtons: function(extra1ButtonString, extra2ButtonString,
   1.241 +                       nextFinishButtonString, canAdvance, showCancel) {
   1.242 +    this.wiz.canAdvance = canAdvance;
   1.243 +
   1.244 +    var bnf = this.wiz.getButton(this.wiz.onLastPage ? "finish" : "next");
   1.245 +    var be1 = this.wiz.getButton("extra1");
   1.246 +    var be2 = this.wiz.getButton("extra2");
   1.247 +    var bc = this.wiz.getButton("cancel");
   1.248 +
   1.249 +    // Set the labels for the next / finish, extra1, and extra2 buttons
   1.250 +    this._setButton(bnf, nextFinishButtonString);
   1.251 +    this._setButton(be1, extra1ButtonString);
   1.252 +    this._setButton(be2, extra2ButtonString);
   1.253 +
   1.254 +    bnf.hidden = bnf.disabled = !nextFinishButtonString;
   1.255 +    be1.hidden = be1.disabled = !extra1ButtonString;
   1.256 +    be2.hidden = be2.disabled = !extra2ButtonString;
   1.257 +    bc.hidden = bc.disabled = !showCancel;
   1.258 +
   1.259 +    // Hide and disable the back button each time setButtons is called
   1.260 +    // (see bug 464765).
   1.261 +    var btn = this.wiz.getButton("back");
   1.262 +    btn.hidden = btn.disabled = true;
   1.263 +
   1.264 +    // Hide and disable the finish button if not on the last page or the next
   1.265 +    // button if on the last page each time setButtons is called.
   1.266 +    btn = this.wiz.getButton(this.wiz.onLastPage ? "next" : "finish");
   1.267 +    btn.hidden = btn.disabled = true;
   1.268 +  },
   1.269 +
   1.270 +  getAUSString: function(key, strings) {
   1.271 +    if (strings)
   1.272 +      return this.strings.getFormattedString(key, strings);
   1.273 +    return this.strings.getString(key);
   1.274 +  },
   1.275 +
   1.276 +  never: function () {
   1.277 +    // If the user clicks "No Thanks", we should not prompt them to update to
   1.278 +    // this version again unless they manually select "Check for Updates..."
   1.279 +    // which will clear all of the "never" prefs.
   1.280 +    var neverPrefName = PREF_APP_UPDATE_NEVER_BRANCH + this.update.appVersion;
   1.281 +    Services.prefs.setBoolPref(neverPrefName, true);
   1.282 +  },
   1.283 +
   1.284 +  /**
   1.285 +   * A hash of |pageid| attribute to page object. Can be used to dispatch
   1.286 +   * function calls to the appropriate page.
   1.287 +   */
   1.288 +  _pages: { },
   1.289 +
   1.290 +  /**
   1.291 +   * Called when the user presses the "Finish" button on the wizard, dispatches
   1.292 +   * the function call to the selected page.
   1.293 +   */
   1.294 +  onWizardFinish: function() {
   1.295 +    this._runUnload = false;
   1.296 +    var pageid = document.documentElement.currentPage.pageid;
   1.297 +    if ("onWizardFinish" in this._pages[pageid])
   1.298 +      this._pages[pageid].onWizardFinish();
   1.299 +    this._sendLastPageCodePing(pageid);
   1.300 +  },
   1.301 +
   1.302 +  /**
   1.303 +   * Called when the user presses the "Cancel" button on the wizard, dispatches
   1.304 +   * the function call to the selected page.
   1.305 +   */
   1.306 +  onWizardCancel: function() {
   1.307 +    this._runUnload = false;
   1.308 +    var pageid = document.documentElement.currentPage.pageid;
   1.309 +    if ("onWizardCancel" in this._pages[pageid])
   1.310 +      this._pages[pageid].onWizardCancel();
   1.311 +    this._sendLastPageCodePing(pageid);
   1.312 +  },
   1.313 +
   1.314 +  /**
   1.315 +   * Called when the user presses the "Next" button on the wizard, dispatches
   1.316 +   * the function call to the selected page.
   1.317 +   */
   1.318 +  onWizardNext: function() {
   1.319 +    var cp = document.documentElement.currentPage;
   1.320 +    if (!cp)
   1.321 +      return;
   1.322 +    var pageid = cp.pageid;
   1.323 +    if ("onWizardNext" in this._pages[pageid])
   1.324 +      this._pages[pageid].onWizardNext();
   1.325 +  },
   1.326 +
   1.327 +  /**
   1.328 +   * The checking process that spawned this update UI. There are two types:
   1.329 +   * SRCEVT_FOREGROUND:
   1.330 +   *   Some user-generated event caused this UI to appear, e.g. the Help
   1.331 +   *   menu item or the button in preferences. When in this mode, the UI
   1.332 +   *   should remain active for the duration of the download.
   1.333 +   * SRCEVT_BACKGROUND:
   1.334 +   *   A background update check caused this UI to appear, probably because
   1.335 +   *   incompatibilities in Extensions or other addons were discovered and
   1.336 +   *   the user's consent to continue was required. When in this mode, the
   1.337 +   *   UI will disappear after the user's consent is obtained.
   1.338 +   */
   1.339 +  sourceEvent: SRCEVT_FOREGROUND,
   1.340 +
   1.341 +  /**
   1.342 +   * Helper function for onLoad
   1.343 +   * Saves default button label & accesskey for use by _setButton
   1.344 +   */
   1.345 +  _cacheButtonStrings: function (buttonName) {
   1.346 +    var button = this.wiz.getButton(buttonName);
   1.347 +    button.defaultLabel = button.label;
   1.348 +    button.defaultAccesskey = button.getAttribute("accesskey");
   1.349 +  },
   1.350 +
   1.351 +  /**
   1.352 +   * Called when the wizard UI is loaded.
   1.353 +   */
   1.354 +  onLoad: function() {
   1.355 +    this.wiz = document.documentElement;
   1.356 +
   1.357 +    gLogEnabled = getPref("getBoolPref", PREF_APP_UPDATE_LOG, false)
   1.358 +
   1.359 +    this.strings = document.getElementById("updateStrings");
   1.360 +    var brandStrings = document.getElementById("brandStrings");
   1.361 +    this.brandName = brandStrings.getString("brandShortName");
   1.362 +
   1.363 +    var pages = this.wiz.childNodes;
   1.364 +    for (var i = 0; i < pages.length; ++i) {
   1.365 +      var page = pages[i];
   1.366 +      if (page.localName == "wizardpage")
   1.367 +        this._pages[page.pageid] = eval(page.getAttribute("object"));
   1.368 +    }
   1.369 +
   1.370 +    // Cache the standard button labels in case we need to restore them
   1.371 +    this._cacheButtonStrings("next");
   1.372 +    this._cacheButtonStrings("finish");
   1.373 +    this._cacheButtonStrings("extra1");
   1.374 +    this._cacheButtonStrings("extra2");
   1.375 +
   1.376 +    // Advance to the Start page.
   1.377 +    this.getStartPageID(function(startPageID) {
   1.378 +      LOG("gUpdates", "onLoad - setting current page to startpage " + startPageID);
   1.379 +      gUpdates.wiz.currentPage = document.getElementById(startPageID);
   1.380 +    });
   1.381 +  },
   1.382 +
   1.383 +  /**
   1.384 +   * Called when the wizard UI is unloaded.
   1.385 +   */
   1.386 +  onUnload: function() {
   1.387 +    if (this._runUnload) {
   1.388 +      var cp = this.wiz.currentPage;
   1.389 +      if (cp.pageid != "finished" && cp.pageid != "finishedBackground")
   1.390 +        this.onWizardCancel();
   1.391 +    }
   1.392 +  },
   1.393 +
   1.394 +  /**
   1.395 +   * Gets the ID of the <wizardpage> object that should be displayed first. This
   1.396 +   * is an asynchronous method that passes the resulting object to a callback
   1.397 +   * function.
   1.398 +   *
   1.399 +   * This is determined by how we were called by the update prompt:
   1.400 +   *
   1.401 +   * Prompt Method:       Arg0:         Update State: Src Event:  Failed:   Result:
   1.402 +   * showUpdateAvailable  nsIUpdate obj --            background  --        see Note below
   1.403 +   * showUpdateDownloaded nsIUpdate obj pending       background  --        finishedBackground
   1.404 +   * showUpdateInstalled  "installed"   --            --          --        installed
   1.405 +   * showUpdateError      nsIUpdate obj failed        either      partial   errorpatching
   1.406 +   * showUpdateError      nsIUpdate obj failed        either      complete  errors
   1.407 +   * checkForUpdates      null          --            foreground  --        checking
   1.408 +   * checkForUpdates      null          downloading   foreground  --        downloading
   1.409 +   *
   1.410 +   * Note: the page returned (e.g. Result) for showUpdateAvaulable is as follows:
   1.411 +   * New enabled incompatible add-ons   : incompatibleCheck page
   1.412 +   * No new enabled incompatible add-ons: either updatesfoundbasic or
   1.413 +   *                                      updatesfoundbillboard as determined by
   1.414 +   *                                      updatesFoundPageId
   1.415 +   * @param   aCallback
   1.416 +   *          A callback to pass the <wizardpage> object to be displayed first to.
   1.417 +   */
   1.418 +  getStartPageID: function(aCallback) {
   1.419 +    if ("arguments" in window && window.arguments[0]) {
   1.420 +      var arg0 = window.arguments[0];
   1.421 +      if (arg0 instanceof CoI.nsIUpdate) {
   1.422 +        // If the first argument is a nsIUpdate object, we are notifying the
   1.423 +        // user that the background checking found an update that requires
   1.424 +        // their permission to install, and it's ready for download.
   1.425 +        this.setUpdate(arg0);
   1.426 +        if (this.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
   1.427 +            this.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE ||
   1.428 +            this.update.errorCode == BACKGROUNDCHECK_MULTIPLE_FAILURES) {
   1.429 +          aCallback("errorextra");
   1.430 +          return;
   1.431 +        }
   1.432 +
   1.433 +        if (this.update.unsupported) {
   1.434 +          aCallback("unsupported");
   1.435 +          return;
   1.436 +        }
   1.437 +
   1.438 +        var p = this.update.selectedPatch;
   1.439 +        if (p) {
   1.440 +          var state = p.state;
   1.441 +          var patchFailed;
   1.442 +          try {
   1.443 +            patchFailed = this.update.getProperty("patchingFailed");
   1.444 +          }
   1.445 +          catch (e) {
   1.446 +          }
   1.447 +          if (patchFailed) {
   1.448 +            if (patchFailed == "partial" && this.update.patchCount == 2) {
   1.449 +              // If the system failed to apply the partial patch, show the
   1.450 +              // screen which best describes this condition, which is triggered
   1.451 +              // by the |STATE_FAILED| state.
   1.452 +              state = STATE_FAILED;
   1.453 +            }
   1.454 +            else {
   1.455 +              // Otherwise, if the complete patch failed, which is far less
   1.456 +              // likely, show the error text held by the update object in the
   1.457 +              // generic errors page, triggered by the |STATE_DOWNLOAD_FAILED|
   1.458 +              // state.
   1.459 +              state = STATE_DOWNLOAD_FAILED;
   1.460 +            }
   1.461 +          }
   1.462 +
   1.463 +          // Now select the best page to start with, given the current state of
   1.464 +          // the Update.
   1.465 +          switch (state) {
   1.466 +          case STATE_PENDING:
   1.467 +          case STATE_PENDING_SVC:
   1.468 +          case STATE_APPLIED:
   1.469 +          case STATE_APPLIED_SVC:
   1.470 +            this.sourceEvent = SRCEVT_BACKGROUND;
   1.471 +            aCallback("finishedBackground");
   1.472 +            return;
   1.473 +          case STATE_DOWNLOADING:
   1.474 +            aCallback("downloading");
   1.475 +            return;
   1.476 +          case STATE_FAILED:
   1.477 +            window.getAttention();
   1.478 +            aCallback("errorpatching");
   1.479 +            return;
   1.480 +          case STATE_DOWNLOAD_FAILED:
   1.481 +          case STATE_APPLYING:
   1.482 +            aCallback("errors");
   1.483 +            return;
   1.484 +          }
   1.485 +        }
   1.486 +        if (this.update.licenseURL)
   1.487 +          this.wiz.getPageById(this.updatesFoundPageId).setAttribute("next", "license");
   1.488 +
   1.489 +        var self = this;
   1.490 +        this.getShouldCheckAddonCompatibility(function(shouldCheck) {
   1.491 +          if (shouldCheck) {
   1.492 +            var incompatCheckPage = document.getElementById("incompatibleCheck");
   1.493 +            incompatCheckPage.setAttribute("next", self.updatesFoundPageId);
   1.494 +            aCallback(incompatCheckPage.id);
   1.495 +          }
   1.496 +          else {
   1.497 +            aCallback(self.updatesFoundPageId);
   1.498 +          }
   1.499 +        });
   1.500 +        return;
   1.501 +      }
   1.502 +      else if (arg0 == "installed") {
   1.503 +        aCallback("installed");
   1.504 +        return;
   1.505 +      }
   1.506 +    }
   1.507 +    else {
   1.508 +      var um = CoC["@mozilla.org/updates/update-manager;1"].
   1.509 +               getService(CoI.nsIUpdateManager);
   1.510 +      if (um.activeUpdate) {
   1.511 +        this.setUpdate(um.activeUpdate);
   1.512 +        aCallback("downloading");
   1.513 +        return;
   1.514 +      }
   1.515 +    }
   1.516 +
   1.517 +    // Provide the ability to test the billboard html
   1.518 +    var billboardTestURL = getPref("getCharPref", PREF_APP_UPDATE_BILLBOARD_TEST_URL, null);
   1.519 +    if (billboardTestURL) {
   1.520 +      var updatesFoundBillboardPage = document.getElementById("updatesfoundbillboard");
   1.521 +      updatesFoundBillboardPage.setAttribute("next", "dummy");
   1.522 +      gUpdatesFoundBillboardPage.onExtra1 = function(){ gUpdates.wiz.cancel(); };
   1.523 +      gUpdatesFoundBillboardPage.onExtra2 = function(){ gUpdates.wiz.cancel(); };
   1.524 +      this.onWizardNext = function() { gUpdates.wiz.cancel(); };
   1.525 +      this.update = { billboardURL        : billboardTestURL,
   1.526 +                      brandName           : this.brandName,
   1.527 +                      displayVersion      : "Billboard Test 1.0",
   1.528 +                      showNeverForVersion : true,
   1.529 +                      type                : "major" };
   1.530 +      aCallback(updatesFoundBillboardPage.id);
   1.531 +    }
   1.532 +    else {
   1.533 +      aCallback("checking");
   1.534 +    }
   1.535 +  },
   1.536 +
   1.537 +  getShouldCheckAddonCompatibility: function(aCallback) {
   1.538 +    // this early return should never happen
   1.539 +    if (!this.update) {
   1.540 +      aCallback(false);
   1.541 +      return;
   1.542 +    }
   1.543 +
   1.544 +#ifdef TOR_BROWSER_UPDATE
   1.545 +    var appVersion = TOR_BROWSER_VERSION;
   1.546 +#else
   1.547 +    var appVersion = Services.appinfo.version;
   1.548 +#endif
   1.549 +    if (!this.update.appVersion ||
   1.550 +        Services.vc.compare(this.update.appVersion, appVersion) == 0) {
   1.551 +      aCallback(false);
   1.552 +      return;
   1.553 +    }
   1.554 +
   1.555 +    try {
   1.556 +      var hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
   1.557 +    }
   1.558 +    catch (e) { }
   1.559 +
   1.560 +    var self = this;
   1.561 +    AddonManager.getAllAddons(function(addons) {
   1.562 +#ifdef TOR_BROWSER_UPDATE
   1.563 +      let compatVersion = self.update.platformVersion;
   1.564 +#else
   1.565 +      let compatVersion = self.update.appVersion;
   1.566 +#endif
   1.567 +      self.addons = [];
   1.568 +      addons.forEach(function(addon) {
   1.569 +        // Protect against code that overrides the add-ons manager and doesn't
   1.570 +        // implement the isCompatibleWith or the findUpdates method.
   1.571 +        if (!("isCompatibleWith" in addon) || !("findUpdates" in addon)) {
   1.572 +          let errMsg = "Add-on doesn't implement either the isCompatibleWith " +
   1.573 +                       "or the findUpdates method!";
   1.574 +          if (addon.id)
   1.575 +            errMsg += " Add-on ID: " + addon.id;
   1.576 +          Components.utils.reportError(errMsg);
   1.577 +          return;
   1.578 +        }
   1.579 +
   1.580 +        // If an add-on isn't appDisabled and isn't userDisabled then it is
   1.581 +        // either active now or the user expects it to be active after the
   1.582 +        // restart. If that is the case and the add-on is not installed by the
   1.583 +        // application and is not compatible with the new application version
   1.584 +        // then the user should be warned that the add-on will become
   1.585 +        // incompatible. If an addon's type equals plugin it is skipped since
   1.586 +        // checking plugins compatibility information isn't supported and
   1.587 +        // getting the scope property of a plugin breaks in some environments
   1.588 +        // (see bug 566787). The hotfix add-on is also ignored as it shouldn't
   1.589 +        // block the user from upgrading.
   1.590 +        try {
   1.591 +          if (addon.type != "plugin" && addon.id != hotfixID &&
   1.592 +              !addon.appDisabled && !addon.userDisabled &&
   1.593 +              addon.scope != AddonManager.SCOPE_APPLICATION &&
   1.594 +              addon.isCompatible &&
   1.595 +              !addon.isCompatibleWith(compatVersion,
   1.596 +                                      self.update.platformVersion))
   1.597 +            self.addons.push(addon);
   1.598 +        }
   1.599 +        catch (e) {
   1.600 +          Components.utils.reportError(e);
   1.601 +        }
   1.602 +      });
   1.603 +
   1.604 +      aCallback(self.addons.length != 0);
   1.605 +    });
   1.606 +  },
   1.607 +
   1.608 +  /**
   1.609 +   * Returns the string page ID for the appropriate updates found page based
   1.610 +   * on the update's metadata.
   1.611 +   */
   1.612 +  get updatesFoundPageId() {
   1.613 +    if (gUpdatesFoundPageId)
   1.614 +      return gUpdatesFoundPageId;
   1.615 +    return gUpdatesFoundPageId = this.update.billboardURL ? "updatesfoundbillboard"
   1.616 +                                                          : "updatesfoundbasic";
   1.617 +  },
   1.618 +
   1.619 +  /**
   1.620 +   * Sets the Update object for this wizard
   1.621 +   * @param   update
   1.622 +   *          The update object
   1.623 +   */
   1.624 +  setUpdate: function(update) {
   1.625 +    this.update = update;
   1.626 +    if (this.update)
   1.627 +      this.update.QueryInterface(CoI.nsIWritablePropertyBag);
   1.628 +  }
   1.629 +}
   1.630 +
   1.631 +/**
   1.632 + * The "Checking for Updates" page. Provides feedback on the update checking
   1.633 + * process.
   1.634 + */
   1.635 +var gCheckingPage = {
   1.636 +  /**
   1.637 +   * The nsIUpdateChecker that is currently checking for updates. We hold onto
   1.638 +   * this so we can cancel the update check if the user closes the window.
   1.639 +   */
   1.640 +  _checker: null,
   1.641 +
   1.642 +  /**
   1.643 +   * Initialize
   1.644 +   */
   1.645 +  onPageShow: function() {
   1.646 +    gUpdates.setButtons(null, null, null, false, true);
   1.647 +    gUpdates.wiz.getButton("cancel").focus();
   1.648 +
   1.649 +    // Clear all of the "never" prefs to handle the scenario where the user
   1.650 +    // clicked "never" for an update, selected "Check for Updates...", and
   1.651 +    // then canceled.  If we don't clear the "never" prefs future
   1.652 +    // notifications will never happen.
   1.653 +    Services.prefs.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH);
   1.654 +
   1.655 +    // The user will be notified if there is an error so clear the background
   1.656 +    // check error count.
   1.657 +    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
   1.658 +      Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
   1.659 +
   1.660 +    // The preference will be set back to true if the system is still
   1.661 +    // unsupported.
   1.662 +    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED))
   1.663 +      Services.prefs.clearUserPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED);
   1.664 +
   1.665 +    this._checker = CoC["@mozilla.org/updates/update-checker;1"].
   1.666 +                    createInstance(CoI.nsIUpdateChecker);
   1.667 +    this._checker.checkForUpdates(this.updateListener, true);
   1.668 +  },
   1.669 +
   1.670 +  /**
   1.671 +   * The user has closed the window, either by pressing cancel or using a Window
   1.672 +   * Manager control, so stop checking for updates.
   1.673 +   */
   1.674 +  onWizardCancel: function() {
   1.675 +    this._checker.stopChecking(CoI.nsIUpdateChecker.CURRENT_CHECK);
   1.676 +  },
   1.677 +
   1.678 +  /**
   1.679 +   * An object implementing nsIUpdateCheckListener that is notified as the
   1.680 +   * update check commences.
   1.681 +   */
   1.682 +  updateListener: {
   1.683 +    /**
   1.684 +     * See nsIUpdateCheckListener
   1.685 +     */
   1.686 +    onCheckComplete: function(request, updates, updateCount) {
   1.687 +      var aus = CoC["@mozilla.org/updates/update-service;1"].
   1.688 +                getService(CoI.nsIApplicationUpdateService);
   1.689 +      gUpdates.setUpdate(aus.selectUpdate(updates, updates.length));
   1.690 +      if (gUpdates.update) {
   1.691 +        LOG("gCheckingPage", "onCheckComplete - update found");
   1.692 +        if (gUpdates.update.unsupported) {
   1.693 +          gUpdates.wiz.goTo("unsupported");
   1.694 +          return;
   1.695 +        }
   1.696 +
   1.697 +        if (!aus.canApplyUpdates) {
   1.698 +          // Prevent multiple notifications for the same update when the user is
   1.699 +          // unable to apply updates.
   1.700 +          gUpdates.never();
   1.701 +          gUpdates.wiz.goTo("manualUpdate");
   1.702 +          return;
   1.703 +        }
   1.704 +
   1.705 +        if (gUpdates.update.licenseURL) {
   1.706 +          // gUpdates.updatesFoundPageId returns the pageid and not the
   1.707 +          // element's id so use the wizard's getPageById method.
   1.708 +          gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "license");
   1.709 +        }
   1.710 +
   1.711 +        gUpdates.getShouldCheckAddonCompatibility(function(shouldCheck) {
   1.712 +          if (shouldCheck) {
   1.713 +            var incompatCheckPage = document.getElementById("incompatibleCheck");
   1.714 +            incompatCheckPage.setAttribute("next", gUpdates.updatesFoundPageId);
   1.715 +            gUpdates.wiz.goTo("incompatibleCheck");
   1.716 +          }
   1.717 +          else {
   1.718 +            gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
   1.719 +          }
   1.720 +        });
   1.721 +        return;
   1.722 +      }
   1.723 +
   1.724 +      LOG("gCheckingPage", "onCheckComplete - no update found");
   1.725 +      gUpdates.wiz.goTo("noupdatesfound");
   1.726 +    },
   1.727 +
   1.728 +    /**
   1.729 +     * See nsIUpdateCheckListener
   1.730 +     */
   1.731 +    onError: function(request, update) {
   1.732 +      LOG("gCheckingPage", "onError - proceeding to error page");
   1.733 +      gUpdates.setUpdate(update);
   1.734 +      if (update.errorCode &&
   1.735 +          (update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
   1.736 +           update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE)) {
   1.737 +        gUpdates.wiz.goTo("errorextra");
   1.738 +      }
   1.739 +      else {
   1.740 +        gUpdates.wiz.goTo("errors");
   1.741 +      }
   1.742 +    },
   1.743 +
   1.744 +    /**
   1.745 +     * See nsISupports.idl
   1.746 +     */
   1.747 +    QueryInterface: function(aIID) {
   1.748 +      if (!aIID.equals(CoI.nsIUpdateCheckListener) &&
   1.749 +          !aIID.equals(CoI.nsISupports))
   1.750 +        throw CoR.NS_ERROR_NO_INTERFACE;
   1.751 +      return this;
   1.752 +    }
   1.753 +  }
   1.754 +};
   1.755 +
   1.756 +/**
   1.757 + * The "You have outdated plugins" page
   1.758 + */
   1.759 +var gPluginsPage = {
   1.760 +  /**
   1.761 +   * URL of the plugin updates page
   1.762 +   */
   1.763 +  _url: null,
   1.764 +  
   1.765 +  /**
   1.766 +   * Initialize
   1.767 +   */
   1.768 +  onPageShow: function() {
   1.769 +    var prefs = Services.prefs;
   1.770 +    if (prefs.getPrefType(PREF_PLUGINS_UPDATEURL) == prefs.PREF_INVALID) {
   1.771 +      gUpdates.wiz.goTo("noupdatesfound");
   1.772 +      return;
   1.773 +    }
   1.774 +    
   1.775 +    this._url = Services.urlFormatter.formatURLPref(PREF_PLUGINS_UPDATEURL);
   1.776 +    var link = document.getElementById("pluginupdateslink");
   1.777 +    link.setAttribute("href", this._url);
   1.778 +
   1.779 +
   1.780 +    var phs = CoC["@mozilla.org/plugin/host;1"].
   1.781 +                 getService(CoI.nsIPluginHost);
   1.782 +    var plugins = phs.getPluginTags();
   1.783 +    var blocklist = CoC["@mozilla.org/extensions/blocklist;1"].
   1.784 +                      getService(CoI.nsIBlocklistService);
   1.785 +
   1.786 +    var hasOutdated = false;
   1.787 +    for (let i = 0; i < plugins.length; i++) {
   1.788 +      let pluginState = blocklist.getPluginBlocklistState(plugins[i]);
   1.789 +      if (pluginState == CoI.nsIBlocklistService.STATE_OUTDATED) {
   1.790 +        hasOutdated = true;
   1.791 +        break;
   1.792 +      }
   1.793 +    }
   1.794 +    if (!hasOutdated) {
   1.795 +      gUpdates.wiz.goTo("noupdatesfound");
   1.796 +      return;
   1.797 +    }
   1.798 +
   1.799 +    gUpdates.setButtons(null, null, "okButton", true);
   1.800 +    gUpdates.wiz.getButton("finish").focus();
   1.801 +  },
   1.802 +  
   1.803 +  /**
   1.804 +   * Finish button clicked.
   1.805 +   */
   1.806 +  onWizardFinish: function() {
   1.807 +    openURL(this._url);
   1.808 +  }
   1.809 +};
   1.810 +
   1.811 +/**
   1.812 + * The "No Updates Are Available" page
   1.813 + */
   1.814 +var gNoUpdatesPage = {
   1.815 +  /**
   1.816 +   * Initialize
   1.817 +   */
   1.818 +  onPageShow: function() {
   1.819 +    LOG("gNoUpdatesPage", "onPageShow - could not select an appropriate " +
   1.820 +        "update. Either there were no updates or |selectUpdate| failed");
   1.821 +
   1.822 +    if (getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true))
   1.823 +      document.getElementById("noUpdatesAutoEnabled").hidden = false;
   1.824 +    else
   1.825 +      document.getElementById("noUpdatesAutoDisabled").hidden = false;
   1.826 +
   1.827 +    gUpdates.setButtons(null, null, "okButton", true);
   1.828 +    gUpdates.wiz.getButton("finish").focus();
   1.829 +  }
   1.830 +};
   1.831 +
   1.832 +
   1.833 +/**
   1.834 + * The page that checks if there are any incompatible add-ons.
   1.835 + */
   1.836 +var gIncompatibleCheckPage = {
   1.837 +  /**
   1.838 +   * Count of incompatible add-ons to check for updates
   1.839 +   */
   1.840 +  _totalCount: 0,
   1.841 +
   1.842 +  /**
   1.843 +   * Count of incompatible add-ons that have beend checked for updates
   1.844 +   */
   1.845 +  _completedCount: 0,
   1.846 +
   1.847 +  /**
   1.848 +   * The progress bar for this page
   1.849 +   */
   1.850 +  _pBar: null,
   1.851 +
   1.852 +  /**
   1.853 +   * Initialize
   1.854 +   */
   1.855 +  onPageShow: function() {
   1.856 +    LOG("gIncompatibleCheckPage", "onPageShow - checking for updates to " +
   1.857 +        "incompatible add-ons");
   1.858 +
   1.859 +    gUpdates.setButtons(null, null, null, false, true);
   1.860 +    gUpdates.wiz.getButton("cancel").focus();
   1.861 +    this._pBar = document.getElementById("incompatibleCheckProgress");
   1.862 +    this._totalCount = gUpdates.addons.length;
   1.863 +
   1.864 +    this._pBar.mode = "normal";
   1.865 +#ifdef TOR_BROWSER_UPDATE
   1.866 +    let compatVersion = gUpdates.update.platformVersion;
   1.867 +#else
   1.868 +    let compatVersion = gUpdates.update.appVersion;
   1.869 +#endif
   1.870 +    gUpdates.addons.forEach(function(addon) {
   1.871 +      addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
   1.872 +                        compatVersion,
   1.873 +                        gUpdates.update.platformVersion);
   1.874 +    }, this);
   1.875 +  },
   1.876 +
   1.877 +  // Addon UpdateListener
   1.878 +  onCompatibilityUpdateAvailable: function(addon) {
   1.879 +    // Remove the add-on from the list of add-ons that will become incompatible
   1.880 +    // with the new version of the application.
   1.881 +    for (var i = 0; i < gUpdates.addons.length; ++i) {
   1.882 +      if (gUpdates.addons[i].id == addon.id) {
   1.883 +        LOG("gIncompatibleCheckPage", "onCompatibilityUpdateAvailable - " +
   1.884 +            "found update for add-on ID: " + addon.id);
   1.885 +        gUpdates.addons.splice(i, 1);
   1.886 +        break;
   1.887 +      }
   1.888 +    }
   1.889 +  },
   1.890 +
   1.891 +  onUpdateAvailable: function(addon, install) {
   1.892 +    // If the new version of this add-on is blocklisted for the new application
   1.893 +    // then it isn't a valid update and the user should still be warned that
   1.894 +    // the add-on will become incompatible.
   1.895 +    let bs = CoC["@mozilla.org/extensions/blocklist;1"].
   1.896 +             getService(CoI.nsIBlocklistService);
   1.897 +#ifdef TOR_BROWSER_UPDATE
   1.898 +    let compatVersion = gUpdates.update.platformVersion;
   1.899 +#else
   1.900 +    let compatVersion = gUpdates.update.appVersion;
   1.901 +#endif
   1.902 +    if (bs.isAddonBlocklisted(addon,
   1.903 +                              compatVersion,
   1.904 +                              gUpdates.update.platformVersion))
   1.905 +      return;
   1.906 +
   1.907 +    // Compatibility or new version updates mean the same thing here.
   1.908 +    this.onCompatibilityUpdateAvailable(addon);
   1.909 +  },
   1.910 +
   1.911 +  onUpdateFinished: function(addon) {
   1.912 +    ++this._completedCount;
   1.913 +    this._pBar.value = Math.ceil((this._completedCount / this._totalCount) * 100);
   1.914 +
   1.915 +    if (this._completedCount < this._totalCount)
   1.916 +      return;
   1.917 +
   1.918 +    if (gUpdates.addons.length == 0) {
   1.919 +      LOG("gIncompatibleCheckPage", "onUpdateFinished - updates were found " +
   1.920 +          "for all incompatible add-ons");
   1.921 +    }
   1.922 +    else {
   1.923 +      LOG("gIncompatibleCheckPage", "onUpdateFinished - there are still " +
   1.924 +          "incompatible add-ons");
   1.925 +      if (gUpdates.update.licenseURL) {
   1.926 +        document.getElementById("license").setAttribute("next", "incompatibleList");
   1.927 +      }
   1.928 +      else {
   1.929 +        // gUpdates.updatesFoundPageId returns the pageid and not the element's
   1.930 +        // id so use the wizard's getPageById method.
   1.931 +        gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "incompatibleList");
   1.932 +      }
   1.933 +    }
   1.934 +    gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
   1.935 +  }
   1.936 +};
   1.937 +
   1.938 +/**
   1.939 + * The "Unable to Update" page. Provides the user information about why they
   1.940 + * were unable to update and a manual download url.
   1.941 + */
   1.942 +var gManualUpdatePage = {
   1.943 +  onPageShow: function() {
   1.944 +    var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
   1.945 +    var manualUpdateLinkLabel = document.getElementById("manualUpdateLinkLabel");
   1.946 +    manualUpdateLinkLabel.value = manualURL;
   1.947 +    manualUpdateLinkLabel.setAttribute("url", manualURL);
   1.948 +
   1.949 +    gUpdates.setButtons(null, null, "okButton", true);
   1.950 +    gUpdates.wiz.getButton("finish").focus();
   1.951 +  }
   1.952 +};
   1.953 +
   1.954 +/**
   1.955 + * The "System Unsupported" page. Provides the user with information about their
   1.956 + * system no longer being supported and an url for more information.
   1.957 + */
   1.958 +var gUnsupportedPage = {
   1.959 +  onPageShow: function() {
   1.960 +    Services.prefs.setBoolPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED, true);
   1.961 +    if (gUpdates.update.detailsURL) {
   1.962 +      let unsupportedLinkLabel = document.getElementById("unsupportedLinkLabel");
   1.963 +      unsupportedLinkLabel.setAttribute("url", gUpdates.update.detailsURL);
   1.964 +    }
   1.965 +
   1.966 +    gUpdates.setButtons(null, null, "okButton", true);
   1.967 +    gUpdates.wiz.getButton("finish").focus();
   1.968 +  }
   1.969 +};
   1.970 +
   1.971 +/**
   1.972 + * The "Updates Are Available" page. Provides the user information about the
   1.973 + * available update.
   1.974 + */
   1.975 +var gUpdatesFoundBasicPage = {
   1.976 +  /**
   1.977 +   * Initialize
   1.978 +   */
   1.979 +  onPageShow: function() {
   1.980 +    gUpdates.wiz.canRewind = false;
   1.981 +    var update = gUpdates.update;
   1.982 +    gUpdates.setButtons("askLaterButton",
   1.983 +                        update.showNeverForVersion ? "noThanksButton" : null,
   1.984 +                        "updateButton_" + update.type, true);
   1.985 +    var btn = gUpdates.wiz.getButton("next");
   1.986 +    btn.focus();
   1.987 +
   1.988 +    var updateName = update.name;
   1.989 +    if (update.channel == "nightly") {
   1.990 +      updateName = gUpdates.getAUSString("updateNightlyName",
   1.991 +                                         [gUpdates.brandName,
   1.992 +                                          update.displayVersion,
   1.993 +                                          update.buildID]);
   1.994 +    }
   1.995 +    var updateNameElement = document.getElementById("updateName");
   1.996 +    updateNameElement.value = updateName;
   1.997 +
   1.998 +    var introText = gUpdates.getAUSString("intro_" + update.type,
   1.999 +                                          [gUpdates.brandName, update.displayVersion]);
  1.1000 +    var introElem = document.getElementById("updatesFoundInto");
  1.1001 +    introElem.setAttribute("severity", update.type);
  1.1002 +    introElem.textContent = introText;
  1.1003 +
  1.1004 +    var updateMoreInfoURL = document.getElementById("updateMoreInfoURL");
  1.1005 +    if (update.detailsURL)
  1.1006 +      updateMoreInfoURL.setAttribute("url", update.detailsURL);
  1.1007 +    else
  1.1008 +      updateMoreInfoURL.hidden = true;
  1.1009 +
  1.1010 +    var updateTitle = gUpdates.getAUSString("updatesfound_" + update.type +
  1.1011 +                                            ".title");
  1.1012 +    document.getElementById("updatesFoundBasicHeader").setAttribute("label", updateTitle);
  1.1013 +  },
  1.1014 +
  1.1015 +  onExtra1: function() {
  1.1016 +    gUpdates.wiz.cancel();
  1.1017 +  },
  1.1018 +
  1.1019 +  onExtra2: function() {
  1.1020 +    gUpdates.never();
  1.1021 +    gUpdates.wiz.cancel();
  1.1022 +  }
  1.1023 +};
  1.1024 +
  1.1025 +/**
  1.1026 + * The "Updates Are Available" page with a billboard. Provides the user
  1.1027 + * information about the available update.
  1.1028 + */
  1.1029 +var gUpdatesFoundBillboardPage = {
  1.1030 +  /**
  1.1031 +   * If this page has been previously loaded
  1.1032 +   */
  1.1033 +  _billboardLoaded: false,
  1.1034 +
  1.1035 +  /**
  1.1036 +   * Initialize
  1.1037 +   */
  1.1038 +  onPageShow: function() {
  1.1039 +    var update = gUpdates.update;
  1.1040 +    gUpdates.setButtons("askLaterButton",
  1.1041 +                        update.showNeverForVersion ? "noThanksButton" : null,
  1.1042 +                        "updateButton_" + update.type, true);
  1.1043 +    gUpdates.wiz.getButton("next").focus();
  1.1044 +
  1.1045 +    if (this._billboardLoaded)
  1.1046 +      return;
  1.1047 +
  1.1048 +    var remoteContent = document.getElementById("updateMoreInfoContent");
  1.1049 +    remoteContent.addEventListener("load",
  1.1050 +                                   gUpdatesFoundBillboardPage.onBillboardLoad,
  1.1051 +                                   false);
  1.1052 +    // update_name and update_version need to be set before url
  1.1053 +    // so that when attempting to download the url, we can show
  1.1054 +    // the formatted "Download..." string
  1.1055 +    remoteContent.update_name = gUpdates.brandName;
  1.1056 +    remoteContent.update_version = update.displayVersion;
  1.1057 +
  1.1058 +    var billboardTestURL = getPref("getCharPref", PREF_APP_UPDATE_BILLBOARD_TEST_URL, null);
  1.1059 +    if (billboardTestURL) {
  1.1060 +      // Allow file urls when testing the billboard and fallback to the
  1.1061 +      // normal method if the URL isn't a file.
  1.1062 +      var scheme = Services.io.newURI(billboardTestURL, null, null).scheme;
  1.1063 +      if (scheme == "file")
  1.1064 +        remoteContent.testFileUrl = update.billboardURL;
  1.1065 +      else
  1.1066 +        remoteContent.url = update.billboardURL;
  1.1067 +    }
  1.1068 +    else
  1.1069 +      remoteContent.url = update.billboardURL;
  1.1070 +
  1.1071 +    this._billboardLoaded = true;
  1.1072 +  },
  1.1073 +
  1.1074 +  /**
  1.1075 +   * When the billboard document has loaded
  1.1076 +   */
  1.1077 +  onBillboardLoad: function(aEvent) {
  1.1078 +    var remoteContent = document.getElementById("updateMoreInfoContent");
  1.1079 +    // Note: may be called multiple times due to multiple onLoad events.
  1.1080 +    var state = remoteContent.getAttribute("state");
  1.1081 +    if (state == "loading" || aEvent.originalTarget != remoteContent)
  1.1082 +      return;
  1.1083 +
  1.1084 +    remoteContent.removeEventListener("load", gUpdatesFoundBillboardPage.onBillboardLoad, false);
  1.1085 +    if (state == "error") {
  1.1086 +      gUpdatesFoundPageId = "updatesfoundbasic";
  1.1087 +      var next = gUpdates.wiz.getPageById("updatesfoundbillboard").getAttribute("next");
  1.1088 +      gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", next);
  1.1089 +      gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
  1.1090 +    }
  1.1091 +  },
  1.1092 +
  1.1093 +  onExtra1: function() {
  1.1094 +    this.onWizardCancel();
  1.1095 +    gUpdates.wiz.cancel();
  1.1096 +  },
  1.1097 +
  1.1098 +  onExtra2: function() {
  1.1099 +    this.onWizardCancel();
  1.1100 +    gUpdates.never();
  1.1101 +    gUpdates.wiz.cancel();
  1.1102 +  },
  1.1103 +
  1.1104 +  /**
  1.1105 +   * When the user cancels the wizard
  1.1106 +   */
  1.1107 +  onWizardCancel: function() {
  1.1108 +    try {
  1.1109 +      var remoteContent = document.getElementById("updateMoreInfoContent");
  1.1110 +      if (remoteContent)
  1.1111 +        remoteContent.stopDownloading();
  1.1112 +    }
  1.1113 +    catch (e) {
  1.1114 +      LOG("gUpdatesFoundBillboardPage", "onWizardCancel - " +
  1.1115 +          "moreInfoContent.stopDownloading() failed: " + e);
  1.1116 +    }
  1.1117 +  }
  1.1118 +};
  1.1119 +
  1.1120 +/**
  1.1121 + * The page which shows the user a license associated with an update. The
  1.1122 + * user must agree to the terms of the license before continuing to install
  1.1123 + * the update.
  1.1124 + */
  1.1125 +var gLicensePage = {
  1.1126 +  /**
  1.1127 +   * If the license url has been previously loaded
  1.1128 +   */
  1.1129 +  _licenseLoaded: false,
  1.1130 +
  1.1131 +  /**
  1.1132 +   * Initialize
  1.1133 +   */
  1.1134 +  onPageShow: function() {
  1.1135 +    gUpdates.setButtons("backButton", null, "acceptTermsButton", false);
  1.1136 +
  1.1137 +    var licenseContent = document.getElementById("licenseContent");
  1.1138 +    if (this._licenseLoaded || licenseContent.getAttribute("state") == "error") {
  1.1139 +      this.onAcceptDeclineRadio();
  1.1140 +      var licenseGroup = document.getElementById("acceptDeclineLicense");
  1.1141 +      licenseGroup.focus();
  1.1142 +      return;
  1.1143 +    }
  1.1144 +
  1.1145 +    gUpdates.wiz.canAdvance = false;
  1.1146 +
  1.1147 +    // Disable the license radiogroup until the EULA has been downloaded
  1.1148 +    document.getElementById("acceptDeclineLicense").disabled = true;
  1.1149 +    gUpdates.update.setProperty("licenseAccepted", "false");
  1.1150 +
  1.1151 +    licenseContent.addEventListener("load", gLicensePage.onLicenseLoad, false);
  1.1152 +    // The update_name and update_version need to be set before url so the ui
  1.1153 +    // can display the formatted "Download..." string when attempting to
  1.1154 +    // download the url.
  1.1155 +    licenseContent.update_name = gUpdates.brandName;
  1.1156 +    licenseContent.update_version = gUpdates.update.displayVersion;
  1.1157 +    licenseContent.url = gUpdates.update.licenseURL;
  1.1158 +  },
  1.1159 +
  1.1160 +  /**
  1.1161 +   * When the license document has loaded
  1.1162 +   */
  1.1163 +  onLicenseLoad: function(aEvent) {
  1.1164 +    var licenseContent = document.getElementById("licenseContent");
  1.1165 +    // Disable or enable the radiogroup based on the state attribute of
  1.1166 +    // licenseContent.
  1.1167 +    // Note: may be called multiple times due to multiple onLoad events.
  1.1168 +    var state = licenseContent.getAttribute("state");
  1.1169 +    if (state == "loading" || aEvent.originalTarget != licenseContent)
  1.1170 +      return;
  1.1171 +
  1.1172 +    licenseContent.removeEventListener("load", gLicensePage.onLicenseLoad, false);
  1.1173 +
  1.1174 +    if (state == "error") {
  1.1175 +      gUpdates.wiz.goTo("manualUpdate");
  1.1176 +      return;
  1.1177 +    }
  1.1178 +
  1.1179 +    gLicensePage._licenseLoaded = true;
  1.1180 +    document.getElementById("acceptDeclineLicense").disabled = false;
  1.1181 +    gUpdates.wiz.getButton("extra1").disabled = false;
  1.1182 +  },
  1.1183 +
  1.1184 +  /**
  1.1185 +   * When the user changes the state of the accept / decline radio group
  1.1186 +   */
  1.1187 +  onAcceptDeclineRadio: function() {
  1.1188 +    // Return early if this page hasn't been loaded (bug 405257). This event is
  1.1189 +    // fired during the construction of the wizard before gUpdates has received
  1.1190 +    // its onload event (bug 452389).
  1.1191 +    if (!this._licenseLoaded)
  1.1192 +      return;
  1.1193 +
  1.1194 +    var selectedIndex = document.getElementById("acceptDeclineLicense")
  1.1195 +                                .selectedIndex;
  1.1196 +    // 0 == Accept, 1 == Decline
  1.1197 +    var licenseAccepted = (selectedIndex == 0);
  1.1198 +    gUpdates.wiz.canAdvance = licenseAccepted;
  1.1199 +  },
  1.1200 +
  1.1201 +  /**
  1.1202 +   * The non-standard "Back" button.
  1.1203 +   */
  1.1204 +  onExtra1: function() {
  1.1205 +    gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
  1.1206 +  },
  1.1207 +
  1.1208 +  /**
  1.1209 +   * When the user clicks next after accepting the license
  1.1210 +   */
  1.1211 +  onWizardNext: function() {
  1.1212 +    try {
  1.1213 +      gUpdates.update.setProperty("licenseAccepted", "true");
  1.1214 +      var um = CoC["@mozilla.org/updates/update-manager;1"].
  1.1215 +               getService(CoI.nsIUpdateManager);
  1.1216 +      um.saveUpdates();
  1.1217 +    }
  1.1218 +    catch (e) {
  1.1219 +      LOG("gLicensePage", "onWizardNext - nsIUpdateManager:saveUpdates() " +
  1.1220 +          "failed: " + e);
  1.1221 +    }
  1.1222 +  },
  1.1223 +
  1.1224 +  /**
  1.1225 +   * When the user cancels the wizard
  1.1226 +   */
  1.1227 +  onWizardCancel: function() {
  1.1228 +    try {
  1.1229 +      var licenseContent = document.getElementById("licenseContent");
  1.1230 +      // If the license was downloading, stop it.
  1.1231 +      if (licenseContent)
  1.1232 +        licenseContent.stopDownloading();
  1.1233 +    }
  1.1234 +    catch (e) {
  1.1235 +      LOG("gLicensePage", "onWizardCancel - " +
  1.1236 +          "licenseContent.stopDownloading() failed: " + e);
  1.1237 +    }
  1.1238 +  }
  1.1239 +};
  1.1240 +
  1.1241 +/**
  1.1242 + * The page which shows add-ons that are incompatible and do not have updated
  1.1243 + * compatibility information or a version update available to make them
  1.1244 + * compatible.
  1.1245 + */
  1.1246 +var gIncompatibleListPage = {
  1.1247 +  /**
  1.1248 +   * Initialize
  1.1249 +   */
  1.1250 +  onPageShow: function() {
  1.1251 +    gUpdates.setButtons("backButton", null, "okButton", true);
  1.1252 +    var listbox = document.getElementById("incompatibleListbox");
  1.1253 +    if (listbox.children.length > 0)
  1.1254 +      return;
  1.1255 +
  1.1256 +    var intro = gUpdates.getAUSString("incompatAddons_" + gUpdates.update.type,
  1.1257 +                                      [gUpdates.brandName,
  1.1258 +                                       gUpdates.update.displayVersion]);
  1.1259 +    document.getElementById("incompatibleListDesc").textContent = intro;
  1.1260 +
  1.1261 +    var addons = gUpdates.addons;
  1.1262 +    for (var i = 0; i < addons.length; ++i) {
  1.1263 +      var listitem = document.createElement("listitem");
  1.1264 +      var addonLabel = gUpdates.getAUSString("addonLabel", [addons[i].name,
  1.1265 +                                                            addons[i].version]);
  1.1266 +      listitem.setAttribute("label", addonLabel);
  1.1267 +      listbox.appendChild(listitem);
  1.1268 +    }
  1.1269 +  },
  1.1270 +
  1.1271 +  /**
  1.1272 +   * The non-standard "Back" button.
  1.1273 +   */
  1.1274 +  onExtra1: function() {
  1.1275 +    gUpdates.wiz.goTo(gUpdates.update.licenseURL ? "license"
  1.1276 +                                                 : gUpdates.updatesFoundPageId);
  1.1277 +  }
  1.1278 +};
  1.1279 +
  1.1280 +/**
  1.1281 + * The "Update is Downloading" page - provides feedback for the download
  1.1282 + * process plus a pause/resume UI
  1.1283 + */
  1.1284 +var gDownloadingPage = {
  1.1285 +  /**
  1.1286 +   * DOM Elements
  1.1287 +   */
  1.1288 +  _downloadStatus: null,
  1.1289 +  _downloadProgress: null,
  1.1290 +  _pauseButton: null,
  1.1291 +
  1.1292 +  /**
  1.1293 +   * Whether or not we are currently paused
  1.1294 +   */
  1.1295 +  _paused: false,
  1.1296 +
  1.1297 +  /**
  1.1298 +   * Label cache to hold the 'Connecting' string
  1.1299 +   */
  1.1300 +  _label_downloadStatus: null,
  1.1301 +
  1.1302 +  /**
  1.1303 +   * Member variables for updating download status
  1.1304 +   */
  1.1305 +  _lastSec: Infinity,
  1.1306 +  _startTime: null,
  1.1307 +  _pausedStatus: "",
  1.1308 +
  1.1309 +  _hiding: false,
  1.1310 +
  1.1311 +  /**
  1.1312 +   * Have we registered an observer for a background update being staged
  1.1313 +   */
  1.1314 +  _updateApplyingObserver: false,
  1.1315 +
  1.1316 +  /**
  1.1317 +   * Initialize
  1.1318 +   */
  1.1319 +  onPageShow: function() {
  1.1320 +    this._downloadStatus = document.getElementById("downloadStatus");
  1.1321 +    this._downloadProgress = document.getElementById("downloadProgress");
  1.1322 +    this._pauseButton = document.getElementById("pauseButton");
  1.1323 +    this._label_downloadStatus = this._downloadStatus.textContent;
  1.1324 +
  1.1325 +    this._pauseButton.setAttribute("tooltiptext",
  1.1326 +                                   gUpdates.getAUSString("pauseButtonPause"));
  1.1327 +
  1.1328 +    // move focus to the pause/resume button and then disable it (bug #353177)
  1.1329 +    this._pauseButton.focus();
  1.1330 +    this._pauseButton.disabled = true;
  1.1331 +
  1.1332 +    var aus = CoC["@mozilla.org/updates/update-service;1"].
  1.1333 +              getService(CoI.nsIApplicationUpdateService);
  1.1334 +
  1.1335 +    var um = CoC["@mozilla.org/updates/update-manager;1"].
  1.1336 +             getService(CoI.nsIUpdateManager);
  1.1337 +    var activeUpdate = um.activeUpdate;
  1.1338 +    if (activeUpdate)
  1.1339 +      gUpdates.setUpdate(activeUpdate);
  1.1340 +
  1.1341 +    if (!gUpdates.update) {
  1.1342 +      LOG("gDownloadingPage", "onPageShow - no valid update to download?!");
  1.1343 +      return;
  1.1344 +    }
  1.1345 +
  1.1346 +    this._startTime = Date.now();
  1.1347 +
  1.1348 +    try {
  1.1349 +      // Say that this was a foreground download, not a background download,
  1.1350 +      // since the user cared enough to look in on this process.
  1.1351 +      gUpdates.update.QueryInterface(CoI.nsIWritablePropertyBag);
  1.1352 +      gUpdates.update.setProperty("foregroundDownload", "true");
  1.1353 +
  1.1354 +      // Pause any active background download and restart it as a foreground
  1.1355 +      // download.
  1.1356 +      aus.pauseDownload();
  1.1357 +      var state = aus.downloadUpdate(gUpdates.update, false);
  1.1358 +      if (state == "failed") {
  1.1359 +        // We've tried as hard as we could to download a valid update -
  1.1360 +        // we fell back from a partial patch to a complete patch and even
  1.1361 +        // then we couldn't validate. Show a validation error with instructions
  1.1362 +        // on how to manually update.
  1.1363 +        this.cleanUp();
  1.1364 +        gUpdates.wiz.goTo("errors");
  1.1365 +        return;
  1.1366 +      }
  1.1367 +      else {
  1.1368 +        // Add this UI as a listener for active downloads
  1.1369 +        aus.addDownloadListener(this);
  1.1370 +      }
  1.1371 +
  1.1372 +      if (activeUpdate)
  1.1373 +        this._setUIState(!aus.isDownloading);
  1.1374 +    }
  1.1375 +    catch(e) {
  1.1376 +      LOG("gDownloadingPage", "onPageShow - error: " + e);
  1.1377 +    }
  1.1378 +
  1.1379 +    gUpdates.setButtons("hideButton", null, null, false);
  1.1380 +    gUpdates.wiz.getButton("extra1").focus();
  1.1381 +  },
  1.1382 +
  1.1383 +  /**
  1.1384 +   * Updates the text status message
  1.1385 +   */
  1.1386 +  _setStatus: function(status) {
  1.1387 +    // Don't bother setting the same text more than once. This can happen
  1.1388 +    // due to the asynchronous behavior of the downloader.
  1.1389 +    if (this._downloadStatus.textContent == status)
  1.1390 +      return;
  1.1391 +    while (this._downloadStatus.hasChildNodes())
  1.1392 +      this._downloadStatus.removeChild(this._downloadStatus.firstChild);
  1.1393 +    this._downloadStatus.appendChild(document.createTextNode(status));
  1.1394 +  },
  1.1395 +
  1.1396 +  /**
  1.1397 +   * Update download progress status to show time left, speed, and progress.
  1.1398 +   * Also updates the status needed for pausing the download.
  1.1399 +   *
  1.1400 +   * @param aCurr
  1.1401 +   *        Current number of bytes transferred
  1.1402 +   * @param aMax
  1.1403 +   *        Total file size of the download
  1.1404 +   * @return Current active download status
  1.1405 +   */
  1.1406 +  _updateDownloadStatus: function(aCurr, aMax) {
  1.1407 +    let status;
  1.1408 +
  1.1409 +    // Get the download time left and progress
  1.1410 +    let rate = aCurr / (Date.now() - this._startTime) * 1000;
  1.1411 +    [status, this._lastSec] =
  1.1412 +      DownloadUtils.getDownloadStatus(aCurr, aMax, rate, this._lastSec);
  1.1413 +
  1.1414 +    // Get the download progress for pausing
  1.1415 +    this._pausedStatus = DownloadUtils.getTransferTotal(aCurr, aMax);
  1.1416 +
  1.1417 +    return status;
  1.1418 +  },
  1.1419 +
  1.1420 +  /**
  1.1421 +   * Adjust UI to suit a certain state of paused-ness
  1.1422 +   * @param   paused
  1.1423 +   *          Whether or not the download is paused
  1.1424 +   */
  1.1425 +  _setUIState: function(paused) {
  1.1426 +    var u = gUpdates.update;
  1.1427 +    if (paused) {
  1.1428 +      if (this._downloadProgress.mode != "normal")
  1.1429 +        this._downloadProgress.mode = "normal";
  1.1430 +      this._pauseButton.setAttribute("tooltiptext",
  1.1431 +                                     gUpdates.getAUSString("pauseButtonResume"));
  1.1432 +      this._pauseButton.setAttribute("paused", "true");
  1.1433 +      var p = u.selectedPatch.QueryInterface(CoI.nsIPropertyBag);
  1.1434 +      var status = p.getProperty("status");
  1.1435 +      if (status) {
  1.1436 +        let pausedStatus = gUpdates.getAUSString("downloadPausedStatus", [status]);
  1.1437 +        this._setStatus(pausedStatus);
  1.1438 +      }
  1.1439 +    }
  1.1440 +    else {
  1.1441 +      if (this._downloadProgress.mode != "undetermined")
  1.1442 +        this._downloadProgress.mode = "undetermined";
  1.1443 +      this._pauseButton.setAttribute("paused", "false");
  1.1444 +      this._pauseButton.setAttribute("tooltiptext",
  1.1445 +                                     gUpdates.getAUSString("pauseButtonPause"));
  1.1446 +      this._setStatus(this._label_downloadStatus);
  1.1447 +    }
  1.1448 +  },
  1.1449 +
  1.1450 +  /**
  1.1451 +   * Wait for an update being staged in the background.
  1.1452 +   */
  1.1453 +  _setUpdateApplying: function() {
  1.1454 +    this._downloadProgress.mode = "undetermined";
  1.1455 +    this._pauseButton.hidden = true;
  1.1456 +    let applyingStatus = gUpdates.getAUSString("applyingUpdate");
  1.1457 +    this._setStatus(applyingStatus);
  1.1458 +
  1.1459 +    Services.obs.addObserver(this, "update-staged", false);
  1.1460 +    this._updateApplyingObserver = true;
  1.1461 +  },
  1.1462 +
  1.1463 +  /**
  1.1464 +   * Clean up the listener and observer registered for the wizard.
  1.1465 +   */
  1.1466 +  cleanUp: function() {
  1.1467 +    var aus = CoC["@mozilla.org/updates/update-service;1"].
  1.1468 +              getService(CoI.nsIApplicationUpdateService);
  1.1469 +    aus.removeDownloadListener(this);
  1.1470 +
  1.1471 +    if (this._updateApplyingObserver) {
  1.1472 +      Services.obs.removeObserver(this, "update-staged");
  1.1473 +      this._updateApplyingObserver = false;
  1.1474 +    }
  1.1475 +  },
  1.1476 +
  1.1477 +  /**
  1.1478 +   * When the user clicks the Pause/Resume button
  1.1479 +   */
  1.1480 +  onPause: function() {
  1.1481 +    var aus = CoC["@mozilla.org/updates/update-service;1"].
  1.1482 +              getService(CoI.nsIApplicationUpdateService);
  1.1483 +    if (this._paused)
  1.1484 +      aus.downloadUpdate(gUpdates.update, false);
  1.1485 +    else {
  1.1486 +      var patch = gUpdates.update.selectedPatch;
  1.1487 +      patch.QueryInterface(CoI.nsIWritablePropertyBag);
  1.1488 +      patch.setProperty("status", this._pausedStatus);
  1.1489 +      aus.pauseDownload();
  1.1490 +    }
  1.1491 +    this._paused = !this._paused;
  1.1492 +
  1.1493 +    // Update the UI
  1.1494 +    this._setUIState(this._paused);
  1.1495 +  },
  1.1496 +
  1.1497 +  /**
  1.1498 +   * When the user has closed the window using a Window Manager control (this
  1.1499 +   * page doesn't have a cancel button) cancel the update in progress.
  1.1500 +   */
  1.1501 +  onWizardCancel: function() {
  1.1502 +    if (this._hiding)
  1.1503 +      return;
  1.1504 +
  1.1505 +    this.cleanUp();
  1.1506 +  },
  1.1507 +
  1.1508 +  /**
  1.1509 +   * When the user closes the Wizard UI by clicking the Hide button
  1.1510 +   */
  1.1511 +  onHide: function() {
  1.1512 +    // Set _hiding to true to prevent onWizardCancel from cancelling the update
  1.1513 +    // that is in progress.
  1.1514 +    this._hiding = true;
  1.1515 +
  1.1516 +    // Remove ourself as a download listener so that we don't continue to be
  1.1517 +    // fed progress and state notifications after the UI we're updating has
  1.1518 +    // gone away.
  1.1519 +    this.cleanUp();
  1.1520 +
  1.1521 +    var aus = CoC["@mozilla.org/updates/update-service;1"].
  1.1522 +              getService(CoI.nsIApplicationUpdateService);
  1.1523 +    var um = CoC["@mozilla.org/updates/update-manager;1"].
  1.1524 +             getService(CoI.nsIUpdateManager);
  1.1525 +    um.activeUpdate = gUpdates.update;
  1.1526 +
  1.1527 +    // If the download was paused by the user, ask the user if they want to
  1.1528 +    // have the update resume in the background.
  1.1529 +    var downloadInBackground = true;
  1.1530 +    if (this._paused) {
  1.1531 +      var title = gUpdates.getAUSString("resumePausedAfterCloseTitle");
  1.1532 +      var message = gUpdates.getAUSString("resumePausedAfterCloseMsg",
  1.1533 +                                          [gUpdates.brandName]);
  1.1534 +      var ps = Services.prompt;
  1.1535 +      var flags = ps.STD_YES_NO_BUTTONS;
  1.1536 +      // Focus the software update wizard before prompting. This will raise
  1.1537 +      // the software update wizard if it is minimized making it more obvious
  1.1538 +      // what the prompt is for and will solve the problem of windows
  1.1539 +      // obscuring the prompt. See bug #350299 for more details.
  1.1540 +      window.focus();
  1.1541 +      var rv = ps.confirmEx(window, title, message, flags, null, null, null,
  1.1542 +                            null, { });
  1.1543 +      if (rv == CoI.nsIPromptService.BUTTON_POS_0)
  1.1544 +        downloadInBackground = false;
  1.1545 +    }
  1.1546 +    if (downloadInBackground) {
  1.1547 +      // Continue download in the background at full speed.
  1.1548 +      LOG("gDownloadingPage", "onHide - continuing download in background " +
  1.1549 +          "at full speed");
  1.1550 +      aus.downloadUpdate(gUpdates.update, false);
  1.1551 +    }
  1.1552 +    gUpdates.wiz.cancel();
  1.1553 +  },
  1.1554 +
  1.1555 +  /**
  1.1556 +   * When the data transfer begins
  1.1557 +   * @param   request
  1.1558 +   *          The nsIRequest object for the transfer
  1.1559 +   * @param   context
  1.1560 +   *          Additional data
  1.1561 +   */
  1.1562 +  onStartRequest: function(request, context) {
  1.1563 +    // This !paused test is necessary because onStartRequest may fire after
  1.1564 +    // the download was paused (for those speedy clickers...)
  1.1565 +    if (this._paused)
  1.1566 +      return;
  1.1567 +
  1.1568 +    if (this._downloadProgress.mode != "undetermined")
  1.1569 +      this._downloadProgress.mode = "undetermined";
  1.1570 +    this._setStatus(this._label_downloadStatus);
  1.1571 +  },
  1.1572 +
  1.1573 +  /**
  1.1574 +   * When new data has been downloaded
  1.1575 +   * @param   request
  1.1576 +   *          The nsIRequest object for the transfer
  1.1577 +   * @param   context
  1.1578 +   *          Additional data
  1.1579 +   * @param   progress
  1.1580 +   *          The current number of bytes transferred
  1.1581 +   * @param   maxProgress
  1.1582 +   *          The total number of bytes that must be transferred
  1.1583 +   */
  1.1584 +  onProgress: function(request, context, progress, maxProgress) {
  1.1585 +    let status = this._updateDownloadStatus(progress, maxProgress);
  1.1586 +    var currentProgress = Math.round(100 * (progress / maxProgress));
  1.1587 +
  1.1588 +    var p = gUpdates.update.selectedPatch;
  1.1589 +    p.QueryInterface(CoI.nsIWritablePropertyBag);
  1.1590 +    p.setProperty("progress", currentProgress);
  1.1591 +    p.setProperty("status", status);
  1.1592 +
  1.1593 +    // This !paused test is necessary because onProgress may fire after
  1.1594 +    // the download was paused (for those speedy clickers...)
  1.1595 +    if (this._paused)
  1.1596 +      return;
  1.1597 +
  1.1598 +    if (this._downloadProgress.mode != "normal")
  1.1599 +      this._downloadProgress.mode = "normal";
  1.1600 +    if (this._downloadProgress.value != currentProgress)
  1.1601 +      this._downloadProgress.value = currentProgress;
  1.1602 +    if (this._pauseButton.disabled)
  1.1603 +      this._pauseButton.disabled = false;
  1.1604 +
  1.1605 +    // If the update has completed downloading and the download status contains
  1.1606 +    // the original text return early to avoid an assertion in debug builds.
  1.1607 +    // Since the page will advance immmediately due to the update completing the
  1.1608 +    // download updating the status is not important.
  1.1609 +    // nsTextFrame::GetTrimmedOffsets 'Can only call this on frames that have
  1.1610 +    // been reflowed'.
  1.1611 +    if (progress == maxProgress &&
  1.1612 +        this._downloadStatus.textContent == this._label_downloadStatus)
  1.1613 +      return;
  1.1614 +
  1.1615 +    this._setStatus(status);
  1.1616 +  },
  1.1617 +
  1.1618 +  /**
  1.1619 +   * When we have new status text
  1.1620 +   * @param   request
  1.1621 +   *          The nsIRequest object for the transfer
  1.1622 +   * @param   context
  1.1623 +   *          Additional data
  1.1624 +   * @param   status
  1.1625 +   *          A status code
  1.1626 +   * @param   statusText
  1.1627 +   *          Human readable version of |status|
  1.1628 +   */
  1.1629 +  onStatus: function(request, context, status, statusText) {
  1.1630 +    this._setStatus(statusText);
  1.1631 +  },
  1.1632 +
  1.1633 +  /**
  1.1634 +   * When data transfer ceases
  1.1635 +   * @param   request
  1.1636 +   *          The nsIRequest object for the transfer
  1.1637 +   * @param   context
  1.1638 +   *          Additional data
  1.1639 +   * @param   status
  1.1640 +   *          Status code containing the reason for the cessation.
  1.1641 +   */
  1.1642 +  onStopRequest: function(request, context, status) {
  1.1643 +    if (this._downloadProgress.mode != "normal")
  1.1644 +      this._downloadProgress.mode = "normal";
  1.1645 +
  1.1646 +    var u = gUpdates.update;
  1.1647 +    switch (status) {
  1.1648 +    case CoR.NS_ERROR_CORRUPTED_CONTENT:
  1.1649 +    case CoR.NS_ERROR_UNEXPECTED:
  1.1650 +      if (u.selectedPatch.state == STATE_DOWNLOAD_FAILED &&
  1.1651 +          (u.isCompleteUpdate || u.patchCount != 2)) {
  1.1652 +        // Verification error of complete patch, informational text is held in
  1.1653 +        // the update object.
  1.1654 +        this.cleanUp();
  1.1655 +        gUpdates.wiz.goTo("errors");
  1.1656 +        break;
  1.1657 +      }
  1.1658 +      // Verification failed for a partial patch, complete patch is now
  1.1659 +      // downloading so return early and do NOT remove the download listener!
  1.1660 +
  1.1661 +      // Reset the progress meter to "undertermined" mode so that we don't
  1.1662 +      // show old progress for the new download of the "complete" patch.
  1.1663 +      this._downloadProgress.mode = "undetermined";
  1.1664 +      this._pauseButton.disabled = true;
  1.1665 +      document.getElementById("verificationFailed").hidden = false;
  1.1666 +      break;
  1.1667 +    case CoR.NS_BINDING_ABORTED:
  1.1668 +      LOG("gDownloadingPage", "onStopRequest - pausing download");
  1.1669 +      // Do not remove UI listener since the user may resume downloading again.
  1.1670 +      break;
  1.1671 +    case CoR.NS_OK:
  1.1672 +      LOG("gDownloadingPage", "onStopRequest - patch verification succeeded");
  1.1673 +      // If the background update pref is set, we should wait until the update
  1.1674 +      // is actually staged in the background.
  1.1675 +      var aus = CoC["@mozilla.org/updates/update-service;1"].
  1.1676 +                getService(CoI.nsIApplicationUpdateService);
  1.1677 +      if (aus.canStageUpdates) {
  1.1678 +        this._setUpdateApplying();
  1.1679 +      } else {
  1.1680 +        this.cleanUp();
  1.1681 +        gUpdates.wiz.goTo("finished");
  1.1682 +      }
  1.1683 +      break;
  1.1684 +    default:
  1.1685 +      LOG("gDownloadingPage", "onStopRequest - transfer failed");
  1.1686 +      // Some kind of transfer error, die.
  1.1687 +      this.cleanUp();
  1.1688 +      gUpdates.wiz.goTo("errors");
  1.1689 +      break;
  1.1690 +    }
  1.1691 +  },
  1.1692 +
  1.1693 +  /**
  1.1694 +   * See nsIObserver.idl
  1.1695 +   */
  1.1696 +  observe: function(aSubject, aTopic, aData) {
  1.1697 +    if (aTopic == "update-staged") {
  1.1698 +      if (aData == STATE_DOWNLOADING) {
  1.1699 +        // We've fallen back to downloding the full update because the
  1.1700 +        // partial update failed to get staged in the background.
  1.1701 +        this._setStatus("downloading");
  1.1702 +        return;
  1.1703 +      }
  1.1704 +      this.cleanUp();
  1.1705 +      if (aData == STATE_APPLIED ||
  1.1706 +          aData == STATE_APPLIED_SVC ||
  1.1707 +          aData == STATE_PENDING ||
  1.1708 +          aData == STATE_PENDING_SVC) {
  1.1709 +        // If the update is successfully applied, or if the updater has
  1.1710 +        // fallen back to non-staged updates, go to the finish page.
  1.1711 +        gUpdates.wiz.goTo("finished");
  1.1712 +      } else {
  1.1713 +        gUpdates.wiz.goTo("errors");
  1.1714 +      }
  1.1715 +    }
  1.1716 +  },
  1.1717 +
  1.1718 +  /**
  1.1719 +   * See nsISupports.idl
  1.1720 +   */
  1.1721 +  QueryInterface: function(iid) {
  1.1722 +    if (!iid.equals(CoI.nsIRequestObserver) &&
  1.1723 +        !iid.equals(CoI.nsIProgressEventSink) &&
  1.1724 +        !iid.equals(CoI.nsIObserver) &&
  1.1725 +        !iid.equals(CoI.nsISupports))
  1.1726 +      throw CoR.NS_ERROR_NO_INTERFACE;
  1.1727 +    return this;
  1.1728 +  }
  1.1729 +};
  1.1730 +
  1.1731 +/**
  1.1732 + * The "There was an error during the update" page.
  1.1733 + */
  1.1734 +var gErrorsPage = {
  1.1735 +  /**
  1.1736 +   * Initialize
  1.1737 +   */
  1.1738 +  onPageShow: function() {
  1.1739 +    gUpdates.setButtons(null, null, "okButton", true);
  1.1740 +    gUpdates.wiz.getButton("finish").focus();
  1.1741 +
  1.1742 +    var statusText = gUpdates.update.statusText;
  1.1743 +    LOG("gErrorsPage" , "onPageShow - update.statusText: " + statusText);
  1.1744 +
  1.1745 +    var errorReason = document.getElementById("errorReason");
  1.1746 +    errorReason.value = statusText;
  1.1747 +    var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
  1.1748 +    var errorLinkLabel = document.getElementById("errorLinkLabel");
  1.1749 +    errorLinkLabel.value = manualURL;
  1.1750 +    errorLinkLabel.setAttribute("url", manualURL);
  1.1751 +  }
  1.1752 +};
  1.1753 +
  1.1754 +/**
  1.1755 + * The page shown when there is a background check or a certificate attribute
  1.1756 + * error.
  1.1757 + */
  1.1758 +var gErrorExtraPage = {
  1.1759 +  /**
  1.1760 +   * Initialize
  1.1761 +   */
  1.1762 +  onPageShow: function() {
  1.1763 +    gUpdates.setButtons(null, null, "okButton", true);
  1.1764 +    gUpdates.wiz.getButton("finish").focus();
  1.1765 +    let secHistogram = CoC["@mozilla.org/base/telemetry;1"].
  1.1766 +                                  getService(CoI.nsITelemetry).
  1.1767 +                                  getHistogramById("SECURITY_UI");
  1.1768 +
  1.1769 +    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS))
  1.1770 +      Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS);
  1.1771 +
  1.1772 +    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
  1.1773 +      Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
  1.1774 +
  1.1775 +    if (gUpdates.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE) {
  1.1776 +      document.getElementById("errorCertAttrHasUpdateLabel").hidden = false;
  1.1777 +      secHistogram.add(CoI.nsISecurityUITelemetry.WARNING_INSECURE_UPDATE);
  1.1778 +    }
  1.1779 +    else {
  1.1780 +      if (gUpdates.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE){
  1.1781 +        document.getElementById("errorCertCheckNoUpdateLabel").hidden = false;
  1.1782 +        secHistogram.add(CoI.nsISecurityUITelemetry.WARNING_NO_SECURE_UPDATE);
  1.1783 +      }
  1.1784 +      else
  1.1785 +        document.getElementById("genericBackgroundErrorLabel").hidden = false;
  1.1786 +      var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
  1.1787 +      var errorLinkLabel = document.getElementById("errorExtraLinkLabel");
  1.1788 +      errorLinkLabel.value = manualURL;
  1.1789 +      errorLinkLabel.setAttribute("url", manualURL);
  1.1790 +      errorLinkLabel.hidden = false;
  1.1791 +    }
  1.1792 +  }
  1.1793 +};
  1.1794 +
  1.1795 +/**
  1.1796 + * The "There was an error applying a partial patch" page.
  1.1797 + */
  1.1798 +var gErrorPatchingPage = {
  1.1799 +  /**
  1.1800 +   * Initialize
  1.1801 +   */
  1.1802 +  onPageShow: function() {
  1.1803 +    gUpdates.setButtons(null, null, "okButton", true);
  1.1804 +  },
  1.1805 +
  1.1806 +  onWizardNext: function() {
  1.1807 +    switch (gUpdates.update.selectedPatch.state) {
  1.1808 +    case STATE_PENDING:
  1.1809 +    case STATE_PENDING_SVC: 
  1.1810 +      gUpdates.wiz.goTo("finished");
  1.1811 +      break;
  1.1812 +    case STATE_DOWNLOADING:
  1.1813 +      gUpdates.wiz.goTo("downloading");
  1.1814 +      break;
  1.1815 +    case STATE_DOWNLOAD_FAILED:
  1.1816 +      gUpdates.wiz.goTo("errors");
  1.1817 +      break;
  1.1818 +    }
  1.1819 +  }
  1.1820 +};
  1.1821 +
  1.1822 +/**
  1.1823 + * The "Update has been downloaded" page. Shows information about what
  1.1824 + * was downloaded.
  1.1825 + */
  1.1826 +var gFinishedPage = {
  1.1827 +  /**
  1.1828 +   * Initialize
  1.1829 +   */
  1.1830 +  onPageShow: function() {
  1.1831 +    gUpdates.setButtons("restartLaterButton", null, "restartNowButton",
  1.1832 +                        true);
  1.1833 +    gUpdates.wiz.getButton("finish").focus();
  1.1834 +  },
  1.1835 +
  1.1836 +  /**
  1.1837 +   * Initialize the Wizard Page for a Background Source Event
  1.1838 +   */
  1.1839 +  onPageShowBackground: function() {
  1.1840 +    this.onPageShow();
  1.1841 +    var updateFinishedName = document.getElementById("updateFinishedName");
  1.1842 +    updateFinishedName.value = gUpdates.update.name;
  1.1843 +
  1.1844 +    var link = document.getElementById("finishedBackgroundLink");
  1.1845 +    if (gUpdates.update.detailsURL) {
  1.1846 +      link.setAttribute("url", gUpdates.update.detailsURL);
  1.1847 +      // The details link is stealing focus so it is disabled by default and
  1.1848 +      // should only be enabled after onPageShow has been called.
  1.1849 +      link.disabled = false;
  1.1850 +    }
  1.1851 +    else
  1.1852 +      link.hidden = true;
  1.1853 +
  1.1854 +    if (getPref("getBoolPref", PREF_APP_UPDATE_TEST_LOOP, false)) {
  1.1855 +      setTimeout(function () {
  1.1856 +                   gUpdates.wiz.getButton("finish").click();
  1.1857 +                 }, UPDATE_TEST_LOOP_INTERVAL);
  1.1858 +    }
  1.1859 +  },
  1.1860 +
  1.1861 +  /**
  1.1862 +   * Called when the wizard finishes, i.e. the "Restart Now" button is
  1.1863 +   * clicked.
  1.1864 +   */
  1.1865 +  onWizardFinish: function() {
  1.1866 +    // Do the restart
  1.1867 +    LOG("gFinishedPage" , "onWizardFinish - restarting the application");
  1.1868 +
  1.1869 +    // disable the "finish" (Restart) and "extra1" (Later) buttons
  1.1870 +    // because the Software Update wizard is still up at the point,
  1.1871 +    // and will remain up until we return and we close the
  1.1872 +    // window with a |window.close()| in wizard.xml
  1.1873 +    // (it was the firing the "wizardfinish" event that got us here.)
  1.1874 +    // This prevents the user from switching back
  1.1875 +    // to the Software Update dialog and clicking "Restart" or "Later"
  1.1876 +    // when dealing with the "confirm close" prompts.
  1.1877 +    // See bug #350299 for more details.
  1.1878 +    gUpdates.wiz.getButton("finish").disabled = true;
  1.1879 +    gUpdates.wiz.getButton("extra1").disabled = true;
  1.1880 +
  1.1881 +    // Notify all windows that an application quit has been requested.
  1.1882 +    var cancelQuit = CoC["@mozilla.org/supports-PRBool;1"].
  1.1883 +                     createInstance(CoI.nsISupportsPRBool);
  1.1884 +    Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
  1.1885 +                                 "restart");
  1.1886 +
  1.1887 +    // Something aborted the quit process.
  1.1888 +    if (cancelQuit.data)
  1.1889 +      return;
  1.1890 +
  1.1891 +    // If already in safe mode restart in safe mode (bug 327119)
  1.1892 +    if (Services.appinfo.inSafeMode) {
  1.1893 +      let env = CoC["@mozilla.org/process/environment;1"].
  1.1894 +                getService(CoI.nsIEnvironment);
  1.1895 +      env.set("MOZ_SAFE_MODE_RESTART", "1");
  1.1896 +    }
  1.1897 +
  1.1898 +    // Restart the application
  1.1899 +    CoC["@mozilla.org/toolkit/app-startup;1"].getService(CoI.nsIAppStartup).
  1.1900 +    quit(CoI.nsIAppStartup.eAttemptQuit | CoI.nsIAppStartup.eRestart);
  1.1901 +  },
  1.1902 +
  1.1903 +  /**
  1.1904 +   * When the user clicks the "Restart Later" instead of the Restart Now" button
  1.1905 +   * in the wizard after an update has been downloaded.
  1.1906 +   */
  1.1907 +  onExtra1: function() {
  1.1908 +    gUpdates.wiz.cancel();
  1.1909 +  }
  1.1910 +};
  1.1911 +
  1.1912 +/**
  1.1913 + * The "Update was Installed Successfully" page.
  1.1914 + */
  1.1915 +var gInstalledPage = {
  1.1916 +  /**
  1.1917 +   * Initialize
  1.1918 +   */
  1.1919 +  onPageShow: function() {
  1.1920 +    var branding = document.getElementById("brandStrings");
  1.1921 +    try {
  1.1922 +      // whatsNewURL should just be a pref (bug 546609).
  1.1923 +      var url = branding.getFormattedString("whatsNewURL", [Services.appinfo.version]);
  1.1924 +      var whatsnewLink = document.getElementById("whatsnewLink");
  1.1925 +      whatsnewLink.setAttribute("url", url);
  1.1926 +      whatsnewLink.hidden = false;
  1.1927 +    }
  1.1928 +    catch (e) {
  1.1929 +    }
  1.1930 +
  1.1931 +    gUpdates.setButtons(null, null, "okButton", true);
  1.1932 +    gUpdates.wiz.getButton("finish").focus();
  1.1933 +  }
  1.1934 +};
  1.1935 +
  1.1936 +/**
  1.1937 + * Callback for the Update Prompt to set the current page if an Update Wizard
  1.1938 + * window is already found to be open.
  1.1939 + * @param   pageid
  1.1940 + *          The ID of the page to switch to
  1.1941 + */
  1.1942 +function setCurrentPage(pageid) {
  1.1943 +  gUpdates.wiz.currentPage = document.getElementById(pageid);
  1.1944 +}

mercurial