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 +}