Bug 1171792 - An update is attempted from an update notification when it is not possible to update. r=spohl
authorRobert Strong <robert.bugzilla@gmail.com>
Mon, 08 Jun 2015 13:11:43 -0700
changeset 248040 51056edec2f3ac5736c722237d42d2aacd184d01
parent 248039 df5ab244894468ab455c856ad04a10b4aa6ece30
child 248041 779e05d8125c3f4e519c542c44aafd2cd3d77bd1
push id60888
push userkwierso@gmail.com
push dateThu, 11 Jun 2015 01:38:38 +0000
treeherdermozilla-inbound@39e638ed06bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersspohl
bugs1171792
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1171792 - An update is attempted from an update notification when it is not possible to update. r=spohl
toolkit/mozapps/update/content/updates.js
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/tests/chrome/chrome.ini
toolkit/mozapps/update/tests/chrome/test_0171_check_noPerms_manual.xul
toolkit/mozapps/update/tests/chrome/test_0172_notify_noPerms_manual.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
@@ -439,27 +439,35 @@ var gUpdates = {
               aCallback("errorpatching");
               return;
             case STATE_DOWNLOAD_FAILED:
             case STATE_APPLYING:
               aCallback("errors");
               return;
           }
         }
-        if (this.update.licenseURL)
+
+        let aus = CoC["@mozilla.org/updates/update-service;1"].
+                  getService(CoI.nsIApplicationUpdateService);
+        if (!aus.canApplyUpdates) {
+          aCallback("manualUpdate");
+          return;
+        }
+
+        if (this.update.licenseURL) {
           this.wiz.getPageById(this.updatesFoundPageId).setAttribute("next", "license");
+        }
 
         var self = this;
         this.getShouldCheckAddonCompatibility(function(shouldCheck) {
           if (shouldCheck) {
             var incompatCheckPage = document.getElementById("incompatibleCheck");
             incompatCheckPage.setAttribute("next", self.updatesFoundPageId);
             aCallback(incompatCheckPage.id);
-          }
-          else {
+          } else {
             aCallback(self.updatesFoundPageId);
           }
         });
         return;
       }
       else if (arg0 == "installed") {
         aCallback("installed");
         return;
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -490,44 +490,44 @@ function hasUpdateMutex() {
     return true;
   }
   if (!gUpdateMutexHandle) {
     gUpdateMutexHandle = createMutex(getPerInstallationMutexName(true), false);
   }
   return !!gUpdateMutexHandle;
 }
 
-XPCOMUtils.defineLazyGetter(this, "gCanApplyUpdates", function aus_gCanApplyUpdates() {
+function getCanApplyUpdates() {
   let useService = false;
   if (shouldUseService()) {
     // No need to perform directory write checks, the maintenance service will
     // be able to write to all directories.
-    LOG("gCanApplyUpdates - bypass the write checks because we'll use the service");
+    LOG("getCanApplyUpdates - bypass the write checks because we'll use the service");
     useService = true;
   }
 
   if (!useService) {
     try {
       let updateTestFile = getUpdateFile([FILE_PERMS_TEST]);
-      LOG("gCanApplyUpdates - testing write access " + updateTestFile.path);
+      LOG("getCanApplyUpdates - testing write access " + updateTestFile.path);
       testWriteAccess(updateTestFile, false);
       if (AppConstants.platform == "macosx") {
         // Check that the application bundle can be written to.
         let appDirTestFile = getAppBaseDir();
         appDirTestFile.append(FILE_PERMS_TEST);
-        LOG("gCanApplyUpdates - testing write access " + appDirTestFile.path);
+        LOG("getCanApplyUpdates - testing write access " + appDirTestFile.path);
         if (appDirTestFile.exists()) {
           appDirTestFile.remove(false);
         }
         appDirTestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
         appDirTestFile.remove(false);
       } else if (AppConstants.platform == "win") {
         // Example windowsVersion:  Windows XP == 5.1
         let windowsVersion = Services.sysinfo.getProperty("version");
-        LOG("gCanApplyUpdates - windowsVersion = " + windowsVersion);
+        LOG("getCanApplyUpdates - windowsVersion = " + windowsVersion);
 
       /**
        * For Vista, updates can be performed to a location requiring admin
        * privileges by requesting elevation via the UAC prompt when launching
        * updater.exe if the appDir is under the Program Files directory
        * (e.g. C:\Program Files\) and UAC is turned on and  we can elevate
        * (e.g. user has a split token).
        *
@@ -540,23 +540,23 @@ XPCOMUtils.defineLazyGetter(this, "gCanA
         if (parseFloat(windowsVersion) >= 6) {
           try {
             // KEY_UPDROOT will fail and throw an exception if
             // appDir is not under the Program Files, so we rely on that
             let dir = Services.dirsvc.get(KEY_UPDROOT, Ci.nsIFile);
             // appDir is under Program Files, so check if the user can elevate
             userCanElevate = Services.appinfo.QueryInterface(Ci.nsIWinAppHelper).
                              userCanElevate;
-            LOG("gCanApplyUpdates - on Vista, userCanElevate: " + userCanElevate);
+            LOG("getCanApplyUpdates - on Vista, userCanElevate: " + userCanElevate);
           }
           catch (ex) {
             // When the installation directory is not under Program Files,
             // fall through to checking if write access to the
             // installation directory is available.
-            LOG("gCanApplyUpdates - on Vista, appDir is not under Program Files");
+            LOG("getCanApplyUpdates - on Vista, appDir is not under Program Files");
           }
         }
 
         /**
          * On Windows, we no longer store the update under the app dir.
          *
          * If we are on Windows (including Vista, if we can't elevate) we need to
          * to check that we can create and remove files from the actual app
@@ -574,34 +574,34 @@ XPCOMUtils.defineLazyGetter(this, "gCanA
          *    (e.g. the user does not have a split token)
          * 4) UAC is turned on and the user is already elevated, so they can't be
          *    elevated again
          */
         if (!userCanElevate) {
           // if we're unable to create the test file this will throw an exception.
           let appDirTestFile = getAppBaseDir();
           appDirTestFile.append(FILE_PERMS_TEST);
-          LOG("gCanApplyUpdates - testing write access " + appDirTestFile.path);
+          LOG("getCanApplyUpdates - testing write access " + appDirTestFile.path);
           if (appDirTestFile.exists()) {
             appDirTestFile.remove(false);
           }
           appDirTestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
           appDirTestFile.remove(false);
         }
       }
     } catch (e) {
-       LOG("gCanApplyUpdates - unable to apply updates. Exception: " + e);
+       LOG("getCanApplyUpdates - unable to apply updates. Exception: " + e);
       // No write privileges to install directory
       return false;
     }
   } // if (!useService)
 
-  LOG("gCanApplyUpdates - able to apply updates");
+  LOG("getCanApplyUpdates - able to apply updates");
   return true;
-});
+}
 
 /**
  * Whether or not the application can stage an update for the current session.
  * These checks are only performed once per session due to using a lazy getter.
  *
  * @return true if updates can be staged for this session.
  */
 XPCOMUtils.defineLazyGetter(this, "gCanStageUpdatesSession",
@@ -2391,17 +2391,17 @@ UpdateService.prototype = {
     // UPDATE_PING_COUNT_NOTIFY
     AUSTLMY.pingGeneric("UPDATE_PING_COUNT_" + this._pingSuffix,
                         true, false);
 
     // Histogram IDs:
     // UPDATE_UNABLE_TO_APPLY_EXTERNAL
     // UPDATE_UNABLE_TO_APPLY_NOTIFY
     AUSTLMY.pingGeneric("UPDATE_UNABLE_TO_APPLY_" + this._pingSuffix,
-                        gCanApplyUpdates, true);
+                        getCanApplyUpdates(), true);
     // Histogram IDs:
     // UPDATE_CANNOT_STAGE_EXTERNAL
     // UPDATE_CANNOT_STAGE_NOTIFY
     AUSTLMY.pingGeneric("UPDATE_CANNOT_STAGE_" + this._pingSuffix,
                         getCanStageUpdates(), true);
     // Histogram IDs:
     // UPDATE_INVALID_LASTUPDATETIME_EXTERNAL
     // UPDATE_INVALID_LASTUPDATETIME_NOTIFY
@@ -2641,17 +2641,17 @@ UpdateService.prototype = {
         LOG("UpdateService:_selectAndInstallUpdate - notifying that the " +
             "update is not supported for this system");
         this._showPrompt(update);
       }
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNSUPPORTED);
       return;
     }
 
-    if (!gCanApplyUpdates) {
+    if (!getCanApplyUpdates()) {
       LOG("UpdateService:_selectAndInstallUpdate - the user is unable to " +
           "apply updates... prompting");
       this._showPrompt(update);
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNABLE_TO_APPLY);
       return;
     }
 
     /**
@@ -2851,17 +2851,17 @@ UpdateService.prototype = {
     this.onCompatibilityUpdateAvailable(addon);
   },
 
   onUpdateFinished: function(addon) {
     if (--this._updateCheckCount > 0) {
       return;
     }
 
-    if (this._incompatibleAddons.length > 0 || !gCanApplyUpdates) {
+    if (this._incompatibleAddons.length > 0 || !getCanApplyUpdates()) {
       LOG("UpdateService:onUpdateEnded - prompting because there are " +
           "incompatible add-ons");
       if (this._incompatibleAddons.length > 0) {
         AUSTLMY.pingCheckCode(this._pingSuffix,
                               AUSTLMY.CHK_ADDON_HAVE_INCOMPAT);
       } else {
         AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNABLE_TO_APPLY);
       }
@@ -2898,17 +2898,17 @@ UpdateService.prototype = {
   get canCheckForUpdates() {
     return gCanCheckForUpdates && hasUpdateMutex();
   },
 
   /**
    * See nsIUpdateService.idl
    */
   get canApplyUpdates() {
-    return gCanApplyUpdates && hasUpdateMutex();
+    return getCanApplyUpdates() && hasUpdateMutex();
   },
 
   /**
    * See nsIUpdateService.idl
    */
   get canStageUpdates() {
     return getCanStageUpdates();
   },
--- a/toolkit/mozapps/update/tests/chrome/chrome.ini
+++ b/toolkit/mozapps/update/tests/chrome/chrome.ini
@@ -82,11 +82,17 @@ reason = only Windows has the maintenanc
 [test_0123_check_allowNonBuiltinCert_noCertAttrsCheck.xul]
 [test_0131_check_invalidCertAttrs_noUpdate.xul]
 [test_0132_check_invalidCertAttrs_hasUpdate.xul]
 [test_0141_notify_invalidCertAttrs_noUpdate.xul]
 [test_0142_notify_invalidCertAttrs_hasUpdate.xul]
 [test_0151_notify_backgroundCheckError.xul]
 [test_0161_check_unsupported.xul]
 [test_0162_notify_unsupported.xul]
+[test_0171_check_noPerms_manual.xul]
+skip-if = os != 'win'
+reason = test must be able to prevent file deletion.
+[test_0172_notify_noPerms_manual.xul]
+skip-if = os != 'win'
+reason = test must be able to prevent file deletion.
 [test_0900_deprecatedUpdateFormat_minor.xul]
 [test_0901_deprecatedUpdateFormat_major.xul]
 [test_9999_cleanup.xul]
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0171_check_noPerms_manual.xul
@@ -0,0 +1,64 @@
+<?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: manual"
+        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[
+
+const TESTS = [ {
+  pageid: PAGEID_CHECKING
+}, {
+  pageid: PAGEID_MANUAL_UPDATE,
+  buttonClick: "finish",
+  extraCheckFunction: getWriteTestFile
+} ];
+
+function runTest() {
+  debugDump("entering");
+
+  let file = getWriteTestFile();
+  file.create(file.NORMAL_FILE_TYPE, 0o444);
+  file.fileAttributesWin |= file.WFA_READONLY;
+  file.fileAttributesWin &= ~file.WFA_READWRITE;
+
+  let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
+  setUpdateURLOverride(url);
+
+  gUP.checkForUpdates();
+}
+
+function getWriteTestFile() {
+  let file = getAppBaseDir();
+  file.append(FILE_PERMS_TEST);
+  file.QueryInterface(Ci.nsILocalFileWin);
+  if (file.exists()) {
+    file.fileAttributesWin |= file.WFA_READWRITE;
+    file.fileAttributesWin &= ~file.WFA_READONLY;
+    file.remove(true);
+  }
+  return file;
+}
+
+]]>
+</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>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0172_notify_noPerms_manual.xul
@@ -0,0 +1,63 @@
+<?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: manual"
+        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[
+
+const TESTS = [ {
+  pageid: PAGEID_MANUAL_UPDATE,
+  buttonClick: "finish",
+  extraCheckFunction: getWriteTestFile
+} ];
+
+function runTest() {
+  debugDump("entering");
+
+  let file = getWriteTestFile();
+  file.create(file.NORMAL_FILE_TYPE, 0o444);
+  file.fileAttributesWin |= file.WFA_READONLY;
+  file.fileAttributesWin &= ~file.WFA_READWRITE;
+
+  let url = URL_HTTP_UPDATE_XML + "?showDetails=1&showPrompt=1" +
+            getVersionParams();
+  setUpdateURLOverride(url);
+
+  gAUS.checkForBackgroundUpdates();
+}
+
+function getWriteTestFile() {
+  let file = getAppBaseDir();
+  file.append(FILE_PERMS_TEST);
+  file.QueryInterface(Ci.nsILocalFileWin);
+  if (file.exists()) {
+    file.fileAttributesWin |= file.WFA_READWRITE;
+    file.fileAttributesWin &= ~file.WFA_READONLY;
+    file.remove(true);
+  }
+  return file;
+}
+
+]]>
+</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
@@ -8,17 +8,17 @@
  * Most tests can use an array named TESTS that will perform most if not all of
  * the necessary checks. Each element in the array must be an object with the
  * following possible properties. Additional properties besides the ones listed
  * below can be added as needed.
  *
  * overrideCallback (optional)
  *   The function to call for the next test. This is typically called when the
  *   wizard page changes but can also be called for other events by the previous
- *   test. If this property isn't defined then the defailtCallback function will
+ *   test. If this property isn't defined then the defaultCallback function will
  *   be called. If this property is defined then all other properties are
  *   optional.
  *
  * pageid (required unless overrideCallback is specified)
  *   The expected pageid for the wizard. This property is required unless the
  *   overrideCallback property is defined.
  *
  * extraStartFunction (optional)
@@ -132,17 +132,17 @@ const IS_MACOSX = ("nsILocalFileMac" in 
 const IS_WIN = ("@mozilla.org/windows-registry-key;1" in Cc);
 
 // The tests have to use the pageid instead of the pageIndex due to the
 // app update wizard's access method being random.
 const PAGEID_DUMMY            = "dummy";                 // Done
 const PAGEID_CHECKING         = "checking";              // Done
 const PAGEID_PLUGIN_UPDATES   = "pluginupdatesfound";
 const PAGEID_NO_UPDATES_FOUND = "noupdatesfound";        // Done
-const PAGEID_MANUAL_UPDATE    = "manualUpdate"; // Tested on license load failure
+const PAGEID_MANUAL_UPDATE    = "manualUpdate";          // Done
 const PAGEID_UNSUPPORTED      = "unsupported";           // Done
 const PAGEID_INCOMPAT_CHECK   = "incompatibleCheck";     // Done
 const PAGEID_FOUND_BASIC      = "updatesfoundbasic";     // Done
 const PAGEID_FOUND_BILLBOARD  = "updatesfoundbillboard"; // Done
 const PAGEID_LICENSE          = "license";               // Done
 const PAGEID_INCOMPAT_LIST    = "incompatibleList";      // Done
 const PAGEID_DOWNLOADING      = "downloading";           // Done
 const PAGEID_ERRORS           = "errors";                // Done
@@ -1272,17 +1272,17 @@ function setupAddons(aCallback) {
           if (aAddon.userDisabled != gDisableUpdateVersionAddon) {
             aAddon.userDisabled = gDisableUpdateVersionAddon;
           }
         }
       });
       // Start the timout timer before the update window is displayed so it can
       // clean up tests that don't successfully display the update window.
       setupTimer(gTestTimeout);
-      aCallback();
+      SimpleTest.executeSoon(aCallback);
     });
   }
 
   // If the app.update.test.disabledAddons preference exists the pre-existing
   // add-ons have already been disabled so they don't interfere with the tests,
   // the test add-ons have already been installed, and the only thing that needs
   // to be done is setting the appropriate userDisabled value for the noupdate
   // test add-ons.
@@ -1384,17 +1384,17 @@ function resetAddons(aCallback) {
       let disabledAddons = Services.prefs.getCharPref(PREF_DISABLEDADDONS).split(" ");
       Services.prefs.clearUserPref(PREF_DISABLEDADDONS);
       AddonManager.getAllAddons(function(aAddons) {
         aAddons.forEach(function(aAddon) {
           if (disabledAddons.indexOf(aAddon.id)) {
             aAddon.userDisabled = false;
           }
         });
-        aCallback();
+        SimpleTest.executeSoon(aCallback);
       });
     }
   }
 
   let listener = {
     onUninstalled: uninstallCompleted
   };
 
--- a/toolkit/mozapps/update/tests/data/shared.js
+++ b/toolkit/mozapps/update/tests/data/shared.js
@@ -56,16 +56,17 @@ const WRITE_ERROR = 7;
 const DIR_PATCH        = "0";
 const DIR_TOBEDELETED  = "tobedeleted";
 const DIR_UPDATES      = "updates";
 const DIR_UPDATED      = IS_MACOSX ? "Updated.app" : "updated";
 
 const FILE_APPLICATION_INI           = "application.ini";
 const FILE_BACKUP_LOG                = "backup-update.log";
 const FILE_LAST_LOG                  = "last-update.log";
+const FILE_PERMS_TEST                = "update.test";
 const FILE_UPDATER_INI               = "updater.ini";
 const FILE_UPDATES_DB                = "updates.xml";
 const FILE_UPDATE_ACTIVE             = "active-update.xml";
 const FILE_UPDATE_ARCHIVE            = "update.mar";
 const FILE_UPDATE_LOG                = "update.log";
 const FILE_UPDATE_SETTINGS_INI       = "update-settings.ini";
 const FILE_UPDATE_SETTINGS_INI_BAK   = "update-settings.ini.bak";
 const FILE_UPDATE_STATUS             = "update.status";