Bug 1171792 - An update is attempted from an update notification when it is not possible to update. r=spohl, a=lhenry
authorRobert Strong <robert.bugzilla@gmail.com>
Wed, 10 Jun 2015 19:37:13 -0700
changeset 266233 038ab776f75f
parent 266232 a8acd56b1734
child 266234 ab9bfc1f6f00
push id4795
push userrstrong@mozilla.com
push date2015-06-11 02:37 +0000
treeherdermozilla-beta@038ab776f75f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersspohl, lhenry
bugs1171792
milestone39.0
Bug 1171792 - An update is attempted from an update notification when it is not possible to update. r=spohl, a=lhenry
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
@@ -515,47 +515,47 @@ function hasUpdateMutex() {
   }
 
   return !!gUpdateMutexHandle;
 #else
   return true;
 #endif // XP_WIN
 }
 
-XPCOMUtils.defineLazyGetter(this, "gCanApplyUpdates", function aus_gCanApplyUpdates() {
+function getCanApplyUpdates() {
   let useService = false;
   if (shouldUseService() && isServiceInstalled()) {
     // 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 {
       var updateTestFile = getUpdateFile([FILE_PERMS_TEST]);
-      LOG("gCanApplyUpdates - testing write access " + updateTestFile.path);
+      LOG("getCanApplyUpdates - testing write access " + updateTestFile.path);
       testWriteAccess(updateTestFile, false);
 #ifdef XP_MACOSX
       // Check that the application bundle can be written to.
       var 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);
 #elifdef XP_WIN
       var sysInfo = Cc["@mozilla.org/system-info;1"].
                     getService(Ci.nsIPropertyBag2);
 
       // Example windowsVersion:  Windows XP == 5.1
       var windowsVersion = 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).
      *
@@ -570,23 +570,23 @@ XPCOMUtils.defineLazyGetter(this, "gCanA
           var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
                             getService(Ci.nsIProperties);
           // KEY_UPDROOT will fail and throw an exception if
           // appDir is not under the Program Files, so we rely on that
           var dir = fileLocator.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
@@ -604,34 +604,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.
         var 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);
       }
 #endif //XP_WIN
     }
     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.
  *
  * @return true if updates can be staged.
  */
 function getCanStageUpdates() {
   // If background updates are disabled, then just bail out!
@@ -2414,17 +2414,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
@@ -2661,17 +2661,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;
     }
 
     /**
@@ -2870,17 +2870,17 @@ UpdateService.prototype = {
     // Compatibility or new version updates mean the same thing here.
     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);
       }
@@ -2918,17 +2918,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
@@ -77,11 +77,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)
@@ -131,17 +131,17 @@ Cu.import("resource://gre/modules/Servic
 const IS_MACOSX = ("nsILocalFileMac" in Ci);
 
 // 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
@@ -1157,17 +1157,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.
@@ -1269,17 +1269,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
@@ -57,16 +57,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";