PageRenderTime 71ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/toolkit/mozapps/update/content/updates.js

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