toolkit/mozapps/update/content/updates.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 #filter substitution
michael@0 2
michael@0 3 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 4 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 7
michael@0 8 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
michael@0 9 Components.utils.import("resource://gre/modules/AddonManager.jsm");
michael@0 10 Components.utils.import("resource://gre/modules/Services.jsm");
michael@0 11
michael@0 12 // Firefox's macBrowserOverlay.xul includes scripts that define Cc, Ci, and Cr
michael@0 13 // so we have to use different names.
michael@0 14 const CoC = Components.classes;
michael@0 15 const CoI = Components.interfaces;
michael@0 16 const CoR = Components.results;
michael@0 17
michael@0 18 const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 19
michael@0 20 const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors";
michael@0 21 const PREF_APP_UPDATE_BILLBOARD_TEST_URL = "app.update.billboard.test_url";
michael@0 22 const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
michael@0 23 const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
michael@0 24 const PREF_APP_UPDATE_LOG = "app.update.log";
michael@0 25 const PREF_APP_UPDATE_MANUAL_URL = "app.update.url.manual";
michael@0 26 const PREF_APP_UPDATE_NEVER_BRANCH = "app.update.never.";
michael@0 27 const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
michael@0 28 const PREF_APP_UPDATE_TEST_LOOP = "app.update.test.loop";
michael@0 29 const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
michael@0 30
michael@0 31 const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
michael@0 32
michael@0 33 const UPDATE_TEST_LOOP_INTERVAL = 2000;
michael@0 34
michael@0 35 const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties";
michael@0 36
michael@0 37 const STATE_DOWNLOADING = "downloading";
michael@0 38 const STATE_PENDING = "pending";
michael@0 39 const STATE_PENDING_SVC = "pending-service";
michael@0 40 const STATE_APPLYING = "applying";
michael@0 41 const STATE_APPLIED = "applied";
michael@0 42 const STATE_APPLIED_SVC = "applied-service";
michael@0 43 const STATE_SUCCEEDED = "succeeded";
michael@0 44 const STATE_DOWNLOAD_FAILED = "download-failed";
michael@0 45 const STATE_FAILED = "failed";
michael@0 46
michael@0 47 const SRCEVT_FOREGROUND = 1;
michael@0 48 const SRCEVT_BACKGROUND = 2;
michael@0 49
michael@0 50 const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
michael@0 51 const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
michael@0 52 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
michael@0 53
michael@0 54 #ifdef TOR_BROWSER_VERSION
michael@0 55 # Add double-quotes back on (stripped by JarMaker.py).
michael@0 56 #expand const TOR_BROWSER_VERSION = "__TOR_BROWSER_VERSION__";
michael@0 57 #endif
michael@0 58
michael@0 59 var gLogEnabled = false;
michael@0 60 var gUpdatesFoundPageId;
michael@0 61
michael@0 62 // Notes:
michael@0 63 // 1. use the wizard's goTo method whenever possible to change the wizard
michael@0 64 // page since it is simpler than most other methods and behaves nicely with
michael@0 65 // mochitests.
michael@0 66 // 2. using a page's onPageShow method to then change to a different page will
michael@0 67 // of course call that page's onPageShow method which can make mochitests
michael@0 68 // overly complicated and fragile so avoid doing this if at all possible.
michael@0 69 // This is why a page's next attribute is set prior to the page being shown
michael@0 70 // whenever possible.
michael@0 71
michael@0 72 /**
michael@0 73 * Logs a string to the error console.
michael@0 74 * @param string
michael@0 75 * The string to write to the error console..
michael@0 76 */
michael@0 77 function LOG(module, string) {
michael@0 78 if (gLogEnabled) {
michael@0 79 dump("*** AUS:UI " + module + ":" + string + "\n");
michael@0 80 Services.console.logStringMessage("AUS:UI " + module + ":" + string);
michael@0 81 }
michael@0 82 }
michael@0 83
michael@0 84 /**
michael@0 85 * Opens a URL using the event target's url attribute for the URL. This is a
michael@0 86 * workaround for Bug 263433 which prevents respecting tab browser preferences
michael@0 87 * for where to open a URL.
michael@0 88 */
michael@0 89 function openUpdateURL(event) {
michael@0 90 if (event.button == 0)
michael@0 91 openURL(event.target.getAttribute("url"));
michael@0 92 }
michael@0 93
michael@0 94 /**
michael@0 95 * Gets a preference value, handling the case where there is no default.
michael@0 96 * @param func
michael@0 97 * The name of the preference function to call, on nsIPrefBranch
michael@0 98 * @param preference
michael@0 99 * The name of the preference
michael@0 100 * @param defaultValue
michael@0 101 * The default value to return in the event the preference has
michael@0 102 * no setting
michael@0 103 * @returns The value of the preference, or undefined if there was no
michael@0 104 * user or default value.
michael@0 105 */
michael@0 106 function getPref(func, preference, defaultValue) {
michael@0 107 try {
michael@0 108 return Services.prefs[func](preference);
michael@0 109 }
michael@0 110 catch (e) {
michael@0 111 LOG("General", "getPref - failed to get preference: " + preference);
michael@0 112 }
michael@0 113 return defaultValue;
michael@0 114 }
michael@0 115
michael@0 116 /**
michael@0 117 * A set of shared data and control functions for the wizard as a whole.
michael@0 118 */
michael@0 119 var gUpdates = {
michael@0 120 /**
michael@0 121 * The nsIUpdate object being used by this window (either for downloading,
michael@0 122 * notification or both).
michael@0 123 */
michael@0 124 update: null,
michael@0 125
michael@0 126 /**
michael@0 127 * List of incompatible add-ons
michael@0 128 */
michael@0 129 addons: [],
michael@0 130
michael@0 131 /**
michael@0 132 * The updates.properties <stringbundle> element.
michael@0 133 */
michael@0 134 strings: null,
michael@0 135
michael@0 136 /**
michael@0 137 * The Application brandShortName (e.g. "Firefox")
michael@0 138 */
michael@0 139 brandName: null,
michael@0 140
michael@0 141 /**
michael@0 142 * The <wizard> element
michael@0 143 */
michael@0 144 wiz: null,
michael@0 145
michael@0 146 /**
michael@0 147 * Whether to run the unload handler. This will be set to false when the user
michael@0 148 * exits the wizard via onWizardCancel or onWizardFinish.
michael@0 149 */
michael@0 150 _runUnload: true,
michael@0 151
michael@0 152 /**
michael@0 153 * Submit the last page code when the wizard exited. The pageid is used to map
michael@0 154 * to an integer instead of using the pageindex since pages can be added and
michael@0 155 * removed which would change the page's pageindex.
michael@0 156 * @param pageID
michael@0 157 */
michael@0 158 _sendLastPageCodePing: function(pageID) {
michael@0 159 var pageMap = { invalid: 0,
michael@0 160 dummy: 1,
michael@0 161 checking: 2,
michael@0 162 pluginupdatesfound: 3,
michael@0 163 noupdatesfound: 4,
michael@0 164 manualUpdate: 5,
michael@0 165 unsupported: 6,
michael@0 166 incompatibleCheck: 7,
michael@0 167 updatesfoundbasic: 8,
michael@0 168 updatesfoundbillboard: 9,
michael@0 169 license: 10,
michael@0 170 incompatibleList: 11,
michael@0 171 downloading: 12,
michael@0 172 errors: 13,
michael@0 173 errorextra: 14,
michael@0 174 errorpatching: 15,
michael@0 175 finished: 16,
michael@0 176 finishedBackground: 17,
michael@0 177 installed: 18 };
michael@0 178 try {
michael@0 179 Services.telemetry.getHistogramById("UPDATER_WIZ_LAST_PAGE_CODE").
michael@0 180 add(pageMap[pageID] || pageMap.invalid);
michael@0 181 }
michael@0 182 catch (e) {
michael@0 183 Components.utils.reportError(e);
michael@0 184 }
michael@0 185 },
michael@0 186
michael@0 187 /**
michael@0 188 * Helper function for setButtons
michael@0 189 * Resets button to original label & accesskey if string is null.
michael@0 190 */
michael@0 191 _setButton: function(button, string) {
michael@0 192 if (string) {
michael@0 193 var label = this.getAUSString(string);
michael@0 194 if (label.indexOf("%S") != -1)
michael@0 195 label = label.replace(/%S/, this.brandName);
michael@0 196 button.label = label;
michael@0 197 button.setAttribute("accesskey",
michael@0 198 this.getAUSString(string + ".accesskey"));
michael@0 199 } else {
michael@0 200 button.label = button.defaultLabel;
michael@0 201 button.setAttribute("accesskey", button.defaultAccesskey);
michael@0 202 }
michael@0 203 },
michael@0 204
michael@0 205 /**
michael@0 206 * Sets the attributes needed for this Wizard's control buttons (labels,
michael@0 207 * disabled, hidden, etc.)
michael@0 208 * @param extra1ButtonString
michael@0 209 * The property in the stringbundle containing the label to put on
michael@0 210 * the first extra button, or null to hide the first extra button.
michael@0 211 * @param extra2ButtonString
michael@0 212 * The property in the stringbundle containing the label to put on
michael@0 213 * the second extra button, or null to hide the second extra button.
michael@0 214 * @param nextFinishButtonString
michael@0 215 * The property in the stringbundle containing the label to put on
michael@0 216 * the Next / Finish button, or null to hide the button. The Next and
michael@0 217 * Finish buttons are never displayed at the same time in a wizard
michael@0 218 * with the the Finish button only being displayed when there are no
michael@0 219 * additional pages to display in the wizard.
michael@0 220 * @param canAdvance
michael@0 221 * true if the wizard can be advanced (e.g. the next / finish button
michael@0 222 * should be enabled), false otherwise.
michael@0 223 * @param showCancel
michael@0 224 * true if the wizard's cancel button should be shown, false
michael@0 225 * otherwise. If not specified this will default to false.
michael@0 226 *
michael@0 227 * Note:
michael@0 228 * Per Bug 324121 the wizard should not look like a wizard and to accomplish
michael@0 229 * this the back button is never displayed and the cancel button is only
michael@0 230 * displayed for the checking and the incompatibleCheck pages. This causes the
michael@0 231 * wizard buttons to be arranged as follows on Windows with the next and
michael@0 232 * finish buttons never being displayed at the same time.
michael@0 233 * +--------------------------------------------------------------+
michael@0 234 * | [ extra1 ] [ extra2 ] [ next or finish ] |
michael@0 235 * +--------------------------------------------------------------+
michael@0 236 */
michael@0 237 setButtons: function(extra1ButtonString, extra2ButtonString,
michael@0 238 nextFinishButtonString, canAdvance, showCancel) {
michael@0 239 this.wiz.canAdvance = canAdvance;
michael@0 240
michael@0 241 var bnf = this.wiz.getButton(this.wiz.onLastPage ? "finish" : "next");
michael@0 242 var be1 = this.wiz.getButton("extra1");
michael@0 243 var be2 = this.wiz.getButton("extra2");
michael@0 244 var bc = this.wiz.getButton("cancel");
michael@0 245
michael@0 246 // Set the labels for the next / finish, extra1, and extra2 buttons
michael@0 247 this._setButton(bnf, nextFinishButtonString);
michael@0 248 this._setButton(be1, extra1ButtonString);
michael@0 249 this._setButton(be2, extra2ButtonString);
michael@0 250
michael@0 251 bnf.hidden = bnf.disabled = !nextFinishButtonString;
michael@0 252 be1.hidden = be1.disabled = !extra1ButtonString;
michael@0 253 be2.hidden = be2.disabled = !extra2ButtonString;
michael@0 254 bc.hidden = bc.disabled = !showCancel;
michael@0 255
michael@0 256 // Hide and disable the back button each time setButtons is called
michael@0 257 // (see bug 464765).
michael@0 258 var btn = this.wiz.getButton("back");
michael@0 259 btn.hidden = btn.disabled = true;
michael@0 260
michael@0 261 // Hide and disable the finish button if not on the last page or the next
michael@0 262 // button if on the last page each time setButtons is called.
michael@0 263 btn = this.wiz.getButton(this.wiz.onLastPage ? "next" : "finish");
michael@0 264 btn.hidden = btn.disabled = true;
michael@0 265 },
michael@0 266
michael@0 267 getAUSString: function(key, strings) {
michael@0 268 if (strings)
michael@0 269 return this.strings.getFormattedString(key, strings);
michael@0 270 return this.strings.getString(key);
michael@0 271 },
michael@0 272
michael@0 273 never: function () {
michael@0 274 // If the user clicks "No Thanks", we should not prompt them to update to
michael@0 275 // this version again unless they manually select "Check for Updates..."
michael@0 276 // which will clear all of the "never" prefs.
michael@0 277 var neverPrefName = PREF_APP_UPDATE_NEVER_BRANCH + this.update.appVersion;
michael@0 278 Services.prefs.setBoolPref(neverPrefName, true);
michael@0 279 },
michael@0 280
michael@0 281 /**
michael@0 282 * A hash of |pageid| attribute to page object. Can be used to dispatch
michael@0 283 * function calls to the appropriate page.
michael@0 284 */
michael@0 285 _pages: { },
michael@0 286
michael@0 287 /**
michael@0 288 * Called when the user presses the "Finish" button on the wizard, dispatches
michael@0 289 * the function call to the selected page.
michael@0 290 */
michael@0 291 onWizardFinish: function() {
michael@0 292 this._runUnload = false;
michael@0 293 var pageid = document.documentElement.currentPage.pageid;
michael@0 294 if ("onWizardFinish" in this._pages[pageid])
michael@0 295 this._pages[pageid].onWizardFinish();
michael@0 296 this._sendLastPageCodePing(pageid);
michael@0 297 },
michael@0 298
michael@0 299 /**
michael@0 300 * Called when the user presses the "Cancel" button on the wizard, dispatches
michael@0 301 * the function call to the selected page.
michael@0 302 */
michael@0 303 onWizardCancel: function() {
michael@0 304 this._runUnload = false;
michael@0 305 var pageid = document.documentElement.currentPage.pageid;
michael@0 306 if ("onWizardCancel" in this._pages[pageid])
michael@0 307 this._pages[pageid].onWizardCancel();
michael@0 308 this._sendLastPageCodePing(pageid);
michael@0 309 },
michael@0 310
michael@0 311 /**
michael@0 312 * Called when the user presses the "Next" button on the wizard, dispatches
michael@0 313 * the function call to the selected page.
michael@0 314 */
michael@0 315 onWizardNext: function() {
michael@0 316 var cp = document.documentElement.currentPage;
michael@0 317 if (!cp)
michael@0 318 return;
michael@0 319 var pageid = cp.pageid;
michael@0 320 if ("onWizardNext" in this._pages[pageid])
michael@0 321 this._pages[pageid].onWizardNext();
michael@0 322 },
michael@0 323
michael@0 324 /**
michael@0 325 * The checking process that spawned this update UI. There are two types:
michael@0 326 * SRCEVT_FOREGROUND:
michael@0 327 * Some user-generated event caused this UI to appear, e.g. the Help
michael@0 328 * menu item or the button in preferences. When in this mode, the UI
michael@0 329 * should remain active for the duration of the download.
michael@0 330 * SRCEVT_BACKGROUND:
michael@0 331 * A background update check caused this UI to appear, probably because
michael@0 332 * incompatibilities in Extensions or other addons were discovered and
michael@0 333 * the user's consent to continue was required. When in this mode, the
michael@0 334 * UI will disappear after the user's consent is obtained.
michael@0 335 */
michael@0 336 sourceEvent: SRCEVT_FOREGROUND,
michael@0 337
michael@0 338 /**
michael@0 339 * Helper function for onLoad
michael@0 340 * Saves default button label & accesskey for use by _setButton
michael@0 341 */
michael@0 342 _cacheButtonStrings: function (buttonName) {
michael@0 343 var button = this.wiz.getButton(buttonName);
michael@0 344 button.defaultLabel = button.label;
michael@0 345 button.defaultAccesskey = button.getAttribute("accesskey");
michael@0 346 },
michael@0 347
michael@0 348 /**
michael@0 349 * Called when the wizard UI is loaded.
michael@0 350 */
michael@0 351 onLoad: function() {
michael@0 352 this.wiz = document.documentElement;
michael@0 353
michael@0 354 gLogEnabled = getPref("getBoolPref", PREF_APP_UPDATE_LOG, false)
michael@0 355
michael@0 356 this.strings = document.getElementById("updateStrings");
michael@0 357 var brandStrings = document.getElementById("brandStrings");
michael@0 358 this.brandName = brandStrings.getString("brandShortName");
michael@0 359
michael@0 360 var pages = this.wiz.childNodes;
michael@0 361 for (var i = 0; i < pages.length; ++i) {
michael@0 362 var page = pages[i];
michael@0 363 if (page.localName == "wizardpage")
michael@0 364 this._pages[page.pageid] = eval(page.getAttribute("object"));
michael@0 365 }
michael@0 366
michael@0 367 // Cache the standard button labels in case we need to restore them
michael@0 368 this._cacheButtonStrings("next");
michael@0 369 this._cacheButtonStrings("finish");
michael@0 370 this._cacheButtonStrings("extra1");
michael@0 371 this._cacheButtonStrings("extra2");
michael@0 372
michael@0 373 // Advance to the Start page.
michael@0 374 this.getStartPageID(function(startPageID) {
michael@0 375 LOG("gUpdates", "onLoad - setting current page to startpage " + startPageID);
michael@0 376 gUpdates.wiz.currentPage = document.getElementById(startPageID);
michael@0 377 });
michael@0 378 },
michael@0 379
michael@0 380 /**
michael@0 381 * Called when the wizard UI is unloaded.
michael@0 382 */
michael@0 383 onUnload: function() {
michael@0 384 if (this._runUnload) {
michael@0 385 var cp = this.wiz.currentPage;
michael@0 386 if (cp.pageid != "finished" && cp.pageid != "finishedBackground")
michael@0 387 this.onWizardCancel();
michael@0 388 }
michael@0 389 },
michael@0 390
michael@0 391 /**
michael@0 392 * Gets the ID of the <wizardpage> object that should be displayed first. This
michael@0 393 * is an asynchronous method that passes the resulting object to a callback
michael@0 394 * function.
michael@0 395 *
michael@0 396 * This is determined by how we were called by the update prompt:
michael@0 397 *
michael@0 398 * Prompt Method: Arg0: Update State: Src Event: Failed: Result:
michael@0 399 * showUpdateAvailable nsIUpdate obj -- background -- see Note below
michael@0 400 * showUpdateDownloaded nsIUpdate obj pending background -- finishedBackground
michael@0 401 * showUpdateInstalled "installed" -- -- -- installed
michael@0 402 * showUpdateError nsIUpdate obj failed either partial errorpatching
michael@0 403 * showUpdateError nsIUpdate obj failed either complete errors
michael@0 404 * checkForUpdates null -- foreground -- checking
michael@0 405 * checkForUpdates null downloading foreground -- downloading
michael@0 406 *
michael@0 407 * Note: the page returned (e.g. Result) for showUpdateAvaulable is as follows:
michael@0 408 * New enabled incompatible add-ons : incompatibleCheck page
michael@0 409 * No new enabled incompatible add-ons: either updatesfoundbasic or
michael@0 410 * updatesfoundbillboard as determined by
michael@0 411 * updatesFoundPageId
michael@0 412 * @param aCallback
michael@0 413 * A callback to pass the <wizardpage> object to be displayed first to.
michael@0 414 */
michael@0 415 getStartPageID: function(aCallback) {
michael@0 416 if ("arguments" in window && window.arguments[0]) {
michael@0 417 var arg0 = window.arguments[0];
michael@0 418 if (arg0 instanceof CoI.nsIUpdate) {
michael@0 419 // If the first argument is a nsIUpdate object, we are notifying the
michael@0 420 // user that the background checking found an update that requires
michael@0 421 // their permission to install, and it's ready for download.
michael@0 422 this.setUpdate(arg0);
michael@0 423 if (this.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
michael@0 424 this.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE ||
michael@0 425 this.update.errorCode == BACKGROUNDCHECK_MULTIPLE_FAILURES) {
michael@0 426 aCallback("errorextra");
michael@0 427 return;
michael@0 428 }
michael@0 429
michael@0 430 if (this.update.unsupported) {
michael@0 431 aCallback("unsupported");
michael@0 432 return;
michael@0 433 }
michael@0 434
michael@0 435 var p = this.update.selectedPatch;
michael@0 436 if (p) {
michael@0 437 var state = p.state;
michael@0 438 var patchFailed;
michael@0 439 try {
michael@0 440 patchFailed = this.update.getProperty("patchingFailed");
michael@0 441 }
michael@0 442 catch (e) {
michael@0 443 }
michael@0 444 if (patchFailed) {
michael@0 445 if (patchFailed == "partial" && this.update.patchCount == 2) {
michael@0 446 // If the system failed to apply the partial patch, show the
michael@0 447 // screen which best describes this condition, which is triggered
michael@0 448 // by the |STATE_FAILED| state.
michael@0 449 state = STATE_FAILED;
michael@0 450 }
michael@0 451 else {
michael@0 452 // Otherwise, if the complete patch failed, which is far less
michael@0 453 // likely, show the error text held by the update object in the
michael@0 454 // generic errors page, triggered by the |STATE_DOWNLOAD_FAILED|
michael@0 455 // state.
michael@0 456 state = STATE_DOWNLOAD_FAILED;
michael@0 457 }
michael@0 458 }
michael@0 459
michael@0 460 // Now select the best page to start with, given the current state of
michael@0 461 // the Update.
michael@0 462 switch (state) {
michael@0 463 case STATE_PENDING:
michael@0 464 case STATE_PENDING_SVC:
michael@0 465 case STATE_APPLIED:
michael@0 466 case STATE_APPLIED_SVC:
michael@0 467 this.sourceEvent = SRCEVT_BACKGROUND;
michael@0 468 aCallback("finishedBackground");
michael@0 469 return;
michael@0 470 case STATE_DOWNLOADING:
michael@0 471 aCallback("downloading");
michael@0 472 return;
michael@0 473 case STATE_FAILED:
michael@0 474 window.getAttention();
michael@0 475 aCallback("errorpatching");
michael@0 476 return;
michael@0 477 case STATE_DOWNLOAD_FAILED:
michael@0 478 case STATE_APPLYING:
michael@0 479 aCallback("errors");
michael@0 480 return;
michael@0 481 }
michael@0 482 }
michael@0 483 if (this.update.licenseURL)
michael@0 484 this.wiz.getPageById(this.updatesFoundPageId).setAttribute("next", "license");
michael@0 485
michael@0 486 var self = this;
michael@0 487 this.getShouldCheckAddonCompatibility(function(shouldCheck) {
michael@0 488 if (shouldCheck) {
michael@0 489 var incompatCheckPage = document.getElementById("incompatibleCheck");
michael@0 490 incompatCheckPage.setAttribute("next", self.updatesFoundPageId);
michael@0 491 aCallback(incompatCheckPage.id);
michael@0 492 }
michael@0 493 else {
michael@0 494 aCallback(self.updatesFoundPageId);
michael@0 495 }
michael@0 496 });
michael@0 497 return;
michael@0 498 }
michael@0 499 else if (arg0 == "installed") {
michael@0 500 aCallback("installed");
michael@0 501 return;
michael@0 502 }
michael@0 503 }
michael@0 504 else {
michael@0 505 var um = CoC["@mozilla.org/updates/update-manager;1"].
michael@0 506 getService(CoI.nsIUpdateManager);
michael@0 507 if (um.activeUpdate) {
michael@0 508 this.setUpdate(um.activeUpdate);
michael@0 509 aCallback("downloading");
michael@0 510 return;
michael@0 511 }
michael@0 512 }
michael@0 513
michael@0 514 // Provide the ability to test the billboard html
michael@0 515 var billboardTestURL = getPref("getCharPref", PREF_APP_UPDATE_BILLBOARD_TEST_URL, null);
michael@0 516 if (billboardTestURL) {
michael@0 517 var updatesFoundBillboardPage = document.getElementById("updatesfoundbillboard");
michael@0 518 updatesFoundBillboardPage.setAttribute("next", "dummy");
michael@0 519 gUpdatesFoundBillboardPage.onExtra1 = function(){ gUpdates.wiz.cancel(); };
michael@0 520 gUpdatesFoundBillboardPage.onExtra2 = function(){ gUpdates.wiz.cancel(); };
michael@0 521 this.onWizardNext = function() { gUpdates.wiz.cancel(); };
michael@0 522 this.update = { billboardURL : billboardTestURL,
michael@0 523 brandName : this.brandName,
michael@0 524 displayVersion : "Billboard Test 1.0",
michael@0 525 showNeverForVersion : true,
michael@0 526 type : "major" };
michael@0 527 aCallback(updatesFoundBillboardPage.id);
michael@0 528 }
michael@0 529 else {
michael@0 530 aCallback("checking");
michael@0 531 }
michael@0 532 },
michael@0 533
michael@0 534 getShouldCheckAddonCompatibility: function(aCallback) {
michael@0 535 // this early return should never happen
michael@0 536 if (!this.update) {
michael@0 537 aCallback(false);
michael@0 538 return;
michael@0 539 }
michael@0 540
michael@0 541 #ifdef TOR_BROWSER_UPDATE
michael@0 542 var appVersion = TOR_BROWSER_VERSION;
michael@0 543 #else
michael@0 544 var appVersion = Services.appinfo.version;
michael@0 545 #endif
michael@0 546 if (!this.update.appVersion ||
michael@0 547 Services.vc.compare(this.update.appVersion, appVersion) == 0) {
michael@0 548 aCallback(false);
michael@0 549 return;
michael@0 550 }
michael@0 551
michael@0 552 try {
michael@0 553 var hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
michael@0 554 }
michael@0 555 catch (e) { }
michael@0 556
michael@0 557 var self = this;
michael@0 558 AddonManager.getAllAddons(function(addons) {
michael@0 559 #ifdef TOR_BROWSER_UPDATE
michael@0 560 let compatVersion = self.update.platformVersion;
michael@0 561 #else
michael@0 562 let compatVersion = self.update.appVersion;
michael@0 563 #endif
michael@0 564 self.addons = [];
michael@0 565 addons.forEach(function(addon) {
michael@0 566 // Protect against code that overrides the add-ons manager and doesn't
michael@0 567 // implement the isCompatibleWith or the findUpdates method.
michael@0 568 if (!("isCompatibleWith" in addon) || !("findUpdates" in addon)) {
michael@0 569 let errMsg = "Add-on doesn't implement either the isCompatibleWith " +
michael@0 570 "or the findUpdates method!";
michael@0 571 if (addon.id)
michael@0 572 errMsg += " Add-on ID: " + addon.id;
michael@0 573 Components.utils.reportError(errMsg);
michael@0 574 return;
michael@0 575 }
michael@0 576
michael@0 577 // If an add-on isn't appDisabled and isn't userDisabled then it is
michael@0 578 // either active now or the user expects it to be active after the
michael@0 579 // restart. If that is the case and the add-on is not installed by the
michael@0 580 // application and is not compatible with the new application version
michael@0 581 // then the user should be warned that the add-on will become
michael@0 582 // incompatible. If an addon's type equals plugin it is skipped since
michael@0 583 // checking plugins compatibility information isn't supported and
michael@0 584 // getting the scope property of a plugin breaks in some environments
michael@0 585 // (see bug 566787). The hotfix add-on is also ignored as it shouldn't
michael@0 586 // block the user from upgrading.
michael@0 587 try {
michael@0 588 if (addon.type != "plugin" && addon.id != hotfixID &&
michael@0 589 !addon.appDisabled && !addon.userDisabled &&
michael@0 590 addon.scope != AddonManager.SCOPE_APPLICATION &&
michael@0 591 addon.isCompatible &&
michael@0 592 !addon.isCompatibleWith(compatVersion,
michael@0 593 self.update.platformVersion))
michael@0 594 self.addons.push(addon);
michael@0 595 }
michael@0 596 catch (e) {
michael@0 597 Components.utils.reportError(e);
michael@0 598 }
michael@0 599 });
michael@0 600
michael@0 601 aCallback(self.addons.length != 0);
michael@0 602 });
michael@0 603 },
michael@0 604
michael@0 605 /**
michael@0 606 * Returns the string page ID for the appropriate updates found page based
michael@0 607 * on the update's metadata.
michael@0 608 */
michael@0 609 get updatesFoundPageId() {
michael@0 610 if (gUpdatesFoundPageId)
michael@0 611 return gUpdatesFoundPageId;
michael@0 612 return gUpdatesFoundPageId = this.update.billboardURL ? "updatesfoundbillboard"
michael@0 613 : "updatesfoundbasic";
michael@0 614 },
michael@0 615
michael@0 616 /**
michael@0 617 * Sets the Update object for this wizard
michael@0 618 * @param update
michael@0 619 * The update object
michael@0 620 */
michael@0 621 setUpdate: function(update) {
michael@0 622 this.update = update;
michael@0 623 if (this.update)
michael@0 624 this.update.QueryInterface(CoI.nsIWritablePropertyBag);
michael@0 625 }
michael@0 626 }
michael@0 627
michael@0 628 /**
michael@0 629 * The "Checking for Updates" page. Provides feedback on the update checking
michael@0 630 * process.
michael@0 631 */
michael@0 632 var gCheckingPage = {
michael@0 633 /**
michael@0 634 * The nsIUpdateChecker that is currently checking for updates. We hold onto
michael@0 635 * this so we can cancel the update check if the user closes the window.
michael@0 636 */
michael@0 637 _checker: null,
michael@0 638
michael@0 639 /**
michael@0 640 * Initialize
michael@0 641 */
michael@0 642 onPageShow: function() {
michael@0 643 gUpdates.setButtons(null, null, null, false, true);
michael@0 644 gUpdates.wiz.getButton("cancel").focus();
michael@0 645
michael@0 646 // Clear all of the "never" prefs to handle the scenario where the user
michael@0 647 // clicked "never" for an update, selected "Check for Updates...", and
michael@0 648 // then canceled. If we don't clear the "never" prefs future
michael@0 649 // notifications will never happen.
michael@0 650 Services.prefs.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH);
michael@0 651
michael@0 652 // The user will be notified if there is an error so clear the background
michael@0 653 // check error count.
michael@0 654 if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
michael@0 655 Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
michael@0 656
michael@0 657 // The preference will be set back to true if the system is still
michael@0 658 // unsupported.
michael@0 659 if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED))
michael@0 660 Services.prefs.clearUserPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED);
michael@0 661
michael@0 662 this._checker = CoC["@mozilla.org/updates/update-checker;1"].
michael@0 663 createInstance(CoI.nsIUpdateChecker);
michael@0 664 this._checker.checkForUpdates(this.updateListener, true);
michael@0 665 },
michael@0 666
michael@0 667 /**
michael@0 668 * The user has closed the window, either by pressing cancel or using a Window
michael@0 669 * Manager control, so stop checking for updates.
michael@0 670 */
michael@0 671 onWizardCancel: function() {
michael@0 672 this._checker.stopChecking(CoI.nsIUpdateChecker.CURRENT_CHECK);
michael@0 673 },
michael@0 674
michael@0 675 /**
michael@0 676 * An object implementing nsIUpdateCheckListener that is notified as the
michael@0 677 * update check commences.
michael@0 678 */
michael@0 679 updateListener: {
michael@0 680 /**
michael@0 681 * See nsIUpdateCheckListener
michael@0 682 */
michael@0 683 onCheckComplete: function(request, updates, updateCount) {
michael@0 684 var aus = CoC["@mozilla.org/updates/update-service;1"].
michael@0 685 getService(CoI.nsIApplicationUpdateService);
michael@0 686 gUpdates.setUpdate(aus.selectUpdate(updates, updates.length));
michael@0 687 if (gUpdates.update) {
michael@0 688 LOG("gCheckingPage", "onCheckComplete - update found");
michael@0 689 if (gUpdates.update.unsupported) {
michael@0 690 gUpdates.wiz.goTo("unsupported");
michael@0 691 return;
michael@0 692 }
michael@0 693
michael@0 694 if (!aus.canApplyUpdates) {
michael@0 695 // Prevent multiple notifications for the same update when the user is
michael@0 696 // unable to apply updates.
michael@0 697 gUpdates.never();
michael@0 698 gUpdates.wiz.goTo("manualUpdate");
michael@0 699 return;
michael@0 700 }
michael@0 701
michael@0 702 if (gUpdates.update.licenseURL) {
michael@0 703 // gUpdates.updatesFoundPageId returns the pageid and not the
michael@0 704 // element's id so use the wizard's getPageById method.
michael@0 705 gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "license");
michael@0 706 }
michael@0 707
michael@0 708 gUpdates.getShouldCheckAddonCompatibility(function(shouldCheck) {
michael@0 709 if (shouldCheck) {
michael@0 710 var incompatCheckPage = document.getElementById("incompatibleCheck");
michael@0 711 incompatCheckPage.setAttribute("next", gUpdates.updatesFoundPageId);
michael@0 712 gUpdates.wiz.goTo("incompatibleCheck");
michael@0 713 }
michael@0 714 else {
michael@0 715 gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
michael@0 716 }
michael@0 717 });
michael@0 718 return;
michael@0 719 }
michael@0 720
michael@0 721 LOG("gCheckingPage", "onCheckComplete - no update found");
michael@0 722 gUpdates.wiz.goTo("noupdatesfound");
michael@0 723 },
michael@0 724
michael@0 725 /**
michael@0 726 * See nsIUpdateCheckListener
michael@0 727 */
michael@0 728 onError: function(request, update) {
michael@0 729 LOG("gCheckingPage", "onError - proceeding to error page");
michael@0 730 gUpdates.setUpdate(update);
michael@0 731 if (update.errorCode &&
michael@0 732 (update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
michael@0 733 update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE)) {
michael@0 734 gUpdates.wiz.goTo("errorextra");
michael@0 735 }
michael@0 736 else {
michael@0 737 gUpdates.wiz.goTo("errors");
michael@0 738 }
michael@0 739 },
michael@0 740
michael@0 741 /**
michael@0 742 * See nsISupports.idl
michael@0 743 */
michael@0 744 QueryInterface: function(aIID) {
michael@0 745 if (!aIID.equals(CoI.nsIUpdateCheckListener) &&
michael@0 746 !aIID.equals(CoI.nsISupports))
michael@0 747 throw CoR.NS_ERROR_NO_INTERFACE;
michael@0 748 return this;
michael@0 749 }
michael@0 750 }
michael@0 751 };
michael@0 752
michael@0 753 /**
michael@0 754 * The "You have outdated plugins" page
michael@0 755 */
michael@0 756 var gPluginsPage = {
michael@0 757 /**
michael@0 758 * URL of the plugin updates page
michael@0 759 */
michael@0 760 _url: null,
michael@0 761
michael@0 762 /**
michael@0 763 * Initialize
michael@0 764 */
michael@0 765 onPageShow: function() {
michael@0 766 var prefs = Services.prefs;
michael@0 767 if (prefs.getPrefType(PREF_PLUGINS_UPDATEURL) == prefs.PREF_INVALID) {
michael@0 768 gUpdates.wiz.goTo("noupdatesfound");
michael@0 769 return;
michael@0 770 }
michael@0 771
michael@0 772 this._url = Services.urlFormatter.formatURLPref(PREF_PLUGINS_UPDATEURL);
michael@0 773 var link = document.getElementById("pluginupdateslink");
michael@0 774 link.setAttribute("href", this._url);
michael@0 775
michael@0 776
michael@0 777 var phs = CoC["@mozilla.org/plugin/host;1"].
michael@0 778 getService(CoI.nsIPluginHost);
michael@0 779 var plugins = phs.getPluginTags();
michael@0 780 var blocklist = CoC["@mozilla.org/extensions/blocklist;1"].
michael@0 781 getService(CoI.nsIBlocklistService);
michael@0 782
michael@0 783 var hasOutdated = false;
michael@0 784 for (let i = 0; i < plugins.length; i++) {
michael@0 785 let pluginState = blocklist.getPluginBlocklistState(plugins[i]);
michael@0 786 if (pluginState == CoI.nsIBlocklistService.STATE_OUTDATED) {
michael@0 787 hasOutdated = true;
michael@0 788 break;
michael@0 789 }
michael@0 790 }
michael@0 791 if (!hasOutdated) {
michael@0 792 gUpdates.wiz.goTo("noupdatesfound");
michael@0 793 return;
michael@0 794 }
michael@0 795
michael@0 796 gUpdates.setButtons(null, null, "okButton", true);
michael@0 797 gUpdates.wiz.getButton("finish").focus();
michael@0 798 },
michael@0 799
michael@0 800 /**
michael@0 801 * Finish button clicked.
michael@0 802 */
michael@0 803 onWizardFinish: function() {
michael@0 804 openURL(this._url);
michael@0 805 }
michael@0 806 };
michael@0 807
michael@0 808 /**
michael@0 809 * The "No Updates Are Available" page
michael@0 810 */
michael@0 811 var gNoUpdatesPage = {
michael@0 812 /**
michael@0 813 * Initialize
michael@0 814 */
michael@0 815 onPageShow: function() {
michael@0 816 LOG("gNoUpdatesPage", "onPageShow - could not select an appropriate " +
michael@0 817 "update. Either there were no updates or |selectUpdate| failed");
michael@0 818
michael@0 819 if (getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true))
michael@0 820 document.getElementById("noUpdatesAutoEnabled").hidden = false;
michael@0 821 else
michael@0 822 document.getElementById("noUpdatesAutoDisabled").hidden = false;
michael@0 823
michael@0 824 gUpdates.setButtons(null, null, "okButton", true);
michael@0 825 gUpdates.wiz.getButton("finish").focus();
michael@0 826 }
michael@0 827 };
michael@0 828
michael@0 829
michael@0 830 /**
michael@0 831 * The page that checks if there are any incompatible add-ons.
michael@0 832 */
michael@0 833 var gIncompatibleCheckPage = {
michael@0 834 /**
michael@0 835 * Count of incompatible add-ons to check for updates
michael@0 836 */
michael@0 837 _totalCount: 0,
michael@0 838
michael@0 839 /**
michael@0 840 * Count of incompatible add-ons that have beend checked for updates
michael@0 841 */
michael@0 842 _completedCount: 0,
michael@0 843
michael@0 844 /**
michael@0 845 * The progress bar for this page
michael@0 846 */
michael@0 847 _pBar: null,
michael@0 848
michael@0 849 /**
michael@0 850 * Initialize
michael@0 851 */
michael@0 852 onPageShow: function() {
michael@0 853 LOG("gIncompatibleCheckPage", "onPageShow - checking for updates to " +
michael@0 854 "incompatible add-ons");
michael@0 855
michael@0 856 gUpdates.setButtons(null, null, null, false, true);
michael@0 857 gUpdates.wiz.getButton("cancel").focus();
michael@0 858 this._pBar = document.getElementById("incompatibleCheckProgress");
michael@0 859 this._totalCount = gUpdates.addons.length;
michael@0 860
michael@0 861 this._pBar.mode = "normal";
michael@0 862 #ifdef TOR_BROWSER_UPDATE
michael@0 863 let compatVersion = gUpdates.update.platformVersion;
michael@0 864 #else
michael@0 865 let compatVersion = gUpdates.update.appVersion;
michael@0 866 #endif
michael@0 867 gUpdates.addons.forEach(function(addon) {
michael@0 868 addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
michael@0 869 compatVersion,
michael@0 870 gUpdates.update.platformVersion);
michael@0 871 }, this);
michael@0 872 },
michael@0 873
michael@0 874 // Addon UpdateListener
michael@0 875 onCompatibilityUpdateAvailable: function(addon) {
michael@0 876 // Remove the add-on from the list of add-ons that will become incompatible
michael@0 877 // with the new version of the application.
michael@0 878 for (var i = 0; i < gUpdates.addons.length; ++i) {
michael@0 879 if (gUpdates.addons[i].id == addon.id) {
michael@0 880 LOG("gIncompatibleCheckPage", "onCompatibilityUpdateAvailable - " +
michael@0 881 "found update for add-on ID: " + addon.id);
michael@0 882 gUpdates.addons.splice(i, 1);
michael@0 883 break;
michael@0 884 }
michael@0 885 }
michael@0 886 },
michael@0 887
michael@0 888 onUpdateAvailable: function(addon, install) {
michael@0 889 // If the new version of this add-on is blocklisted for the new application
michael@0 890 // then it isn't a valid update and the user should still be warned that
michael@0 891 // the add-on will become incompatible.
michael@0 892 let bs = CoC["@mozilla.org/extensions/blocklist;1"].
michael@0 893 getService(CoI.nsIBlocklistService);
michael@0 894 #ifdef TOR_BROWSER_UPDATE
michael@0 895 let compatVersion = gUpdates.update.platformVersion;
michael@0 896 #else
michael@0 897 let compatVersion = gUpdates.update.appVersion;
michael@0 898 #endif
michael@0 899 if (bs.isAddonBlocklisted(addon,
michael@0 900 compatVersion,
michael@0 901 gUpdates.update.platformVersion))
michael@0 902 return;
michael@0 903
michael@0 904 // Compatibility or new version updates mean the same thing here.
michael@0 905 this.onCompatibilityUpdateAvailable(addon);
michael@0 906 },
michael@0 907
michael@0 908 onUpdateFinished: function(addon) {
michael@0 909 ++this._completedCount;
michael@0 910 this._pBar.value = Math.ceil((this._completedCount / this._totalCount) * 100);
michael@0 911
michael@0 912 if (this._completedCount < this._totalCount)
michael@0 913 return;
michael@0 914
michael@0 915 if (gUpdates.addons.length == 0) {
michael@0 916 LOG("gIncompatibleCheckPage", "onUpdateFinished - updates were found " +
michael@0 917 "for all incompatible add-ons");
michael@0 918 }
michael@0 919 else {
michael@0 920 LOG("gIncompatibleCheckPage", "onUpdateFinished - there are still " +
michael@0 921 "incompatible add-ons");
michael@0 922 if (gUpdates.update.licenseURL) {
michael@0 923 document.getElementById("license").setAttribute("next", "incompatibleList");
michael@0 924 }
michael@0 925 else {
michael@0 926 // gUpdates.updatesFoundPageId returns the pageid and not the element's
michael@0 927 // id so use the wizard's getPageById method.
michael@0 928 gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "incompatibleList");
michael@0 929 }
michael@0 930 }
michael@0 931 gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
michael@0 932 }
michael@0 933 };
michael@0 934
michael@0 935 /**
michael@0 936 * The "Unable to Update" page. Provides the user information about why they
michael@0 937 * were unable to update and a manual download url.
michael@0 938 */
michael@0 939 var gManualUpdatePage = {
michael@0 940 onPageShow: function() {
michael@0 941 var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
michael@0 942 var manualUpdateLinkLabel = document.getElementById("manualUpdateLinkLabel");
michael@0 943 manualUpdateLinkLabel.value = manualURL;
michael@0 944 manualUpdateLinkLabel.setAttribute("url", manualURL);
michael@0 945
michael@0 946 gUpdates.setButtons(null, null, "okButton", true);
michael@0 947 gUpdates.wiz.getButton("finish").focus();
michael@0 948 }
michael@0 949 };
michael@0 950
michael@0 951 /**
michael@0 952 * The "System Unsupported" page. Provides the user with information about their
michael@0 953 * system no longer being supported and an url for more information.
michael@0 954 */
michael@0 955 var gUnsupportedPage = {
michael@0 956 onPageShow: function() {
michael@0 957 Services.prefs.setBoolPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED, true);
michael@0 958 if (gUpdates.update.detailsURL) {
michael@0 959 let unsupportedLinkLabel = document.getElementById("unsupportedLinkLabel");
michael@0 960 unsupportedLinkLabel.setAttribute("url", gUpdates.update.detailsURL);
michael@0 961 }
michael@0 962
michael@0 963 gUpdates.setButtons(null, null, "okButton", true);
michael@0 964 gUpdates.wiz.getButton("finish").focus();
michael@0 965 }
michael@0 966 };
michael@0 967
michael@0 968 /**
michael@0 969 * The "Updates Are Available" page. Provides the user information about the
michael@0 970 * available update.
michael@0 971 */
michael@0 972 var gUpdatesFoundBasicPage = {
michael@0 973 /**
michael@0 974 * Initialize
michael@0 975 */
michael@0 976 onPageShow: function() {
michael@0 977 gUpdates.wiz.canRewind = false;
michael@0 978 var update = gUpdates.update;
michael@0 979 gUpdates.setButtons("askLaterButton",
michael@0 980 update.showNeverForVersion ? "noThanksButton" : null,
michael@0 981 "updateButton_" + update.type, true);
michael@0 982 var btn = gUpdates.wiz.getButton("next");
michael@0 983 btn.focus();
michael@0 984
michael@0 985 var updateName = update.name;
michael@0 986 if (update.channel == "nightly") {
michael@0 987 updateName = gUpdates.getAUSString("updateNightlyName",
michael@0 988 [gUpdates.brandName,
michael@0 989 update.displayVersion,
michael@0 990 update.buildID]);
michael@0 991 }
michael@0 992 var updateNameElement = document.getElementById("updateName");
michael@0 993 updateNameElement.value = updateName;
michael@0 994
michael@0 995 var introText = gUpdates.getAUSString("intro_" + update.type,
michael@0 996 [gUpdates.brandName, update.displayVersion]);
michael@0 997 var introElem = document.getElementById("updatesFoundInto");
michael@0 998 introElem.setAttribute("severity", update.type);
michael@0 999 introElem.textContent = introText;
michael@0 1000
michael@0 1001 var updateMoreInfoURL = document.getElementById("updateMoreInfoURL");
michael@0 1002 if (update.detailsURL)
michael@0 1003 updateMoreInfoURL.setAttribute("url", update.detailsURL);
michael@0 1004 else
michael@0 1005 updateMoreInfoURL.hidden = true;
michael@0 1006
michael@0 1007 var updateTitle = gUpdates.getAUSString("updatesfound_" + update.type +
michael@0 1008 ".title");
michael@0 1009 document.getElementById("updatesFoundBasicHeader").setAttribute("label", updateTitle);
michael@0 1010 },
michael@0 1011
michael@0 1012 onExtra1: function() {
michael@0 1013 gUpdates.wiz.cancel();
michael@0 1014 },
michael@0 1015
michael@0 1016 onExtra2: function() {
michael@0 1017 gUpdates.never();
michael@0 1018 gUpdates.wiz.cancel();
michael@0 1019 }
michael@0 1020 };
michael@0 1021
michael@0 1022 /**
michael@0 1023 * The "Updates Are Available" page with a billboard. Provides the user
michael@0 1024 * information about the available update.
michael@0 1025 */
michael@0 1026 var gUpdatesFoundBillboardPage = {
michael@0 1027 /**
michael@0 1028 * If this page has been previously loaded
michael@0 1029 */
michael@0 1030 _billboardLoaded: false,
michael@0 1031
michael@0 1032 /**
michael@0 1033 * Initialize
michael@0 1034 */
michael@0 1035 onPageShow: function() {
michael@0 1036 var update = gUpdates.update;
michael@0 1037 gUpdates.setButtons("askLaterButton",
michael@0 1038 update.showNeverForVersion ? "noThanksButton" : null,
michael@0 1039 "updateButton_" + update.type, true);
michael@0 1040 gUpdates.wiz.getButton("next").focus();
michael@0 1041
michael@0 1042 if (this._billboardLoaded)
michael@0 1043 return;
michael@0 1044
michael@0 1045 var remoteContent = document.getElementById("updateMoreInfoContent");
michael@0 1046 remoteContent.addEventListener("load",
michael@0 1047 gUpdatesFoundBillboardPage.onBillboardLoad,
michael@0 1048 false);
michael@0 1049 // update_name and update_version need to be set before url
michael@0 1050 // so that when attempting to download the url, we can show
michael@0 1051 // the formatted "Download..." string
michael@0 1052 remoteContent.update_name = gUpdates.brandName;
michael@0 1053 remoteContent.update_version = update.displayVersion;
michael@0 1054
michael@0 1055 var billboardTestURL = getPref("getCharPref", PREF_APP_UPDATE_BILLBOARD_TEST_URL, null);
michael@0 1056 if (billboardTestURL) {
michael@0 1057 // Allow file urls when testing the billboard and fallback to the
michael@0 1058 // normal method if the URL isn't a file.
michael@0 1059 var scheme = Services.io.newURI(billboardTestURL, null, null).scheme;
michael@0 1060 if (scheme == "file")
michael@0 1061 remoteContent.testFileUrl = update.billboardURL;
michael@0 1062 else
michael@0 1063 remoteContent.url = update.billboardURL;
michael@0 1064 }
michael@0 1065 else
michael@0 1066 remoteContent.url = update.billboardURL;
michael@0 1067
michael@0 1068 this._billboardLoaded = true;
michael@0 1069 },
michael@0 1070
michael@0 1071 /**
michael@0 1072 * When the billboard document has loaded
michael@0 1073 */
michael@0 1074 onBillboardLoad: function(aEvent) {
michael@0 1075 var remoteContent = document.getElementById("updateMoreInfoContent");
michael@0 1076 // Note: may be called multiple times due to multiple onLoad events.
michael@0 1077 var state = remoteContent.getAttribute("state");
michael@0 1078 if (state == "loading" || aEvent.originalTarget != remoteContent)
michael@0 1079 return;
michael@0 1080
michael@0 1081 remoteContent.removeEventListener("load", gUpdatesFoundBillboardPage.onBillboardLoad, false);
michael@0 1082 if (state == "error") {
michael@0 1083 gUpdatesFoundPageId = "updatesfoundbasic";
michael@0 1084 var next = gUpdates.wiz.getPageById("updatesfoundbillboard").getAttribute("next");
michael@0 1085 gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", next);
michael@0 1086 gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
michael@0 1087 }
michael@0 1088 },
michael@0 1089
michael@0 1090 onExtra1: function() {
michael@0 1091 this.onWizardCancel();
michael@0 1092 gUpdates.wiz.cancel();
michael@0 1093 },
michael@0 1094
michael@0 1095 onExtra2: function() {
michael@0 1096 this.onWizardCancel();
michael@0 1097 gUpdates.never();
michael@0 1098 gUpdates.wiz.cancel();
michael@0 1099 },
michael@0 1100
michael@0 1101 /**
michael@0 1102 * When the user cancels the wizard
michael@0 1103 */
michael@0 1104 onWizardCancel: function() {
michael@0 1105 try {
michael@0 1106 var remoteContent = document.getElementById("updateMoreInfoContent");
michael@0 1107 if (remoteContent)
michael@0 1108 remoteContent.stopDownloading();
michael@0 1109 }
michael@0 1110 catch (e) {
michael@0 1111 LOG("gUpdatesFoundBillboardPage", "onWizardCancel - " +
michael@0 1112 "moreInfoContent.stopDownloading() failed: " + e);
michael@0 1113 }
michael@0 1114 }
michael@0 1115 };
michael@0 1116
michael@0 1117 /**
michael@0 1118 * The page which shows the user a license associated with an update. The
michael@0 1119 * user must agree to the terms of the license before continuing to install
michael@0 1120 * the update.
michael@0 1121 */
michael@0 1122 var gLicensePage = {
michael@0 1123 /**
michael@0 1124 * If the license url has been previously loaded
michael@0 1125 */
michael@0 1126 _licenseLoaded: false,
michael@0 1127
michael@0 1128 /**
michael@0 1129 * Initialize
michael@0 1130 */
michael@0 1131 onPageShow: function() {
michael@0 1132 gUpdates.setButtons("backButton", null, "acceptTermsButton", false);
michael@0 1133
michael@0 1134 var licenseContent = document.getElementById("licenseContent");
michael@0 1135 if (this._licenseLoaded || licenseContent.getAttribute("state") == "error") {
michael@0 1136 this.onAcceptDeclineRadio();
michael@0 1137 var licenseGroup = document.getElementById("acceptDeclineLicense");
michael@0 1138 licenseGroup.focus();
michael@0 1139 return;
michael@0 1140 }
michael@0 1141
michael@0 1142 gUpdates.wiz.canAdvance = false;
michael@0 1143
michael@0 1144 // Disable the license radiogroup until the EULA has been downloaded
michael@0 1145 document.getElementById("acceptDeclineLicense").disabled = true;
michael@0 1146 gUpdates.update.setProperty("licenseAccepted", "false");
michael@0 1147
michael@0 1148 licenseContent.addEventListener("load", gLicensePage.onLicenseLoad, false);
michael@0 1149 // The update_name and update_version need to be set before url so the ui
michael@0 1150 // can display the formatted "Download..." string when attempting to
michael@0 1151 // download the url.
michael@0 1152 licenseContent.update_name = gUpdates.brandName;
michael@0 1153 licenseContent.update_version = gUpdates.update.displayVersion;
michael@0 1154 licenseContent.url = gUpdates.update.licenseURL;
michael@0 1155 },
michael@0 1156
michael@0 1157 /**
michael@0 1158 * When the license document has loaded
michael@0 1159 */
michael@0 1160 onLicenseLoad: function(aEvent) {
michael@0 1161 var licenseContent = document.getElementById("licenseContent");
michael@0 1162 // Disable or enable the radiogroup based on the state attribute of
michael@0 1163 // licenseContent.
michael@0 1164 // Note: may be called multiple times due to multiple onLoad events.
michael@0 1165 var state = licenseContent.getAttribute("state");
michael@0 1166 if (state == "loading" || aEvent.originalTarget != licenseContent)
michael@0 1167 return;
michael@0 1168
michael@0 1169 licenseContent.removeEventListener("load", gLicensePage.onLicenseLoad, false);
michael@0 1170
michael@0 1171 if (state == "error") {
michael@0 1172 gUpdates.wiz.goTo("manualUpdate");
michael@0 1173 return;
michael@0 1174 }
michael@0 1175
michael@0 1176 gLicensePage._licenseLoaded = true;
michael@0 1177 document.getElementById("acceptDeclineLicense").disabled = false;
michael@0 1178 gUpdates.wiz.getButton("extra1").disabled = false;
michael@0 1179 },
michael@0 1180
michael@0 1181 /**
michael@0 1182 * When the user changes the state of the accept / decline radio group
michael@0 1183 */
michael@0 1184 onAcceptDeclineRadio: function() {
michael@0 1185 // Return early if this page hasn't been loaded (bug 405257). This event is
michael@0 1186 // fired during the construction of the wizard before gUpdates has received
michael@0 1187 // its onload event (bug 452389).
michael@0 1188 if (!this._licenseLoaded)
michael@0 1189 return;
michael@0 1190
michael@0 1191 var selectedIndex = document.getElementById("acceptDeclineLicense")
michael@0 1192 .selectedIndex;
michael@0 1193 // 0 == Accept, 1 == Decline
michael@0 1194 var licenseAccepted = (selectedIndex == 0);
michael@0 1195 gUpdates.wiz.canAdvance = licenseAccepted;
michael@0 1196 },
michael@0 1197
michael@0 1198 /**
michael@0 1199 * The non-standard "Back" button.
michael@0 1200 */
michael@0 1201 onExtra1: function() {
michael@0 1202 gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
michael@0 1203 },
michael@0 1204
michael@0 1205 /**
michael@0 1206 * When the user clicks next after accepting the license
michael@0 1207 */
michael@0 1208 onWizardNext: function() {
michael@0 1209 try {
michael@0 1210 gUpdates.update.setProperty("licenseAccepted", "true");
michael@0 1211 var um = CoC["@mozilla.org/updates/update-manager;1"].
michael@0 1212 getService(CoI.nsIUpdateManager);
michael@0 1213 um.saveUpdates();
michael@0 1214 }
michael@0 1215 catch (e) {
michael@0 1216 LOG("gLicensePage", "onWizardNext - nsIUpdateManager:saveUpdates() " +
michael@0 1217 "failed: " + e);
michael@0 1218 }
michael@0 1219 },
michael@0 1220
michael@0 1221 /**
michael@0 1222 * When the user cancels the wizard
michael@0 1223 */
michael@0 1224 onWizardCancel: function() {
michael@0 1225 try {
michael@0 1226 var licenseContent = document.getElementById("licenseContent");
michael@0 1227 // If the license was downloading, stop it.
michael@0 1228 if (licenseContent)
michael@0 1229 licenseContent.stopDownloading();
michael@0 1230 }
michael@0 1231 catch (e) {
michael@0 1232 LOG("gLicensePage", "onWizardCancel - " +
michael@0 1233 "licenseContent.stopDownloading() failed: " + e);
michael@0 1234 }
michael@0 1235 }
michael@0 1236 };
michael@0 1237
michael@0 1238 /**
michael@0 1239 * The page which shows add-ons that are incompatible and do not have updated
michael@0 1240 * compatibility information or a version update available to make them
michael@0 1241 * compatible.
michael@0 1242 */
michael@0 1243 var gIncompatibleListPage = {
michael@0 1244 /**
michael@0 1245 * Initialize
michael@0 1246 */
michael@0 1247 onPageShow: function() {
michael@0 1248 gUpdates.setButtons("backButton", null, "okButton", true);
michael@0 1249 var listbox = document.getElementById("incompatibleListbox");
michael@0 1250 if (listbox.children.length > 0)
michael@0 1251 return;
michael@0 1252
michael@0 1253 var intro = gUpdates.getAUSString("incompatAddons_" + gUpdates.update.type,
michael@0 1254 [gUpdates.brandName,
michael@0 1255 gUpdates.update.displayVersion]);
michael@0 1256 document.getElementById("incompatibleListDesc").textContent = intro;
michael@0 1257
michael@0 1258 var addons = gUpdates.addons;
michael@0 1259 for (var i = 0; i < addons.length; ++i) {
michael@0 1260 var listitem = document.createElement("listitem");
michael@0 1261 var addonLabel = gUpdates.getAUSString("addonLabel", [addons[i].name,
michael@0 1262 addons[i].version]);
michael@0 1263 listitem.setAttribute("label", addonLabel);
michael@0 1264 listbox.appendChild(listitem);
michael@0 1265 }
michael@0 1266 },
michael@0 1267
michael@0 1268 /**
michael@0 1269 * The non-standard "Back" button.
michael@0 1270 */
michael@0 1271 onExtra1: function() {
michael@0 1272 gUpdates.wiz.goTo(gUpdates.update.licenseURL ? "license"
michael@0 1273 : gUpdates.updatesFoundPageId);
michael@0 1274 }
michael@0 1275 };
michael@0 1276
michael@0 1277 /**
michael@0 1278 * The "Update is Downloading" page - provides feedback for the download
michael@0 1279 * process plus a pause/resume UI
michael@0 1280 */
michael@0 1281 var gDownloadingPage = {
michael@0 1282 /**
michael@0 1283 * DOM Elements
michael@0 1284 */
michael@0 1285 _downloadStatus: null,
michael@0 1286 _downloadProgress: null,
michael@0 1287 _pauseButton: null,
michael@0 1288
michael@0 1289 /**
michael@0 1290 * Whether or not we are currently paused
michael@0 1291 */
michael@0 1292 _paused: false,
michael@0 1293
michael@0 1294 /**
michael@0 1295 * Label cache to hold the 'Connecting' string
michael@0 1296 */
michael@0 1297 _label_downloadStatus: null,
michael@0 1298
michael@0 1299 /**
michael@0 1300 * Member variables for updating download status
michael@0 1301 */
michael@0 1302 _lastSec: Infinity,
michael@0 1303 _startTime: null,
michael@0 1304 _pausedStatus: "",
michael@0 1305
michael@0 1306 _hiding: false,
michael@0 1307
michael@0 1308 /**
michael@0 1309 * Have we registered an observer for a background update being staged
michael@0 1310 */
michael@0 1311 _updateApplyingObserver: false,
michael@0 1312
michael@0 1313 /**
michael@0 1314 * Initialize
michael@0 1315 */
michael@0 1316 onPageShow: function() {
michael@0 1317 this._downloadStatus = document.getElementById("downloadStatus");
michael@0 1318 this._downloadProgress = document.getElementById("downloadProgress");
michael@0 1319 this._pauseButton = document.getElementById("pauseButton");
michael@0 1320 this._label_downloadStatus = this._downloadStatus.textContent;
michael@0 1321
michael@0 1322 this._pauseButton.setAttribute("tooltiptext",
michael@0 1323 gUpdates.getAUSString("pauseButtonPause"));
michael@0 1324
michael@0 1325 // move focus to the pause/resume button and then disable it (bug #353177)
michael@0 1326 this._pauseButton.focus();
michael@0 1327 this._pauseButton.disabled = true;
michael@0 1328
michael@0 1329 var aus = CoC["@mozilla.org/updates/update-service;1"].
michael@0 1330 getService(CoI.nsIApplicationUpdateService);
michael@0 1331
michael@0 1332 var um = CoC["@mozilla.org/updates/update-manager;1"].
michael@0 1333 getService(CoI.nsIUpdateManager);
michael@0 1334 var activeUpdate = um.activeUpdate;
michael@0 1335 if (activeUpdate)
michael@0 1336 gUpdates.setUpdate(activeUpdate);
michael@0 1337
michael@0 1338 if (!gUpdates.update) {
michael@0 1339 LOG("gDownloadingPage", "onPageShow - no valid update to download?!");
michael@0 1340 return;
michael@0 1341 }
michael@0 1342
michael@0 1343 this._startTime = Date.now();
michael@0 1344
michael@0 1345 try {
michael@0 1346 // Say that this was a foreground download, not a background download,
michael@0 1347 // since the user cared enough to look in on this process.
michael@0 1348 gUpdates.update.QueryInterface(CoI.nsIWritablePropertyBag);
michael@0 1349 gUpdates.update.setProperty("foregroundDownload", "true");
michael@0 1350
michael@0 1351 // Pause any active background download and restart it as a foreground
michael@0 1352 // download.
michael@0 1353 aus.pauseDownload();
michael@0 1354 var state = aus.downloadUpdate(gUpdates.update, false);
michael@0 1355 if (state == "failed") {
michael@0 1356 // We've tried as hard as we could to download a valid update -
michael@0 1357 // we fell back from a partial patch to a complete patch and even
michael@0 1358 // then we couldn't validate. Show a validation error with instructions
michael@0 1359 // on how to manually update.
michael@0 1360 this.cleanUp();
michael@0 1361 gUpdates.wiz.goTo("errors");
michael@0 1362 return;
michael@0 1363 }
michael@0 1364 else {
michael@0 1365 // Add this UI as a listener for active downloads
michael@0 1366 aus.addDownloadListener(this);
michael@0 1367 }
michael@0 1368
michael@0 1369 if (activeUpdate)
michael@0 1370 this._setUIState(!aus.isDownloading);
michael@0 1371 }
michael@0 1372 catch(e) {
michael@0 1373 LOG("gDownloadingPage", "onPageShow - error: " + e);
michael@0 1374 }
michael@0 1375
michael@0 1376 gUpdates.setButtons("hideButton", null, null, false);
michael@0 1377 gUpdates.wiz.getButton("extra1").focus();
michael@0 1378 },
michael@0 1379
michael@0 1380 /**
michael@0 1381 * Updates the text status message
michael@0 1382 */
michael@0 1383 _setStatus: function(status) {
michael@0 1384 // Don't bother setting the same text more than once. This can happen
michael@0 1385 // due to the asynchronous behavior of the downloader.
michael@0 1386 if (this._downloadStatus.textContent == status)
michael@0 1387 return;
michael@0 1388 while (this._downloadStatus.hasChildNodes())
michael@0 1389 this._downloadStatus.removeChild(this._downloadStatus.firstChild);
michael@0 1390 this._downloadStatus.appendChild(document.createTextNode(status));
michael@0 1391 },
michael@0 1392
michael@0 1393 /**
michael@0 1394 * Update download progress status to show time left, speed, and progress.
michael@0 1395 * Also updates the status needed for pausing the download.
michael@0 1396 *
michael@0 1397 * @param aCurr
michael@0 1398 * Current number of bytes transferred
michael@0 1399 * @param aMax
michael@0 1400 * Total file size of the download
michael@0 1401 * @return Current active download status
michael@0 1402 */
michael@0 1403 _updateDownloadStatus: function(aCurr, aMax) {
michael@0 1404 let status;
michael@0 1405
michael@0 1406 // Get the download time left and progress
michael@0 1407 let rate = aCurr / (Date.now() - this._startTime) * 1000;
michael@0 1408 [status, this._lastSec] =
michael@0 1409 DownloadUtils.getDownloadStatus(aCurr, aMax, rate, this._lastSec);
michael@0 1410
michael@0 1411 // Get the download progress for pausing
michael@0 1412 this._pausedStatus = DownloadUtils.getTransferTotal(aCurr, aMax);
michael@0 1413
michael@0 1414 return status;
michael@0 1415 },
michael@0 1416
michael@0 1417 /**
michael@0 1418 * Adjust UI to suit a certain state of paused-ness
michael@0 1419 * @param paused
michael@0 1420 * Whether or not the download is paused
michael@0 1421 */
michael@0 1422 _setUIState: function(paused) {
michael@0 1423 var u = gUpdates.update;
michael@0 1424 if (paused) {
michael@0 1425 if (this._downloadProgress.mode != "normal")
michael@0 1426 this._downloadProgress.mode = "normal";
michael@0 1427 this._pauseButton.setAttribute("tooltiptext",
michael@0 1428 gUpdates.getAUSString("pauseButtonResume"));
michael@0 1429 this._pauseButton.setAttribute("paused", "true");
michael@0 1430 var p = u.selectedPatch.QueryInterface(CoI.nsIPropertyBag);
michael@0 1431 var status = p.getProperty("status");
michael@0 1432 if (status) {
michael@0 1433 let pausedStatus = gUpdates.getAUSString("downloadPausedStatus", [status]);
michael@0 1434 this._setStatus(pausedStatus);
michael@0 1435 }
michael@0 1436 }
michael@0 1437 else {
michael@0 1438 if (this._downloadProgress.mode != "undetermined")
michael@0 1439 this._downloadProgress.mode = "undetermined";
michael@0 1440 this._pauseButton.setAttribute("paused", "false");
michael@0 1441 this._pauseButton.setAttribute("tooltiptext",
michael@0 1442 gUpdates.getAUSString("pauseButtonPause"));
michael@0 1443 this._setStatus(this._label_downloadStatus);
michael@0 1444 }
michael@0 1445 },
michael@0 1446
michael@0 1447 /**
michael@0 1448 * Wait for an update being staged in the background.
michael@0 1449 */
michael@0 1450 _setUpdateApplying: function() {
michael@0 1451 this._downloadProgress.mode = "undetermined";
michael@0 1452 this._pauseButton.hidden = true;
michael@0 1453 let applyingStatus = gUpdates.getAUSString("applyingUpdate");
michael@0 1454 this._setStatus(applyingStatus);
michael@0 1455
michael@0 1456 Services.obs.addObserver(this, "update-staged", false);
michael@0 1457 this._updateApplyingObserver = true;
michael@0 1458 },
michael@0 1459
michael@0 1460 /**
michael@0 1461 * Clean up the listener and observer registered for the wizard.
michael@0 1462 */
michael@0 1463 cleanUp: function() {
michael@0 1464 var aus = CoC["@mozilla.org/updates/update-service;1"].
michael@0 1465 getService(CoI.nsIApplicationUpdateService);
michael@0 1466 aus.removeDownloadListener(this);
michael@0 1467
michael@0 1468 if (this._updateApplyingObserver) {
michael@0 1469 Services.obs.removeObserver(this, "update-staged");
michael@0 1470 this._updateApplyingObserver = false;
michael@0 1471 }
michael@0 1472 },
michael@0 1473
michael@0 1474 /**
michael@0 1475 * When the user clicks the Pause/Resume button
michael@0 1476 */
michael@0 1477 onPause: function() {
michael@0 1478 var aus = CoC["@mozilla.org/updates/update-service;1"].
michael@0 1479 getService(CoI.nsIApplicationUpdateService);
michael@0 1480 if (this._paused)
michael@0 1481 aus.downloadUpdate(gUpdates.update, false);
michael@0 1482 else {
michael@0 1483 var patch = gUpdates.update.selectedPatch;
michael@0 1484 patch.QueryInterface(CoI.nsIWritablePropertyBag);
michael@0 1485 patch.setProperty("status", this._pausedStatus);
michael@0 1486 aus.pauseDownload();
michael@0 1487 }
michael@0 1488 this._paused = !this._paused;
michael@0 1489
michael@0 1490 // Update the UI
michael@0 1491 this._setUIState(this._paused);
michael@0 1492 },
michael@0 1493
michael@0 1494 /**
michael@0 1495 * When the user has closed the window using a Window Manager control (this
michael@0 1496 * page doesn't have a cancel button) cancel the update in progress.
michael@0 1497 */
michael@0 1498 onWizardCancel: function() {
michael@0 1499 if (this._hiding)
michael@0 1500 return;
michael@0 1501
michael@0 1502 this.cleanUp();
michael@0 1503 },
michael@0 1504
michael@0 1505 /**
michael@0 1506 * When the user closes the Wizard UI by clicking the Hide button
michael@0 1507 */
michael@0 1508 onHide: function() {
michael@0 1509 // Set _hiding to true to prevent onWizardCancel from cancelling the update
michael@0 1510 // that is in progress.
michael@0 1511 this._hiding = true;
michael@0 1512
michael@0 1513 // Remove ourself as a download listener so that we don't continue to be
michael@0 1514 // fed progress and state notifications after the UI we're updating has
michael@0 1515 // gone away.
michael@0 1516 this.cleanUp();
michael@0 1517
michael@0 1518 var aus = CoC["@mozilla.org/updates/update-service;1"].
michael@0 1519 getService(CoI.nsIApplicationUpdateService);
michael@0 1520 var um = CoC["@mozilla.org/updates/update-manager;1"].
michael@0 1521 getService(CoI.nsIUpdateManager);
michael@0 1522 um.activeUpdate = gUpdates.update;
michael@0 1523
michael@0 1524 // If the download was paused by the user, ask the user if they want to
michael@0 1525 // have the update resume in the background.
michael@0 1526 var downloadInBackground = true;
michael@0 1527 if (this._paused) {
michael@0 1528 var title = gUpdates.getAUSString("resumePausedAfterCloseTitle");
michael@0 1529 var message = gUpdates.getAUSString("resumePausedAfterCloseMsg",
michael@0 1530 [gUpdates.brandName]);
michael@0 1531 var ps = Services.prompt;
michael@0 1532 var flags = ps.STD_YES_NO_BUTTONS;
michael@0 1533 // Focus the software update wizard before prompting. This will raise
michael@0 1534 // the software update wizard if it is minimized making it more obvious
michael@0 1535 // what the prompt is for and will solve the problem of windows
michael@0 1536 // obscuring the prompt. See bug #350299 for more details.
michael@0 1537 window.focus();
michael@0 1538 var rv = ps.confirmEx(window, title, message, flags, null, null, null,
michael@0 1539 null, { });
michael@0 1540 if (rv == CoI.nsIPromptService.BUTTON_POS_0)
michael@0 1541 downloadInBackground = false;
michael@0 1542 }
michael@0 1543 if (downloadInBackground) {
michael@0 1544 // Continue download in the background at full speed.
michael@0 1545 LOG("gDownloadingPage", "onHide - continuing download in background " +
michael@0 1546 "at full speed");
michael@0 1547 aus.downloadUpdate(gUpdates.update, false);
michael@0 1548 }
michael@0 1549 gUpdates.wiz.cancel();
michael@0 1550 },
michael@0 1551
michael@0 1552 /**
michael@0 1553 * When the data transfer begins
michael@0 1554 * @param request
michael@0 1555 * The nsIRequest object for the transfer
michael@0 1556 * @param context
michael@0 1557 * Additional data
michael@0 1558 */
michael@0 1559 onStartRequest: function(request, context) {
michael@0 1560 // This !paused test is necessary because onStartRequest may fire after
michael@0 1561 // the download was paused (for those speedy clickers...)
michael@0 1562 if (this._paused)
michael@0 1563 return;
michael@0 1564
michael@0 1565 if (this._downloadProgress.mode != "undetermined")
michael@0 1566 this._downloadProgress.mode = "undetermined";
michael@0 1567 this._setStatus(this._label_downloadStatus);
michael@0 1568 },
michael@0 1569
michael@0 1570 /**
michael@0 1571 * When new data has been downloaded
michael@0 1572 * @param request
michael@0 1573 * The nsIRequest object for the transfer
michael@0 1574 * @param context
michael@0 1575 * Additional data
michael@0 1576 * @param progress
michael@0 1577 * The current number of bytes transferred
michael@0 1578 * @param maxProgress
michael@0 1579 * The total number of bytes that must be transferred
michael@0 1580 */
michael@0 1581 onProgress: function(request, context, progress, maxProgress) {
michael@0 1582 let status = this._updateDownloadStatus(progress, maxProgress);
michael@0 1583 var currentProgress = Math.round(100 * (progress / maxProgress));
michael@0 1584
michael@0 1585 var p = gUpdates.update.selectedPatch;
michael@0 1586 p.QueryInterface(CoI.nsIWritablePropertyBag);
michael@0 1587 p.setProperty("progress", currentProgress);
michael@0 1588 p.setProperty("status", status);
michael@0 1589
michael@0 1590 // This !paused test is necessary because onProgress may fire after
michael@0 1591 // the download was paused (for those speedy clickers...)
michael@0 1592 if (this._paused)
michael@0 1593 return;
michael@0 1594
michael@0 1595 if (this._downloadProgress.mode != "normal")
michael@0 1596 this._downloadProgress.mode = "normal";
michael@0 1597 if (this._downloadProgress.value != currentProgress)
michael@0 1598 this._downloadProgress.value = currentProgress;
michael@0 1599 if (this._pauseButton.disabled)
michael@0 1600 this._pauseButton.disabled = false;
michael@0 1601
michael@0 1602 // If the update has completed downloading and the download status contains
michael@0 1603 // the original text return early to avoid an assertion in debug builds.
michael@0 1604 // Since the page will advance immmediately due to the update completing the
michael@0 1605 // download updating the status is not important.
michael@0 1606 // nsTextFrame::GetTrimmedOffsets 'Can only call this on frames that have
michael@0 1607 // been reflowed'.
michael@0 1608 if (progress == maxProgress &&
michael@0 1609 this._downloadStatus.textContent == this._label_downloadStatus)
michael@0 1610 return;
michael@0 1611
michael@0 1612 this._setStatus(status);
michael@0 1613 },
michael@0 1614
michael@0 1615 /**
michael@0 1616 * When we have new status text
michael@0 1617 * @param request
michael@0 1618 * The nsIRequest object for the transfer
michael@0 1619 * @param context
michael@0 1620 * Additional data
michael@0 1621 * @param status
michael@0 1622 * A status code
michael@0 1623 * @param statusText
michael@0 1624 * Human readable version of |status|
michael@0 1625 */
michael@0 1626 onStatus: function(request, context, status, statusText) {
michael@0 1627 this._setStatus(statusText);
michael@0 1628 },
michael@0 1629
michael@0 1630 /**
michael@0 1631 * When data transfer ceases
michael@0 1632 * @param request
michael@0 1633 * The nsIRequest object for the transfer
michael@0 1634 * @param context
michael@0 1635 * Additional data
michael@0 1636 * @param status
michael@0 1637 * Status code containing the reason for the cessation.
michael@0 1638 */
michael@0 1639 onStopRequest: function(request, context, status) {
michael@0 1640 if (this._downloadProgress.mode != "normal")
michael@0 1641 this._downloadProgress.mode = "normal";
michael@0 1642
michael@0 1643 var u = gUpdates.update;
michael@0 1644 switch (status) {
michael@0 1645 case CoR.NS_ERROR_CORRUPTED_CONTENT:
michael@0 1646 case CoR.NS_ERROR_UNEXPECTED:
michael@0 1647 if (u.selectedPatch.state == STATE_DOWNLOAD_FAILED &&
michael@0 1648 (u.isCompleteUpdate || u.patchCount != 2)) {
michael@0 1649 // Verification error of complete patch, informational text is held in
michael@0 1650 // the update object.
michael@0 1651 this.cleanUp();
michael@0 1652 gUpdates.wiz.goTo("errors");
michael@0 1653 break;
michael@0 1654 }
michael@0 1655 // Verification failed for a partial patch, complete patch is now
michael@0 1656 // downloading so return early and do NOT remove the download listener!
michael@0 1657
michael@0 1658 // Reset the progress meter to "undertermined" mode so that we don't
michael@0 1659 // show old progress for the new download of the "complete" patch.
michael@0 1660 this._downloadProgress.mode = "undetermined";
michael@0 1661 this._pauseButton.disabled = true;
michael@0 1662 document.getElementById("verificationFailed").hidden = false;
michael@0 1663 break;
michael@0 1664 case CoR.NS_BINDING_ABORTED:
michael@0 1665 LOG("gDownloadingPage", "onStopRequest - pausing download");
michael@0 1666 // Do not remove UI listener since the user may resume downloading again.
michael@0 1667 break;
michael@0 1668 case CoR.NS_OK:
michael@0 1669 LOG("gDownloadingPage", "onStopRequest - patch verification succeeded");
michael@0 1670 // If the background update pref is set, we should wait until the update
michael@0 1671 // is actually staged in the background.
michael@0 1672 var aus = CoC["@mozilla.org/updates/update-service;1"].
michael@0 1673 getService(CoI.nsIApplicationUpdateService);
michael@0 1674 if (aus.canStageUpdates) {
michael@0 1675 this._setUpdateApplying();
michael@0 1676 } else {
michael@0 1677 this.cleanUp();
michael@0 1678 gUpdates.wiz.goTo("finished");
michael@0 1679 }
michael@0 1680 break;
michael@0 1681 default:
michael@0 1682 LOG("gDownloadingPage", "onStopRequest - transfer failed");
michael@0 1683 // Some kind of transfer error, die.
michael@0 1684 this.cleanUp();
michael@0 1685 gUpdates.wiz.goTo("errors");
michael@0 1686 break;
michael@0 1687 }
michael@0 1688 },
michael@0 1689
michael@0 1690 /**
michael@0 1691 * See nsIObserver.idl
michael@0 1692 */
michael@0 1693 observe: function(aSubject, aTopic, aData) {
michael@0 1694 if (aTopic == "update-staged") {
michael@0 1695 if (aData == STATE_DOWNLOADING) {
michael@0 1696 // We've fallen back to downloding the full update because the
michael@0 1697 // partial update failed to get staged in the background.
michael@0 1698 this._setStatus("downloading");
michael@0 1699 return;
michael@0 1700 }
michael@0 1701 this.cleanUp();
michael@0 1702 if (aData == STATE_APPLIED ||
michael@0 1703 aData == STATE_APPLIED_SVC ||
michael@0 1704 aData == STATE_PENDING ||
michael@0 1705 aData == STATE_PENDING_SVC) {
michael@0 1706 // If the update is successfully applied, or if the updater has
michael@0 1707 // fallen back to non-staged updates, go to the finish page.
michael@0 1708 gUpdates.wiz.goTo("finished");
michael@0 1709 } else {
michael@0 1710 gUpdates.wiz.goTo("errors");
michael@0 1711 }
michael@0 1712 }
michael@0 1713 },
michael@0 1714
michael@0 1715 /**
michael@0 1716 * See nsISupports.idl
michael@0 1717 */
michael@0 1718 QueryInterface: function(iid) {
michael@0 1719 if (!iid.equals(CoI.nsIRequestObserver) &&
michael@0 1720 !iid.equals(CoI.nsIProgressEventSink) &&
michael@0 1721 !iid.equals(CoI.nsIObserver) &&
michael@0 1722 !iid.equals(CoI.nsISupports))
michael@0 1723 throw CoR.NS_ERROR_NO_INTERFACE;
michael@0 1724 return this;
michael@0 1725 }
michael@0 1726 };
michael@0 1727
michael@0 1728 /**
michael@0 1729 * The "There was an error during the update" page.
michael@0 1730 */
michael@0 1731 var gErrorsPage = {
michael@0 1732 /**
michael@0 1733 * Initialize
michael@0 1734 */
michael@0 1735 onPageShow: function() {
michael@0 1736 gUpdates.setButtons(null, null, "okButton", true);
michael@0 1737 gUpdates.wiz.getButton("finish").focus();
michael@0 1738
michael@0 1739 var statusText = gUpdates.update.statusText;
michael@0 1740 LOG("gErrorsPage" , "onPageShow - update.statusText: " + statusText);
michael@0 1741
michael@0 1742 var errorReason = document.getElementById("errorReason");
michael@0 1743 errorReason.value = statusText;
michael@0 1744 var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
michael@0 1745 var errorLinkLabel = document.getElementById("errorLinkLabel");
michael@0 1746 errorLinkLabel.value = manualURL;
michael@0 1747 errorLinkLabel.setAttribute("url", manualURL);
michael@0 1748 }
michael@0 1749 };
michael@0 1750
michael@0 1751 /**
michael@0 1752 * The page shown when there is a background check or a certificate attribute
michael@0 1753 * error.
michael@0 1754 */
michael@0 1755 var gErrorExtraPage = {
michael@0 1756 /**
michael@0 1757 * Initialize
michael@0 1758 */
michael@0 1759 onPageShow: function() {
michael@0 1760 gUpdates.setButtons(null, null, "okButton", true);
michael@0 1761 gUpdates.wiz.getButton("finish").focus();
michael@0 1762 let secHistogram = CoC["@mozilla.org/base/telemetry;1"].
michael@0 1763 getService(CoI.nsITelemetry).
michael@0 1764 getHistogramById("SECURITY_UI");
michael@0 1765
michael@0 1766 if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS))
michael@0 1767 Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS);
michael@0 1768
michael@0 1769 if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
michael@0 1770 Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
michael@0 1771
michael@0 1772 if (gUpdates.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE) {
michael@0 1773 document.getElementById("errorCertAttrHasUpdateLabel").hidden = false;
michael@0 1774 secHistogram.add(CoI.nsISecurityUITelemetry.WARNING_INSECURE_UPDATE);
michael@0 1775 }
michael@0 1776 else {
michael@0 1777 if (gUpdates.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE){
michael@0 1778 document.getElementById("errorCertCheckNoUpdateLabel").hidden = false;
michael@0 1779 secHistogram.add(CoI.nsISecurityUITelemetry.WARNING_NO_SECURE_UPDATE);
michael@0 1780 }
michael@0 1781 else
michael@0 1782 document.getElementById("genericBackgroundErrorLabel").hidden = false;
michael@0 1783 var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
michael@0 1784 var errorLinkLabel = document.getElementById("errorExtraLinkLabel");
michael@0 1785 errorLinkLabel.value = manualURL;
michael@0 1786 errorLinkLabel.setAttribute("url", manualURL);
michael@0 1787 errorLinkLabel.hidden = false;
michael@0 1788 }
michael@0 1789 }
michael@0 1790 };
michael@0 1791
michael@0 1792 /**
michael@0 1793 * The "There was an error applying a partial patch" page.
michael@0 1794 */
michael@0 1795 var gErrorPatchingPage = {
michael@0 1796 /**
michael@0 1797 * Initialize
michael@0 1798 */
michael@0 1799 onPageShow: function() {
michael@0 1800 gUpdates.setButtons(null, null, "okButton", true);
michael@0 1801 },
michael@0 1802
michael@0 1803 onWizardNext: function() {
michael@0 1804 switch (gUpdates.update.selectedPatch.state) {
michael@0 1805 case STATE_PENDING:
michael@0 1806 case STATE_PENDING_SVC:
michael@0 1807 gUpdates.wiz.goTo("finished");
michael@0 1808 break;
michael@0 1809 case STATE_DOWNLOADING:
michael@0 1810 gUpdates.wiz.goTo("downloading");
michael@0 1811 break;
michael@0 1812 case STATE_DOWNLOAD_FAILED:
michael@0 1813 gUpdates.wiz.goTo("errors");
michael@0 1814 break;
michael@0 1815 }
michael@0 1816 }
michael@0 1817 };
michael@0 1818
michael@0 1819 /**
michael@0 1820 * The "Update has been downloaded" page. Shows information about what
michael@0 1821 * was downloaded.
michael@0 1822 */
michael@0 1823 var gFinishedPage = {
michael@0 1824 /**
michael@0 1825 * Initialize
michael@0 1826 */
michael@0 1827 onPageShow: function() {
michael@0 1828 gUpdates.setButtons("restartLaterButton", null, "restartNowButton",
michael@0 1829 true);
michael@0 1830 gUpdates.wiz.getButton("finish").focus();
michael@0 1831 },
michael@0 1832
michael@0 1833 /**
michael@0 1834 * Initialize the Wizard Page for a Background Source Event
michael@0 1835 */
michael@0 1836 onPageShowBackground: function() {
michael@0 1837 this.onPageShow();
michael@0 1838 var updateFinishedName = document.getElementById("updateFinishedName");
michael@0 1839 updateFinishedName.value = gUpdates.update.name;
michael@0 1840
michael@0 1841 var link = document.getElementById("finishedBackgroundLink");
michael@0 1842 if (gUpdates.update.detailsURL) {
michael@0 1843 link.setAttribute("url", gUpdates.update.detailsURL);
michael@0 1844 // The details link is stealing focus so it is disabled by default and
michael@0 1845 // should only be enabled after onPageShow has been called.
michael@0 1846 link.disabled = false;
michael@0 1847 }
michael@0 1848 else
michael@0 1849 link.hidden = true;
michael@0 1850
michael@0 1851 if (getPref("getBoolPref", PREF_APP_UPDATE_TEST_LOOP, false)) {
michael@0 1852 setTimeout(function () {
michael@0 1853 gUpdates.wiz.getButton("finish").click();
michael@0 1854 }, UPDATE_TEST_LOOP_INTERVAL);
michael@0 1855 }
michael@0 1856 },
michael@0 1857
michael@0 1858 /**
michael@0 1859 * Called when the wizard finishes, i.e. the "Restart Now" button is
michael@0 1860 * clicked.
michael@0 1861 */
michael@0 1862 onWizardFinish: function() {
michael@0 1863 // Do the restart
michael@0 1864 LOG("gFinishedPage" , "onWizardFinish - restarting the application");
michael@0 1865
michael@0 1866 // disable the "finish" (Restart) and "extra1" (Later) buttons
michael@0 1867 // because the Software Update wizard is still up at the point,
michael@0 1868 // and will remain up until we return and we close the
michael@0 1869 // window with a |window.close()| in wizard.xml
michael@0 1870 // (it was the firing the "wizardfinish" event that got us here.)
michael@0 1871 // This prevents the user from switching back
michael@0 1872 // to the Software Update dialog and clicking "Restart" or "Later"
michael@0 1873 // when dealing with the "confirm close" prompts.
michael@0 1874 // See bug #350299 for more details.
michael@0 1875 gUpdates.wiz.getButton("finish").disabled = true;
michael@0 1876 gUpdates.wiz.getButton("extra1").disabled = true;
michael@0 1877
michael@0 1878 // Notify all windows that an application quit has been requested.
michael@0 1879 var cancelQuit = CoC["@mozilla.org/supports-PRBool;1"].
michael@0 1880 createInstance(CoI.nsISupportsPRBool);
michael@0 1881 Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
michael@0 1882 "restart");
michael@0 1883
michael@0 1884 // Something aborted the quit process.
michael@0 1885 if (cancelQuit.data)
michael@0 1886 return;
michael@0 1887
michael@0 1888 // If already in safe mode restart in safe mode (bug 327119)
michael@0 1889 if (Services.appinfo.inSafeMode) {
michael@0 1890 let env = CoC["@mozilla.org/process/environment;1"].
michael@0 1891 getService(CoI.nsIEnvironment);
michael@0 1892 env.set("MOZ_SAFE_MODE_RESTART", "1");
michael@0 1893 }
michael@0 1894
michael@0 1895 // Restart the application
michael@0 1896 CoC["@mozilla.org/toolkit/app-startup;1"].getService(CoI.nsIAppStartup).
michael@0 1897 quit(CoI.nsIAppStartup.eAttemptQuit | CoI.nsIAppStartup.eRestart);
michael@0 1898 },
michael@0 1899
michael@0 1900 /**
michael@0 1901 * When the user clicks the "Restart Later" instead of the Restart Now" button
michael@0 1902 * in the wizard after an update has been downloaded.
michael@0 1903 */
michael@0 1904 onExtra1: function() {
michael@0 1905 gUpdates.wiz.cancel();
michael@0 1906 }
michael@0 1907 };
michael@0 1908
michael@0 1909 /**
michael@0 1910 * The "Update was Installed Successfully" page.
michael@0 1911 */
michael@0 1912 var gInstalledPage = {
michael@0 1913 /**
michael@0 1914 * Initialize
michael@0 1915 */
michael@0 1916 onPageShow: function() {
michael@0 1917 var branding = document.getElementById("brandStrings");
michael@0 1918 try {
michael@0 1919 // whatsNewURL should just be a pref (bug 546609).
michael@0 1920 var url = branding.getFormattedString("whatsNewURL", [Services.appinfo.version]);
michael@0 1921 var whatsnewLink = document.getElementById("whatsnewLink");
michael@0 1922 whatsnewLink.setAttribute("url", url);
michael@0 1923 whatsnewLink.hidden = false;
michael@0 1924 }
michael@0 1925 catch (e) {
michael@0 1926 }
michael@0 1927
michael@0 1928 gUpdates.setButtons(null, null, "okButton", true);
michael@0 1929 gUpdates.wiz.getButton("finish").focus();
michael@0 1930 }
michael@0 1931 };
michael@0 1932
michael@0 1933 /**
michael@0 1934 * Callback for the Update Prompt to set the current page if an Update Wizard
michael@0 1935 * window is already found to be open.
michael@0 1936 * @param pageid
michael@0 1937 * The ID of the page to switch to
michael@0 1938 */
michael@0 1939 function setCurrentPage(pageid) {
michael@0 1940 gUpdates.wiz.currentPage = document.getElementById(pageid);
michael@0 1941 }

mercurial