diff --git a/toolkit/mozapps/update/content/updates.css b/toolkit/mozapps/update/content/updates.css index 90b4c2cd8ddd..86a8de4efee3 100644 --- a/toolkit/mozapps/update/content/updates.css +++ b/toolkit/mozapps/update/content/updates.css @@ -5,18 +5,18 @@ wizard[description=""] .wizard-header-description { display: none; } -link { - -moz-binding: url("chrome://mozapps/content/update/updates.xml#link"); - -moz-user-focus: normal; - cursor: pointer; -} - -link > label { - cursor: inherit; +/** + * Stop animations when they aren't displayed (bug 341749). + */ +#updates:not([currentpageid="checking"]) #checkingProgress, +#updates:not([currentpageid="incompatibleCheck"]) #incompatibleCheckProgress, +#updates:not([currentpageid="downloading"]) #downloadThrobber, +#updates:not([currentpageid="downloading"]) #downloadProgress { + display: none; } /** - * Remote Content + * Remote content displayed in the billboard and license pages. */ remotecontent { -moz-binding: url("chrome://mozapps/content/update/updates.xml#remotecontent"); diff --git a/toolkit/mozapps/update/content/updates.js b/toolkit/mozapps/update/content/updates.js index a4c202532bca..a73fc80c4c1f 100644 --- a/toolkit/mozapps/update/content/updates.js +++ b/toolkit/mozapps/update/content/updates.js @@ -73,6 +73,16 @@ var gConsole = null; var gPref = null; var gLogEnabled = false; +// Notes: +// 1. use the wizard's goTo method whenever possible to change the wizard +// page since it is simpler than most other methods and behaves nicely with +// mochitests. +// 2. using a page's onPageShow method to then change to a different page will +// of course call that page's onPageShow method which can make mochitests +// overly complicated and fragile so avoid doing this if at all possible. +// This is why a page's next attribute is set prior to the page being shown +// whenever possible. + /** * Logs a string to the error console. * @param string @@ -127,6 +137,11 @@ var gUpdates = { */ update: null, + /** + * List of incompatible add-ons + */ + addons: [], + /** * The updates.properties element. */ @@ -184,6 +199,9 @@ var gUpdates = { * @param canAdvance * true if the wizard can be advanced (e.g. the next / finish button * should be enabled), false otherwise. + * @param showCancel + * true if the wizard's cancel button should be shown, false + * otherwise. If not specified this will default to false. * * Note: * Per Bug 324121 the wizard should not look like a wizard and to accomplish @@ -196,23 +214,33 @@ var gUpdates = { * +--------------------------------------------------------------+ */ setButtons: function(extra1ButtonString, extra2ButtonString, - nextFinishButtonString, canAdvance) { + nextFinishButtonString, canAdvance, showCancel) { this.wiz.canAdvance = canAdvance; - var bnf = this.wiz.getButton((this.wiz.onLastPage ? "finish" : "next")); + var bnf = this.wiz.getButton(this.wiz.onLastPage ? "finish" : "next"); var be1 = this.wiz.getButton("extra1"); var be2 = this.wiz.getButton("extra2"); + var bc = this.wiz.getButton("cancel"); // Set the labels for the next / finish, extra1, and extra2 buttons this._setButton(bnf, nextFinishButtonString); this._setButton(be1, extra1ButtonString); this._setButton(be2, extra2ButtonString); - bnf.hidden = !nextFinishButtonString; - be1.hidden = !extra1ButtonString; - be2.hidden = !extra2ButtonString; - // Hide the back button each time setButtons is called (see bug 464765) - this.wiz.getButton("back").hidden = true; + bnf.hidden = bnf.disabled = !nextFinishButtonString; + be1.hidden = be1.disabled = !extra1ButtonString; + be2.hidden = be2.disabled = !extra2ButtonString; + bc.hidden = bc.disabled = !showCancel; + + // Hide and disable the back button each time setButtons is called + // (see bug 464765). + var btn = this.wiz.getButton("back"); + btn.hidden = btn.disabled = true; + + // Hide and disable the finish button if not on the last page or the next + // button if on the last page each time setButtons is called. + btn = this.wiz.getButton(this.wiz.onLastPage ? "next" : "finish"); + btn.hidden = btn.disabled = true; }, getAUSString: function(key, strings) { @@ -225,7 +253,7 @@ var gUpdates = { // If the user clicks "No Thanks", we should not prompt them to update to // this version again unless they manually select "Check for Updates..." // which will clear all of the "never" prefs. - var neverPrefName = PREF_APP_UPDATE_NEVER_BRANCH + gUpdates.update.appVersion; + var neverPrefName = PREF_APP_UPDATE_NEVER_BRANCH + this.update.appVersion; gPref.setBoolPref(neverPrefName, true); this.wiz.cancel(); }, @@ -316,7 +344,7 @@ var gUpdates = { var brandStrings = document.getElementById("brandStrings"); this.brandName = brandStrings.getString("brandShortName"); - var pages = gUpdates.wiz.childNodes; + var pages = this.wiz.childNodes; for (var i = 0; i < pages.length; ++i) { var page = pages[i]; if (page.localName == "wizardpage") @@ -329,12 +357,10 @@ var gUpdates = { this._cacheButtonStrings("extra1"); this._cacheButtonStrings("extra2"); - this.wiz.getButton("cancel").hidden = true; - // Advance to the Start page. var startPage = this.startPage; LOG("gUpdates", "onLoad - setting current page to startpage " + startPage.id); - gUpdates.wiz.currentPage = startPage; + this.wiz.currentPage = startPage; }, /** @@ -342,7 +368,7 @@ var gUpdates = { */ onUnload: function() { if (this._runUnload) { - var cp = gUpdates.wiz.currentPage; + var cp = this.wiz.currentPage; if (cp.pageid != "finished" && cp.pageid != "finishedBackground") this.onWizardCancel(); } @@ -354,13 +380,19 @@ var gUpdates = { * This is determined by how we were called by the update prompt: * * Prompt Method: Arg0: Update State: Src Event: Failed: Result: - * showUpdateAvailable nsIUpdate obj -- background -- incompatibleCheck + * showUpdateAvailable nsIUpdate obj -- background -- see Note below * showUpdateDownloaded nsIUpdate obj pending background -- finishedBackground * showUpdateInstalled "installed" -- -- -- installed * showUpdateError nsIUpdate obj failed either partial errorpatching * showUpdateError nsIUpdate obj failed either complete errors * checkForUpdates null -- foreground -- checking * checkForUpdates null downloading foreground -- downloading + * + * Note: the page returned (e.g. Result) for showUpdateAvaulable is as follows: + * New enabled incompatible add-ons : incompatibleCheck page + * No new enabled incompatible add-ons: either updatesfoundbasic or + * updatesfoundbillboard as determined by + * updatesFoundPageId */ get startPage() { if ("arguments" in window && window.arguments[0]) { @@ -379,18 +411,20 @@ var gUpdates = { } catch (e) { } - if (patchFailed == "partial") { - // If the system failed to apply the partial patch, show the - // screen which best describes this condition, which is triggered - // by the |STATE_FAILED| state. - state = STATE_FAILED; - } - else if (patchFailed == "complete") { - // Otherwise, if the complete patch failed, which is far less - // likely, show the error text held by the update object in the - // generic errors page, triggered by the |STATE_DOWNLOAD_FAILED| - // state. - state = STATE_DOWNLOAD_FAILED; + if (patchFailed) { + if (patchFailed == "partial" && this.update.patchCount == 2) { + // If the system failed to apply the partial patch, show the + // screen which best describes this condition, which is triggered + // by the |STATE_FAILED| state. + state = STATE_FAILED; + } + else { + // Otherwise, if the complete patch failed, which is far less + // likely, show the error text held by the update object in the + // generic errors page, triggered by the |STATE_DOWNLOAD_FAILED| + // state. + state = STATE_DOWNLOAD_FAILED; + } } // Now select the best page to start with, given the current state of @@ -409,7 +443,14 @@ var gUpdates = { return document.getElementById("errors"); } } - return document.getElementById("incompatibleCheck"); + if (this.update.licenseURL) + this.wiz.getPageById(this.updatesFoundPageId).setAttribute("next", "license"); + if (this.shouldCheckAddonCompatibility) { + var incompatCheckPage = document.getElementById("incompatibleCheck"); + incompatCheckPage.setAttribute("next", this.updatesFoundPageId); + return incompatCheckPage; + } + return document.getElementById(this.updatesFoundPageId); } else if (arg0 == "installed") { return document.getElementById("installed"); @@ -426,6 +467,51 @@ var gUpdates = { return document.getElementById("checking"); }, + get shouldCheckAddonCompatibility() { + // this early return should never happen + if (!this.update) + return false; + + delete this.shouldCheckAddonCompatibility; + var ai = CoC["@mozilla.org/xre/app-info;1"].getService(CoI.nsIXULAppInfo); + var vc = CoC["@mozilla.org/xpcom/version-comparator;1"]. + getService(CoI.nsIVersionComparator); + if (!this.update.appVersion || + vc.compare(this.update.appVersion, ai.version) == 0) + return this.shouldCheckAddonCompatibility = false; + + var em = CoC["@mozilla.org/extensions/manager;1"]. + getService(CoI.nsIExtensionManager); + this.addons = em.getIncompatibleItemList(this.update.appVersion, + this.update.platformVersion, + CoI.nsIUpdateItem.TYPE_ANY, false); + if (this.addons.length > 0) { + // Don't include add-ons that are already incompatible with the current + // version of the application + var addons = em.getIncompatibleItemList(null, null, + CoI.nsIUpdateItem.TYPE_ANY, false); + for (var i = 0; i < addons.length; ++i) { + for (var j = 0; j < this.addons.length; ++j) { + if (addons[i].id == this.addons[j].id) { + this.addons.splice(j, 1); + break; + } + } + } + } + return this.shouldCheckAddonCompatibility = (this.addons.length != 0); + }, + + /** + * Returns the string page ID for the appropriate updates found page based + * on the update's metadata. + */ + get updatesFoundPageId() { + delete this.updatesFoundPageId; + return this.updatesFoundPageId = this.update.billboardURL ? "updatesfoundbillboard" + : "updatesfoundbasic"; + }, + /** * Sets the Update object for this wizard * @param update @@ -453,9 +539,8 @@ var gCheckingPage = { * Initialize */ onPageShow: function() { - document.getElementById("checkingProgress").hidden = false; - gUpdates.wiz.getButton("cancel").hidden = false; - gUpdates.setButtons(null, null, null, false); + gUpdates.setButtons(null, null, null, false, true); + gUpdates.wiz.getButton("cancel").focus(); this._checker = CoC["@mozilla.org/updates/update-checker;1"]. createInstance(CoI.nsIUpdateChecker); this._checker.checkForUpdates(this.updateListener, true); @@ -487,20 +572,35 @@ var gCheckingPage = { * See nsIUpdateCheckListener */ onCheckComplete: function(request, updates, updateCount) { - document.getElementById("checkingProgress").hidden = true; - gUpdates.wiz.getButton("cancel").hidden = true; var aus = CoC["@mozilla.org/updates/update-service;1"]. getService(CoI.nsIApplicationUpdateService); gUpdates.setUpdate(aus.selectUpdate(updates, updates.length)); if (gUpdates.update) { LOG("gCheckingPage", "onCheckComplete - update found"); - gUpdates.wiz.currentPage.setAttribute("next", "incompatibleCheck"); - } - else - LOG("gCheckingPage", "onCheckComplete - no update found"); + if (!aus.canApplyUpdates) { + gUpdates.wiz.goTo("manualUpdate"); + return; + } - gUpdates.wiz.canAdvance = true; - gUpdates.wiz.advance(); + if (gUpdates.update.licenseURL) { + // gUpdates.updatesFoundPageId returns the pageid and not the + // element's id so use the wizard's getPageById method. + gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "license"); + } + + if (gUpdates.shouldCheckAddonCompatibility) { + var incompatCheckPage = document.getElementById("incompatibleCheck"); + incompatCheckPage.setAttribute("next", gUpdates.updatesFoundPageId); + gUpdates.wiz.goTo("incompatibleCheck"); + } + else { + gUpdates.wiz.goTo(gUpdates.updatesFoundPageId); + } + return; + } + + LOG("gCheckingPage", "onCheckComplete - no update found"); + gUpdates.wiz.goTo("noupdatesfound"); }, /** @@ -508,10 +608,8 @@ var gCheckingPage = { */ onError: function(request, update) { LOG("gCheckingPage", "onError - proceeding to error page"); - document.getElementById("checkingProgress").hidden = true; - gUpdates.wiz.getButton("cancel").hidden = true; gUpdates.setUpdate(update); - gUpdates.wiz.currentPage = document.getElementById("errors"); + gUpdates.wiz.goTo("errors"); }, /** @@ -608,11 +706,6 @@ var gNoUpdatesPage = { * The page that checks if there are any incompatible add-ons. */ var gIncompatibleCheckPage = { - /** - * List of incompatible add-ons - */ - addons: [], - /** * Count of incompatible add-ons to check for updates */ @@ -632,64 +725,17 @@ var gIncompatibleCheckPage = { * Initialize */ onPageShow: function() { - var aus = CoC["@mozilla.org/updates/update-service;1"]. - getService(CoI.nsIApplicationUpdateService); - // Display the manual update page if the user is unable to apply the update - if (!aus.canApplyUpdates) { - gUpdates.wiz.currentPage.setAttribute("next", "manualUpdate"); - gUpdates.wiz.advance(); - return; - } - - var ai = CoC["@mozilla.org/xre/app-info;1"].getService(CoI.nsIXULAppInfo); - var vc = CoC["@mozilla.org/xpcom/version-comparator;1"]. - getService(CoI.nsIVersionComparator); - if (!gUpdates.update.appVersion || - vc.compare(gUpdates.update.appVersion, ai.version) == 0) { - // Go to the next page - gUpdates.wiz.advance(); - return; - } - - var em = CoC["@mozilla.org/extensions/manager;1"]. - getService(CoI.nsIExtensionManager); - this.addons = em.getIncompatibleItemList(gUpdates.update.appVersion, - gUpdates.update.platformVersion, - CoI.nsIUpdateItem.TYPE_ANY, false); - if (this.addons.length > 0) { - // Don't include add-ons that are already incompatible with the current - // version of the application - var addons = em.getIncompatibleItemList(null, null, - CoI.nsIUpdateItem.TYPE_ANY, false); - for (var i = 0; i < addons.length; ++i) { - for (var j = 0; j < this.addons.length; ++j) { - if (addons[i].id == this.addons[j].id) { - this.addons.splice(j, 1); - break; - } - } - } - } - - if (this.addons.length == 0) { - // Go to the next page - gUpdates.wiz.advance(); - return; - } - LOG("gIncompatibleCheckPage", "onPageShow - checking for updates to " + "incompatible add-ons"); - gUpdates.wiz.getButton("cancel").hidden = false; - gUpdates.setButtons(null, null, null, false); + gUpdates.setButtons(null, null, null, false, true); gUpdates.wiz.getButton("cancel").focus(); this._pBar = document.getElementById("incompatibleCheckProgress"); - this._pBar.hidden = false; - this._totalCount = this.addons.length; + this._totalCount = gUpdates.addons.length; var em = CoC["@mozilla.org/extensions/manager;1"]. getService(CoI.nsIExtensionManager); - em.update(this.addons, this.addons.length, + em.update(gUpdates.addons, gUpdates.addons.length, CoI.nsIExtensionManager.UPDATE_NOTIFY_NEWVERSION, this, CoI.nsIExtensionManager.UPDATE_WHEN_NEW_APP_DETECTED, gUpdates.update.appVersion, gUpdates.update.platformVersion); @@ -706,19 +752,23 @@ var gIncompatibleCheckPage = { * See nsIExtensionManager.idl */ onUpdateEnded: function() { - if (this.addons.length == 0) { + if (gUpdates.addons.length == 0) { LOG("gIncompatibleCheckPage", "onUpdateEnded - updates were found " + "for all incompatible add-ons"); } else { LOG("gIncompatibleCheckPage", "onUpdateEnded - there are still " + "incompatible add-ons"); + if (gUpdates.update.licenseURL) { + document.getElementById("license").setAttribute("next", "incompatibleList"); + } + else { + // gUpdates.updatesFoundPageId returns the pageid and not the element's + // id so use the wizard's getPageById method. + gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "incompatibleList"); + } } - - this._pBar.hidden = true; - gUpdates.wiz.getButton("cancel").hidden = true; - gUpdates.wiz.canAdvance = true; - gUpdates.wiz.advance(); + gUpdates.wiz.goTo(gUpdates.updatesFoundPageId); }, /** @@ -738,11 +788,11 @@ var gIncompatibleCheckPage = { status != CoI.nsIAddonUpdateCheckListener.STATUS_VERSIONINFO) return; - for (var i = 0; i < this.addons.length; ++i) { - if (this.addons[i].id == addon.id) { + for (var i = 0; i < gUpdates.addons.length; ++i) { + if (gUpdates.addons[i].id == addon.id) { LOG("gIncompatibleCheckPage", "onAddonUpdateEnded - found update " + "for add-on ID: " + addon.id); - this.addons.splice(i, 1); + gUpdates.addons.splice(i, 1); break; } } @@ -782,34 +832,20 @@ var gManualUpdatePage = { * The "Updates Are Available" page. Provides the user information about the * available update. */ -var gUpdatesAvailablePage = { - /** - * If this page has been previously loaded - */ - _loaded: false, - +var gUpdatesFoundBasicPage = { /** * Initialize */ onPageShow: function() { + gUpdates.wiz.canRewind = false; var update = gUpdates.update; gUpdates.setButtons("askLaterButton", update.showNeverForVersion ? "noThanksButton" : null, "updateButton_" + update.type, true); var btn = gUpdates.wiz.getButton("next"); - btn.className += " heed"; + btn.classList.add("heed"); btn.focus(); - if (this._loaded) - return; - - if (!update.licenseURL) { - if (gIncompatibleCheckPage.addons.length == 0) - gUpdates.wiz.currentPage.setAttribute("next", "downloading"); - else - gUpdates.wiz.currentPage.setAttribute("next", "incompatibleList"); - } - var updateName = update.name; if (update.channel == "nightly") { updateName = gUpdates.getAUSString("updateName", [gUpdates.brandName, @@ -818,49 +854,17 @@ var gUpdatesAvailablePage = { } var updateNameElement = document.getElementById("updateName"); updateNameElement.value = updateName; - var updateTypeElement = document.getElementById("updateType"); - updateTypeElement.setAttribute("severity", update.type); - var moreInfoContent = document.getElementById("moreInfoContent"); - var intro; - if (update.billboardURL) { - // for major updates, use the brandName and the version for the intro - intro = gUpdates.getAUSString("intro_major_app_and_version", - [gUpdates.brandName, update.displayVersion]); - var remoteContent = document.getElementById("updateMoreInfoContent"); - // update_name and update_version need to be set before url - // so that when attempting to download the url, we can show - // the formatted "Download..." string - remoteContent.update_name = gUpdates.brandName; - remoteContent.update_version = update.displayVersion; - remoteContent.url = update.billboardURL; + var introText = gUpdates.getAUSString("intro_minor_app", [gUpdates.brandName]); + var introElem = document.getElementById("updatesFoundInto"); + introElem.setAttribute("severity", update.type); + introElem.textContent = introText; - moreInfoContent.hidden = false; - document.getElementById("moreInfoURL").hidden = true; - document.getElementById("updateName").hidden = true; - document.getElementById("updateNameSep").hidden = true; - document.getElementById("upgradeEvangelism").hidden = true; - document.getElementById("upgradeEvangelismSep").hidden = true; - - // Clear all of the "never" prefs to handle the scenario where the user - // clicked "never" for an update, selected "Check for Updates...", and - // then canceled. If we don't clear the "never" prefs future - // notifications will never happen. - gPref.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH); - } - else { - // for minor updates, just use the brandName for the intro - intro = gUpdates.getAUSString("intro_minor_app", [gUpdates.brandName]); - // This element when hidden still receives focus events which will - // cause assertions with debug builds so remove it when it isn't used. - moreInfoContent.parentNode.removeChild(moreInfoContent); - var updateMoreInfoURL = document.getElementById("updateMoreInfoURL"); - if (update.detailsURL) - updateMoreInfoURL.setAttribute("url", update.detailsURL); - else - updateMoreInfoURL.hidden = true; - } - updateTypeElement.textContent = intro; + var updateMoreInfoURL = document.getElementById("updateMoreInfoURL"); + if (update.detailsURL) + updateMoreInfoURL.setAttribute("url", update.detailsURL); + else + updateMoreInfoURL.hidden = true; var updateTitle = gUpdates.getAUSString("updatesfound_" + update.type + ".title"); @@ -869,7 +873,79 @@ var gUpdatesAvailablePage = { // wizard page show up gUpdates.wiz._adjustWizardHeader(); - this._loaded = true; + // Clear all of the "never" prefs to handle the scenario where the user + // clicked "never" for an update, selected "Check for Updates...", and + // then canceled. If we don't clear the "never" prefs future + // notifications will never happen. + gPref.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH); + }, + + onExtra1: function() { + gUpdates.later(); + }, + + onExtra2: function() { + gUpdates.never(); + }, + + onWizardNext: function() { + gUpdates.wiz.getButton("next").classList.remove("heed"); + } +}; + +/** + * The "Updates Are Available" page with a billboard. Provides the user + * information about the available update. + */ +var gUpdatesFoundBillboardPage = { + /** + * If this page has been previously loaded + */ + _billboardLoaded: false, + + /** + * Initialize + */ + onPageShow: function() { + var update = gUpdates.update; + var updateTitle = gUpdates.getAUSString("updatesfound_" + update.type + + ".title"); + gUpdates.wiz.currentPage.setAttribute("label", updateTitle); + // this is necessary to make this change to the label of the current + // wizard page show up + gUpdates.wiz._adjustWizardHeader(); + + gUpdates.setButtons("askLaterButton", + update.showNeverForVersion ? "noThanksButton" : null, + "updateButton_" + update.type, true); + var btn = gUpdates.wiz.getButton("next"); + btn.classList.add("heed"); + btn.focus(); + + if (this._billboardLoaded) + return; + + var remoteContent = document.getElementById("updateMoreInfoContent"); + // update_name and update_version need to be set before url + // so that when attempting to download the url, we can show + // the formatted "Download..." string + remoteContent.update_name = gUpdates.brandName; + remoteContent.update_version = update.displayVersion; + remoteContent.url = update.billboardURL; + + var introText = gUpdates.getAUSString("intro_major_app_and_version", + [gUpdates.brandName, update.displayVersion]); + var introElem = document.getElementById("updatesFoundBillboardIntro"); + introElem.setAttribute("severity", update.type); + introElem.textContent = introText; + + // Clear all of the "never" prefs to handle the scenario where the user + // clicked "never" for an update, selected "Check for Updates...", and + // then canceled. If we don't clear the "never" prefs future + // notifications will never happen. + gPref.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH); + + this._billboardLoaded = true; }, onExtra1: function() { @@ -883,9 +959,7 @@ var gUpdatesAvailablePage = { }, onWizardNext: function() { - var regex = new RegExp('\\s*heed'); - var btn = gUpdates.wiz.getButton("next"); - btn.className = btn.className.replace(regex, ""); + gUpdates.wiz.getButton("next").classList.remove("heed"); }, /** @@ -898,7 +972,7 @@ var gUpdatesAvailablePage = { remoteContent.stopDownloading(); } catch (e) { - LOG("gUpdatesAvailablePage", "onWizardCancel - " + + LOG("gUpdatesFoundBillboardPage", "onWizardCancel - " + "moreInfoContent.stopDownloading() failed: " + e); } } @@ -925,16 +999,11 @@ var gLicensePage = { if (this._licenseLoaded || licenseContent.getAttribute("state") == "error") { this.onAcceptDeclineRadio(); var licenseGroup = document.getElementById("acceptDeclineLicense"); - if (licenseGroup.selectedIndex == 0) - gUpdates.wiz.getButton("next").focus(); - else - licenseGroup.focus(); + licenseGroup.focus(); return; } - gUpdates.wiz.getButton("extra1").disabled = true; - if (gIncompatibleCheckPage.addons.length == 0) - gUpdates.wiz.currentPage.setAttribute("next", "downloading"); + gUpdates.wiz.canAdvance = false; // Disable the license radiogroup until the EULA has been downloaded document.getElementById("acceptDeclineLicense").disabled = true; @@ -986,12 +1055,15 @@ var gLicensePage = { gUpdates.wiz.canAdvance = licenseAccepted; }, + /** + * The non-standard "Back" button. + */ onExtra1: function() { - gUpdates.wiz.currentPage = document.getElementById("updatesfound"); + gUpdates.wiz.goTo(gUpdates.updatesFoundPageId); }, /** - * When the user accepts the license by hitting "Next" + * When the user clicks next after accepting the license */ onWizardNext: function() { try { @@ -1051,7 +1123,7 @@ var gIncompatibleListPage = { document.getElementById("incompatibleListDesc").textContent = intro; - var addons = gIncompatibleCheckPage.addons; + var addons = gUpdates.addons; for (var i = 0; i < addons.length; ++i) { var listitem = document.createElement("listitem"); listitem.setAttribute("label", addons[i].name + " " + addons[i].version); @@ -1059,12 +1131,12 @@ var gIncompatibleListPage = { } }, + /** + * The non-standard "Back" button. + */ onExtra1: function() { - var updatesfoundPage = document.getElementById("updatesfound"); - if (updatesfoundPage.getAttribute("next") == "license") - gUpdates.wiz.currentPage = document.getElementById("license"); - else - gUpdates.wiz.currentPage = updatesfoundPage; + gUpdates.wiz.goTo(gUpdates.update.licenseURL ? "license" + : gUpdates.updatesFoundPageId); } }; @@ -1106,7 +1178,6 @@ var gDownloadingPage = { this._downloadName = document.getElementById("downloadName"); this._downloadStatus = document.getElementById("downloadStatus"); this._downloadProgress = document.getElementById("downloadProgress"); - this._downloadProgress.hidden = false; this._downloadThrobber = document.getElementById("downloadThrobber"); this._pauseButton = document.getElementById("pauseButton"); this._label_downloadStatus = this._downloadStatus.textContent; @@ -1147,7 +1218,9 @@ var gDownloadingPage = { // we fell back from a partial patch to a complete patch and even // then we couldn't validate. Show a validation error with instructions // on how to manually update. - this.showVerificationError(); + this.removeDownloadListener(); + gUpdates.wiz.goTo("errors"); + return; } else { // Add this UI as a listener for active downloads @@ -1171,12 +1244,6 @@ var gDownloadingPage = { gUpdates.wiz.getButton("extra1").focus(); }, - showVerificationError: function() { - var verificationError = gUpdates.getAUSString("verificationError", - [gUpdates.brandName]); - gUpdates.advanceToErrorPage(verificationError); - }, - /** * Updates the text status message */ @@ -1252,6 +1319,15 @@ var gDownloadingPage = { } }, + /** + * Removes the download listener. + */ + removeDownloadListener: function() { + var aus = CoC["@mozilla.org/updates/update-service;1"]. + getService(CoI.nsIApplicationUpdateService); + aus.removeDownloadListener(this); + }, + /** * When the user clicks the Pause/Resume button */ @@ -1279,10 +1355,10 @@ var gDownloadingPage = { // Remove ourself as a download listener so that we don't continue to be // fed progress and state notifications after the UI we're updating has // gone away. + this.removeDownloadListener(); + var aus = CoC["@mozilla.org/updates/update-service;1"]. getService(CoI.nsIApplicationUpdateService); - aus.removeDownloadListener(this); - var um = CoC["@mozilla.org/updates/update-manager;1"]. getService(CoI.nsIUpdateManager); um.activeUpdate = gUpdates.update; @@ -1353,7 +1429,6 @@ var gDownloadingPage = { onProgress: function(request, context, progress, maxProgress) { LOG("gDownloadingPage", "onProgress - progress: " + progress + "/" + maxProgress); - var name = gUpdates.getAUSString("downloadingPrefix", [gUpdates.update.name]); let status = this._updateDownloadStatus(progress, maxProgress); var currentProgress = Math.round(100 * (progress / maxProgress)); @@ -1373,9 +1448,23 @@ var gDownloadingPage = { this._downloadThrobber.setAttribute("state", "loading"); if (this._downloadProgress.mode != "normal") this._downloadProgress.mode = "normal"; - this._downloadProgress.value = currentProgress; - this._pauseButton.disabled = false; - this._downloadName.value = name; + if (this._downloadProgress.value != currentProgress) + this._downloadProgress.value = currentProgress; + if (this._pauseButton.disabled) + this._pauseButton.disabled = false; + if (this._downloadName.value != name) + this._downloadName.value = name; + + // If the update has completed downloading and the download status contains + // the original text return early to avoid an assertion in debug builds. + // Since the page will advance immmediately due to the update completing the + // download updating the status is not important. + // nsTextFrame::GetTrimmedOffsets 'Can only call this on frames that have + // been reflowed'. + if (progress == maxProgress && + this._downloadStatus.textContent == this._label_downloadStatus) + return; + this._setStatus(status); }, @@ -1422,8 +1511,8 @@ var gDownloadingPage = { u.isCompleteUpdate) { // Verification error of complete patch, informational text is held in // the update object. - this._downloadProgress.hidden = true; - gUpdates.wiz.currentPage = document.getElementById("errors"); + this.removeDownloadListener(); + gUpdates.wiz.goTo("errors"); } else { // Verification failed for a partial patch, complete patch is now @@ -1447,21 +1536,16 @@ var gDownloadingPage = { return; case CoR.NS_OK: LOG("gDownloadingPage", "onStopRequest - patch verification succeeded"); - this._downloadProgress.hidden = true; - gUpdates.wiz.canAdvance = true; - gUpdates.wiz.advance(); + this.removeDownloadListener(); + gUpdates.wiz.goTo("finished"); break; default: LOG("gDownloadingPage", "onStopRequest - transfer failed"); // Some kind of transfer error, die. - this._downloadProgress.hidden = true; - gUpdates.wiz.currentPage = document.getElementById("errors"); + this.removeDownloadListener(); + gUpdates.wiz.goTo("errors"); break; } - - var aus = CoC["@mozilla.org/updates/update-service;1"]. - getService(CoI.nsIApplicationUpdateService); - aus.removeDownloadListener(this); }, /** @@ -1477,7 +1561,7 @@ var gDownloadingPage = { }; /** - * The "There was an error during the update" page. + * The "There was an error applying the update's patch" page. */ var gErrorsPage = { /** @@ -1499,42 +1583,32 @@ var gErrorsPage = { var errorLinkLabel = document.getElementById("errorLinkLabel"); errorLinkLabel.value = manualURL; errorLinkLabel.setAttribute("url", manualURL); + } +}; + +/** + * The "There was an error applying a partial patch" page. + */ +var gErrorPatchingPage = { + /** + * Initialize + */ + onPageShow: function() { + gUpdates.setButtons(null, null, "okButton", true); }, - /** - * Finish button clicked. - */ - onWizardFinish: function() { - // XXXjwalden COMPLETE AND TOTAL HACK!!! - // - // The problem the following code is working around is this: the update - // service's API for responding to updates is poor. Essentially, all - // the information we can get is that we've started to request a download or - // that the download we've started has finished, with minimal details about - // how it finished which aren't described in the API (and which internally - // are not entirely useful, either -- mostly unuseful nsresults). How - // do you signal the difference between "this download failed" and "all - // downloads failed", particularly if you aim for API compatibility? The - // code in nsApplicationUpdateService only determines the difference *after* - // the current request is stopped, and since the subsequent second call to - // downloadUpdate doesn't start/stop a request, the download listener is - // never notified and whatever code was relying on it just fails without - // notification. The consequence of this is that it's impossible to - // properly remove the download listener. - // - // The code before this patch tried to do the exit after all downloads - // failed but was missing a QueryInterface to work; with it, making sure - // that the download listener is removed in all cases, including in the case - // where the last onStopRequest corresponds to *a* failed download instead - // of to *all* failed downloads, simply means that we have to try to remove - // that listener in the error page spawned by the update service. If there - // were time and API compat weren't a problem, we'd add an onFinish handler - // or something which could signal exactly what happened and not overload - // onStopRequest, but there isn't, so we can't. - // - var aus = CoC["@mozilla.org/updates/update-service;1"]. - getService(CoI.nsIApplicationUpdateService); - aus.removeDownloadListener(gDownloadingPage); + onWizardNext: function() { + switch (gUpdates.update.selectedPatch.state) { + case STATE_PENDING: + gUpdates.wiz.goTo("finished"); + break; + case STATE_DOWNLOADING: + gUpdates.wiz.goTo("downloading"); + break; + case STATE_DOWNLOAD_FAILED: + gUpdates.wiz.goTo("errors"); + break; + } } }; @@ -1550,7 +1624,7 @@ var gFinishedPage = { gUpdates.setButtons("restartLaterButton", null, "restartNowButton", true); var btn = gUpdates.wiz.getButton("finish"); - btn.className += " heed"; + btn.classList.add("heed"); btn.focus(); }, @@ -1637,6 +1711,7 @@ var gInstalledPage = { var branding = document.getElementById("brandStrings"); try { + // whatsNewURL should just be a pref (bug 546609). var url = branding.getFormattedString("whatsNewURL", [ai.version]); var whatsnewLink = document.getElementById("whatsnewLink"); whatsnewLink.setAttribute("url", url); diff --git a/toolkit/mozapps/update/content/updates.xul b/toolkit/mozapps/update/content/updates.xul index 1b6f7232c846..3b409e72891f 100644 --- a/toolkit/mozapps/update/content/updates.xul +++ b/toolkit/mozapps/update/content/updates.xul @@ -22,6 +22,7 @@ # # Contributor(s): # Ben Goodger +# Robert Strong (Original Author) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or @@ -78,7 +79,7 @@ onpageshow="gCheckingPage.onPageShow();"> -