Bug 1355818 - Wait for staging to finish in the update wizard if downloading is done before the download page appears. r=rstrong, a=gchang
authorMatt Howell <mhowell@mozilla.com>
Fri, 19 May 2017 08:51:19 -0700
changeset 394038 82fcc3b1242af487f8faa04da0f0abebf479755a
parent 394037 1094b15fa46efd628f614fd20c03b3d3b4dd741f
child 394039 0511fcd323f3a43ffbdae0e41353b9af776cfc37
push id7339
push userryanvm@gmail.com
push dateMon, 22 May 2017 18:17:11 +0000
treeherdermozilla-beta@5de3b1528ca0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong, gchang
bugs1355818
milestone54.0
Bug 1355818 - Wait for staging to finish in the update wizard if downloading is done before the download page appears. r=rstrong, a=gchang
toolkit/mozapps/update/content/updates.js
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/tests/chrome/chrome.ini
toolkit/mozapps/update/tests/chrome/test_0085_error_patchApplyFailure_partial_complete_staging.xul
toolkit/mozapps/update/tests/chrome/utils.js
toolkit/mozapps/update/tests/data/shared.js
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -749,19 +749,38 @@ var gDownloadingPage = {
     this._pauseButton.disabled = true;
 
     var aus = CoC["@mozilla.org/updates/update-service;1"].
               getService(CoI.nsIApplicationUpdateService);
 
     var um = CoC["@mozilla.org/updates/update-manager;1"].
              getService(CoI.nsIUpdateManager);
     var activeUpdate = um.activeUpdate;
-    if (activeUpdate)
+    if (activeUpdate) {
       gUpdates.setUpdate(activeUpdate);
 
+      // It's possible the update has already been downloaded and is being
+      // applied by the time this page is shown, depending on how fast the
+      // download goes and how quickly the 'next' button is clicked to get here.
+      if (activeUpdate.state == STATE_PENDING ||
+          activeUpdate.state == STATE_PENDING_ELEVATE ||
+          activeUpdate.state == STATE_PENDING_SERVICE) {
+        if (!activeUpdate.getProperty("stagingFailed")) {
+          gUpdates.setButtons("hideButton", null, null, false);
+          gUpdates.wiz.getButton("extra1").focus();
+
+          this._setUpdateApplying();
+          return;
+        }
+
+        gUpdates.wiz.goTo("finished");
+        return;
+      }
+    }
+
     if (!gUpdates.update) {
       LOG("gDownloadingPage", "onPageShow - no valid update to download?!");
       return;
     }
 
     this._startTime = Date.now();
 
     try {
@@ -1201,20 +1220,27 @@ var gErrorPatchingPage = {
   onPageShow() {
     gUpdates.setButtons(null, null, "okButton", true);
   },
 
   onWizardNext() {
     switch (gUpdates.update.selectedPatch.state) {
       case STATE_APPLIED:
       case STATE_APPLIED_SERVICE:
+        gUpdates.wiz.goTo("finished");
+        break;
       case STATE_PENDING:
       case STATE_PENDING_SERVICE:
-        gUpdates.wiz.goTo("finished");
-        break;
+        let aus = CoC["@mozilla.org/updates/update-service;1"].
+                  getService(CoI.nsIApplicationUpdateService);
+        if (!aus.canStageUpdates) {
+          gUpdates.wiz.goTo("finished");
+          break;
+        }
+      // intentional fallthrough
       case STATE_DOWNLOADING:
         gUpdates.wiz.goTo("downloading");
         break;
       case STATE_DOWNLOAD_FAILED:
         gUpdates.wiz.goTo("errors");
         break;
     }
   }
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -3112,16 +3112,19 @@ UpdateManager.prototype = {
     // update is downloaded. By passing false the patch directory won't be
     // removed.
     cleanUpUpdatesDir(false);
 
     if (update.state == STATE_FAILED && parts[1]) {
       if (!handleUpdateFailure(update, parts[1])) {
         handleFallbackToCompleteUpdate(update, true);
       }
+
+      update.QueryInterface(Ci.nsIWritablePropertyBag);
+      update.setProperty("stagingFailed", "true");
     }
     if (update.state == STATE_APPLIED && shouldUseService()) {
       writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SERVICE);
     }
 
     // Send an observer notification which the update wizard uses in
     // order to update its UI.
     LOG("UpdateManager:refreshUpdateStatus - Notifying observers that " +
--- a/toolkit/mozapps/update/tests/chrome/chrome.ini
+++ b/toolkit/mozapps/update/tests/chrome/chrome.ini
@@ -24,16 +24,19 @@ reason = Bug 1168003
 [test_0071_notify_verifyFailPartial_noComplete.xul]
 [test_0072_notify_verifyFailComplete_noPartial.xul]
 [test_0073_notify_verifyFailPartialComplete.xul]
 [test_0074_notify_verifyFailPartial_successComplete.xul]
 [test_0081_error_patchApplyFailure_partial_only.xul]
 [test_0082_error_patchApplyFailure_complete_only.xul]
 [test_0083_error_patchApplyFailure_partial_complete.xul]
 [test_0084_error_patchApplyFailure_verify_failed.xul]
+[test_0085_error_patchApplyFailure_partial_complete_staging.xul]
+skip-if = asan
+reason = Bug 1168003
 [test_0092_finishedBackground.xul]
 [test_0093_restartNotification.xul]
 [test_0094_restartNotification_remote.xul]
 [test_0095_restartNotification_remoteInvalidNumber.xul]
 [test_0096_restartNotification_stagedBackground.xul]
 skip-if = asan
 reason = Bug 1168003
 [test_0097_restartNotification_stagedServiceBackground.xul]
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0085_error_patchApplyFailure_partial_complete_staging.xul
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: error patching, download with staging, and finished (partial failed and download complete), with fast MAR download"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="runTestDefault();">
+<script type="application/javascript"
+        src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+        src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+// This test forces the download to complete before the "next" button on the
+// errorpatching wizard page is clicked. This is done by creating the continue
+// file when the wizard loads to start the download, then clicking the "next"
+// button in the download's onStopRequest event listener.
+
+const testDownloadListener = {
+  onStartRequest(aRequest, aContext) { },
+
+  onProgress(aRequest, aContext, aProgress, aMaxProgress) { },
+
+  onStatus(aRequest, aContext, aStatus, aStatusText) { },
+
+  onStopRequest(aRequest, aContext, aStatus) {
+    debugDump("clicking errorpatching page next button");
+    gDocElem.getButton("next").click();
+    gAUS.removeDownloadListener(this);
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
+                                         Ci.nsIProgressEventSink])
+};
+
+let TESTS = [ {
+  pageid: PAGEID_ERROR_PATCHING,
+  extraCheckFunction: createContinueFile
+}, {
+  pageid: PAGEID_DOWNLOADING
+}, {
+  pageid: PAGEID_FINISHED,
+  buttonClick: "extra1",
+  extraStartFunction: removeContinueFile
+} ];
+
+gUseTestUpdater = true;
+
+function runTest() {
+  debugDump("entering");
+
+  removeContinueFile();
+
+  // Specify the url to update.sjs with a slowDownloadMar param so the ui can
+  // load before the download completes.
+  let slowDownloadURL = URL_HTTP_UPDATE_XML + "?slowDownloadMar=1";
+  let patches = getLocalPatchString("partial", null, null, null, null, null,
+                                    STATE_PENDING) +
+                getLocalPatchString("complete", slowDownloadURL, null, null,
+                                    null, "false");
+  let updates = getLocalUpdateString(patches, null, null, null,
+                                     Services.appinfo.version, null,
+                                     null, null, null, null, "false");
+
+  writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+  writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+  writeStatusFile(STATE_FAILED_READ_ERROR);
+
+  Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
+
+  reloadUpdateManagerData();
+
+  testPostUpdateProcessing();
+
+  gAUS.addDownloadListener(testDownloadListener);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test"></pre>
+</body>
+</window>
--- a/toolkit/mozapps/update/tests/chrome/utils.js
+++ b/toolkit/mozapps/update/tests/chrome/utils.js
@@ -808,16 +808,17 @@ function setupPrefs() {
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_STAGING_ENABLED)) {
     gAppUpdateStagingEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED);
   }
   Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false);
 
   Services.prefs.setIntPref(PREF_APP_UPDATE_IDLETIME, 0);
   Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 0);
   Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, false);
+  Services.prefs.setIntPref(PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL, 0);
 }
 
 /**
  * Restores files that were backed up for the tests and general file cleanup.
  */
 function resetFiles() {
   // Restore the backed up updater-settings.ini if it exists.
   let baseAppDir = getGREDir();
@@ -901,16 +902,20 @@ function resetPrefs() {
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) {
     Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
   }
 
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDMAXERRORS)) {
     Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDMAXERRORS);
   }
 
+  if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL)) {
+    Services.prefs.clearUserPref(PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL);
+  }
+
   try {
     Services.prefs.deleteBranch(PREFBRANCH_APP_UPDATE_NEVER);
   } catch (e) {
   }
 }
 
 function setupTimer(aTestTimeout) {
   gTestTimeout = aTestTimeout;
--- a/toolkit/mozapps/update/tests/data/shared.js
+++ b/toolkit/mozapps/update/tests/data/shared.js
@@ -7,16 +7,17 @@
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const PREF_APP_UPDATE_AUTO                 = "app.update.auto";
 const PREF_APP_UPDATE_BACKGROUNDERRORS     = "app.update.backgroundErrors";
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS  = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CHANNEL              = "app.update.channel";
+const PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL = "app.update.download.backgroundInterval";
 const PREF_APP_UPDATE_ENABLED              = "app.update.enabled";
 const PREF_APP_UPDATE_IDLETIME             = "app.update.idletime";
 const PREF_APP_UPDATE_LOG                  = "app.update.log";
 const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED  = "app.update.notifiedUnsupported";
 const PREF_APP_UPDATE_PROMPTWAITTIME       = "app.update.promptWaitTime";
 const PREF_APP_UPDATE_RETRYTIMEOUT         = "app.update.socket.retryTimeout";
 const PREF_APP_UPDATE_SERVICE_ENABLED      = "app.update.service.enabled";
 const PREF_APP_UPDATE_SILENT               = "app.update.silent";