Merge mozilla-central into mozilla-inbound
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 14 Jun 2012 09:37:35 -0400
changeset 96691 3d4982f328376e18f1e6a3e392cfca1fcbd5124a
parent 96682 bc082ae74492aa5935484d4873f4c2645bf0629f (current diff)
parent 96690 983b91e5aa1715332f78d09a6035382d968c03d2 (diff)
child 96692 bf12b1b12b2d307b04924c5b24f4a0fe348ee007
push id10660
push usereakhgari@mozilla.com
push dateThu, 14 Jun 2012 13:37:41 +0000
treeherdermozilla-inbound@3d4982f32837 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone16.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
Merge mozilla-central into mozilla-inbound
--- a/browser/config/mozconfigs/linux32/debug
+++ b/browser/config/mozconfigs/linux32/debug
@@ -1,11 +1,12 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
+ENABLE_MARIONETTE=1
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
--- a/browser/config/mozconfigs/linux64/debug
+++ b/browser/config/mozconfigs/linux64/debug
@@ -1,11 +1,12 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
+ENABLE_MARIONETTE=1
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
--- a/browser/config/mozconfigs/macosx32/debug
+++ b/browser/config/mozconfigs/macosx32/debug
@@ -1,12 +1,13 @@
 . $topsrcdir/build/macosx/mozconfig.leopard
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
+ENABLE_MARIONETTE=1
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j12"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -1,14 +1,15 @@
 . $topsrcdir/build/macosx/common
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-accessibility
 ac_add_options --enable-signmar
+ENABLE_MARIONETTE=1
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j12"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -1,11 +1,12 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
+ENABLE_MARIONETTE=1
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2010-win64
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -1,14 +1,15 @@
 ac_add_options --target=x86_64-pc-mingw32
 ac_add_options --host=x86_64-pc-mingw32
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
+ENABLE_MARIONETTE=1
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -151,8 +151,12 @@ ifdef MOZ_PKG_MANIFEST_P
 	-diff -u $(DIST)/pack-list.txt $(DIST)/bin-list.txt
 	rm -f $(DIST)/pack-list.txt $(DIST)/bin-list.txt
 endif
 
 installer:: removed-files
 ifdef INSTALLER_DIR
 	$(MAKE) -C $(INSTALLER_DIR)
 endif
+
+ifdef ENABLE_MARIONETTE
+DEFINES += -DENABLE_MARIONETTE=1
+endif
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -461,16 +461,22 @@
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
 
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
+#ifdef ENABLE_MARIONETTE
+@BINPATH@/chrome/marionette@JAREXT@
+@BINPATH@/chrome/marionette.manifest
+@BINPATH@/components/MarionetteComponents.manifest
+@BINPATH@/components/marionettecomponent.js
+#endif
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 #ifdef MOZ_SAFE_BROWSING
 @BINPATH@/components/nsSafebrowsingApplication.manifest
 @BINPATH@/components/nsSafebrowsingApplication.js
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -764,28 +764,32 @@ function cleanUpUpdatesDir(aBackgroundUp
             logFile.moveTo(dir, FILE_BACKUP_LOG);
           }
           catch (e) {
             LOG("cleanUpUpdatesDir - failed to rename file " + logFile.path +
                 " to " + FILE_BACKUP_LOG);
           }
         }
         f.moveTo(dir, FILE_LAST_LOG);
-        continue;
+        if (aBackgroundUpdate) {
+          // We're not going to delete any files, so we can just
+          // bail out of the loop right now.
+          break;
+        } else {
+          continue;
+        }
       }
       catch (e) {
         LOG("cleanUpUpdatesDir - failed to move file " + f.path + " to " +
             dir.path + " and rename it to " + FILE_LAST_LOG);
       }
-    } else if (f.leafName == FILE_UPDATE_STATUS && aBackgroundUpdate) {
-      // Leave the update.status file alone when a background update
-      // has been performed.  We don't remove this file here because
-      // after the application directory gets replaced by the staged
-      // update, this will end up being the update.status file which
-      // represents the status of the update performed.
+    } else if (aBackgroundUpdate) {
+      // Don't delete any files when an update has been staged, as
+      // we need to keep them around in case we would have to fall
+      // back to applying the update on application restart.
       continue;
     }
     // Now, recursively remove this file.  The recursive removal is really
     // only needed on Mac OSX because this directory will contain a copy of
     // updater.app, which is itself a directory.
     try {
       f.remove(true);
     }
@@ -1005,31 +1009,32 @@ function handleUpdateFailure(update, err
   
   return false;
 }
 
 /**
  * Fall back to downloading a complete update in case an update has failed.
  *
  * @param update the update object that has failed to apply.
+ * @param postStaging true if we have just attempted to stage an update.
  */
-function handleFallbackToCompleteUpdate(update) {
+function handleFallbackToCompleteUpdate(update, postStaging) {
   cleanupActiveUpdate();
 
   update.statusText = gUpdateBundle.GetStringFromName("patchApplyFailure");
   var oldType = update.selectedPatch ? update.selectedPatch.type
                                      : "complete";
   if (update.selectedPatch && oldType == "partial" && update.patchCount == 2) {
     // Partial patch application failed, try downloading the complete
     // update in the background instead.
     LOG("UpdateService:_postUpdateProcessing - install of partial patch " +
         "failed, downloading complete patch");
     var status = Cc["@mozilla.org/updates/update-service;1"].
                  getService(Ci.nsIApplicationUpdateService).
-                 downloadUpdate(update, true);
+                 downloadUpdate(update, !postStaging);
     if (status == STATE_NONE)
       cleanupActiveUpdate();
   }
   else {
     LOG("handleFallbackToCompleteUpdate - install of complete or " +
         "only one patch offered failed.");
   }
   update.QueryInterface(Ci.nsIWritablePropertyBag);
@@ -1612,17 +1617,17 @@ UpdateService.prototype = {
       update.state = ary[0];
       if (update.state == STATE_FAILED && ary[1]) {
         if (handleUpdateFailure(update, ary[1])) {
           return;
         }
       }
 
       // Something went wrong with the patch application process.
-      handleFallbackToCompleteUpdate(update);
+      handleFallbackToCompleteUpdate(update, false);
 
       prompter.showUpdateError(update);
     }
   },
 
   /**
    * Submit the results of applying the update via telemetry.
    *
@@ -2427,17 +2432,17 @@ UpdateManager.prototype = {
   refreshUpdateStatus: function UM_refreshUpdateStatus(update) {
     var updateSucceeded = true;
     var status = readStatusFile(getUpdatesDir());
     var ary = status.split(":");
     update.state = ary[0];
     if (update.state == STATE_FAILED && ary[1]) {
       updateSucceeded = false;
       if (!handleUpdateFailure(update, ary[1])) {
-        handleFallbackToCompleteUpdate(update);
+        handleFallbackToCompleteUpdate(update, true);
       }
     }
     if (update.state == STATE_APPLIED && shouldUseService()) {
       writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SVC);
     }
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     um.saveUpdates();
--- a/toolkit/mozapps/update/test/TestAUSHelper.cpp
+++ b/toolkit/mozapps/update/test/TestAUSHelper.cpp
@@ -43,16 +43,17 @@
 # define NS_tsnprintf snprintf
 # define NS_taccess access
 # define NS_tchdir chdir
 # define NS_tfopen fopen
 # define NS_tstrcmp strcmp
 # define NS_ttoi atoi
 # define NS_tstat stat
 # define NS_tgetcwd getcwd
+# define NS_tfputs fputs
 # define LOG_S "%s"
 #endif
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -166,16 +167,19 @@ int NS_main(int argc, NS_tchar **argv)
   if (argc < 3) {
     fprintf(stderr, \
             "\n" \
             "Application Update Service Test Helper\n" \
             "\n" \
             "Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n"  \
             "   or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n" \
             "   or: signature-check filepath\n" \
+            "   or: setup-symlink dir1 dir2 file symlink\n" \
+            "   or: remove-symlink dir1 dir2 file symlink\n" \
+            "   or: check-symlink symlink\n" \
             "\n" \
             "  WORKINGDIR  \tThe relative path to the working directory to use.\n" \
             "  INFILE      \tThe relative path from the working directory for the file to\n" \
             "              \tread actions to perform such as finish.\n" \
             "  OUTFILE     \tThe relative path from the working directory for the file to\n" \
             "              \twrite status information.\n" \
             "  SECONDS     \tThe number of seconds to sleep.\n" \
             "  FILETOLOCK  \tThe relative path from the working directory to an existing\n" \
@@ -200,16 +204,78 @@ int NS_main(int argc, NS_tchar **argv)
       return 1;
     }
 #else 
     // Not implemented on non-Windows platforms
     return 1;
 #endif
   }
 
+  if (!NS_tstrcmp(argv[1], NS_T("setup-symlink"))) {
+#ifdef XP_UNIX
+    NS_tchar path[MAXPATHLEN];
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+    mkdir(path, 0755);
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
+    mkdir(path, 0755);
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
+    FILE * file = NS_tfopen(path, NS_T("w"));
+    if (file) {
+      NS_tfputs(NS_T("test"), file);
+      fclose(file);
+    }
+    symlink(path, argv[5]);
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+    if (argc > 6 && !NS_tstrcmp(argv[6], NS_T("change-perm"))) {
+      chmod(path, 0644);
+    }
+    return 0;
+#else
+    // Not implemented on non-Unix platforms
+    return 1;
+#endif
+  }
+
+  if (!NS_tstrcmp(argv[1], NS_T("remove-symlink"))) {
+#ifdef XP_UNIX
+    NS_tchar path[MAXPATHLEN];
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+    chmod(path, 0755);
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
+    unlink(path);
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
+    rmdir(path);
+    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+    rmdir(path);
+    return 0;
+#else
+    // Not implemented on non-Unix platforms
+    return 1;
+#endif
+  }
+
+  if (!NS_tstrcmp(argv[1], NS_T("check-symlink"))) {
+#ifdef XP_UNIX
+    struct stat ss;
+    lstat(argv[2], &ss);
+    return S_ISLNK(ss.st_mode) ? 0 : 1;
+#else
+    // Not implemented on non-Unix platforms
+    return 1;
+#endif
+  }
+
   if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) {
 #ifdef XP_WIN
     const int maxWaitSeconds = NS_ttoi(argv[3]);
     LPCWSTR serviceName = argv[2];
     DWORD serviceState = WaitForServiceStop(serviceName, maxWaitSeconds);
     if (SERVICE_STOPPED == serviceState) {
       return 0;
     } else {
--- a/toolkit/mozapps/update/test/unit/head_update.js.in
+++ b/toolkit/mozapps/update/test/unit/head_update.js.in
@@ -95,16 +95,17 @@ const ERR_BACKUP_DISCARD = "backup_disca
 
 const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result.";
 
 // variables are used instead of contants so tests can override these values
 var gCallbackBinFile = "callback_app" + BIN_SUFFIX;
 var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
 var gBackgroundUpdate = false;
 var gSwitchApp = false;
+var gDisableReplaceFallback = false;
 
 // Time to wait for the test helper process before continuing the test
 const TEST_HELPER_TIMEOUT = 1000;
 
 // Use a copy of the main application executable for the test to avoid main
 // executable in use errors.
 const FILE_WIN_TEST_EXE = "_aus_test_app.exe";
 
@@ -508,20 +509,31 @@ function runUpdate() {
     if (gSwitchApp) {
       args[2] = "0/replace";
     }
     args = args.concat([cwdPath, callbackAppPath]);
     args = args.concat(gCallbackArgs);
   }
   logTestInfo("Running the updater: " + updateBin.path + " " + args.join(" "));
 
+  let env = AUS_Cc["@mozilla.org/process/environment;1"].
+            getService(AUS_Ci.nsIEnvironment);
+  if (gDisableReplaceFallback) {
+    env.set("MOZ_NO_REPLACE_FALLBACK", "1");
+  }
+
   let process = AUS_Cc["@mozilla.org/process/util;1"].
                 createInstance(AUS_Ci.nsIProcess);
   process.init(updateBin);
   process.run(true, args, args.length);
+
+  if (gDisableReplaceFallback) {
+    env.set("MOZ_NO_REPLACE_FALLBACK", "");
+  }
+
   return process.exitValue;
 }
 
 let gServiceLaunchedCallbackLog = null;
 let gServiceLaunchedCallbackArgs = null;
 
 /**
  * Helper function to check whether the maintenance service updater tests should
--- a/toolkit/mozapps/update/test/unit/test_0113_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0113_general.js
@@ -9,17 +9,17 @@ const TEST_ID = "0113";
 // All we care about is that the last modified time has changed so that Mac OS
 // X Launch Services invalidates its cache so the test allows up to one minute
 // difference in the last modified time.
 const MAX_TIME_DIFFERENCE = 60000;
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
-const TEST_FILES = [
+var TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
   relPathDir       : "a/b/defaults/pref/",
   originalContents : "ShouldNotBeReplaced\n",
   compareContents  : "ShouldNotBeReplaced\n",
   originalFile     : null,
   compareFile      : null,
@@ -229,16 +229,49 @@ ADDITIONAL_TEST_DIRS = [
   relPathDir   : "a/b/2/20/",
   dirRemoved   : true
 }, {
   description  : "Removed by precomplete (rmdir)",
   relPathDir   : "a/b/2/",
   dirRemoved   : true
 }];
 
+function runHelperProcess(args) {
+  let helperBin = do_get_file(HELPER_BIN_FILE);
+  let process = AUS_Cc["@mozilla.org/process/util;1"].
+                createInstance(AUS_Ci.nsIProcess);
+  process.init(helperBin);
+  logTestInfo("Running " + helperBin.path + " " + args.join(" "));
+  process.run(true, args, args.length);
+  do_check_eq(process.exitValue, 0);
+}
+
+function createSymlink() {
+  let args = ["setup-symlink", "moz-foo", "moz-bar", "target",
+              getApplyDirFile().path + "/a/b/link"];
+  runHelperProcess(args);
+  args = ["setup-symlink", "moz-foo2", "moz-bar2", "target2",
+          getApplyDirFile().path + "/a/b/link2", "change-perm"];
+  runHelperProcess(args);
+}
+
+function removeSymlink() {
+  let args = ["remove-symlink", "moz-foo", "moz-bar", "target",
+              getApplyDirFile().path + "/a/b/link"];
+  runHelperProcess(args);
+  args = ["remove-symlink", "moz-foo2", "moz-bar2", "target2",
+          getApplyDirFile().path + "/a/b/link2"];
+  runHelperProcess(args);
+}
+
+function checkSymlink() {
+  let args = ["check-symlink", getApplyDirFile().path + "/a/b/link"];
+  runHelperProcess(args);
+}
+
 function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   gBackgroundUpdate = true;
   setupUpdaterTest(MAR_COMPLETE_FILE);
 
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
@@ -248,20 +281,41 @@ function run_test() {
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     applyToDir.lastModifiedTime = yesterday;
   }
 
+  if (IS_UNIX) {
+    removeSymlink();
+    createSymlink();
+    do_register_cleanup(removeSymlink);
+    TEST_FILES.push({
+      description      : "Readable symlink",
+      fileName         : "link",
+      relPathDir       : "a/b/",
+      originalContents : "test",
+      compareContents  : "test",
+      originalFile     : null,
+      compareFile      : null,
+      originalPerms    : 0664,
+      comparePerms     : 0664
+    });
+  }
+
   // apply the complete mar
   let exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for success when " +
               "applying a complete mar");
+  let updateLog = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX, true);
+  updateLog.append(FILE_UPDATE_LOG);
+  let updateLogContents = readFileBytes(updateLog);
+  logTestInfo(updateLogContents);
   do_check_eq(exitValue, 0);
 
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // For Mac OS X check that the last modified time for a directory has been
   // updated after a successful update (bug 600098).
@@ -303,18 +357,20 @@ function run_test() {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAX_TIME_DIFFERENCE);
   }
 
   checkFilesAfterUpdateSuccess();
-  // Sorting on Linux is different so skip this check for now.
-  if (!IS_UNIX) {
+  if (IS_UNIX) {
+    checkSymlink();
+  } else {
+    // Sorting on Linux is different so skip this check for now.
     checkUpdateLogContents(LOG_COMPLETE_SWITCH_SUCCESS);
   }
 
   // This shouldn't exist anyways in background updates, but let's make sure
   logTestInfo("testing tobedeleted directory doesn't exist");
   toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
--- a/toolkit/mozapps/update/test/unit/test_0161_appInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0161_appInUse_xp_win_complete.js
@@ -210,16 +210,17 @@ function doUpdate() {
 
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
copy from toolkit/mozapps/update/test/unit/test_0161_appInUse_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0162_appInUse_xp_win_complete.js
--- a/toolkit/mozapps/update/test/unit/test_0161_appInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0162_appInUse_xp_win_complete.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* Application in use complete MAR file background patch apply failure test */
+/* Application in use complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0161";
+const TEST_ID = "0162";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -219,21 +219,19 @@ function doUpdate() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  // The update status format for a failure is failed: # where # is the error
-  // code for the failure.
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
--- a/toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js
@@ -218,16 +218,17 @@ function doUpdate() {
 
   logTestInfo("testing update.status should be " + STATE_FAILED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js
@@ -219,16 +219,17 @@ function doUpdate() {
 
   logTestInfo("testing update.status should be " + STATE_FAILED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
copy from toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0174_fileLocked_xp_win_complete.js
--- a/toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0174_fileLocked_xp_win_complete.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File locked complete MAR file background patch apply failure test */
+/* File locked complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0172";
+const TEST_ID = "0174";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -227,21 +227,19 @@ function doUpdate() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  // The update status format for a failure is failed: # where # is the error
-  // code for the failure.
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js
copy to toolkit/mozapps/update/test/unit/test_0175_fileLocked_xp_win_partial.js
--- a/toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0175_fileLocked_xp_win_partial.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File locked partial MAR file background patch apply failure test */
+/* File locked partial MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0173";
+const TEST_ID = "0175";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -228,21 +228,19 @@ function doUpdate() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  // The update status format for a failure is failed: # where # is the error
-  // code for the failure.
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
--- a/toolkit/mozapps/update/test/unit/test_0184_fileInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0184_fileInUse_xp_win_complete.js
@@ -217,16 +217,17 @@ function doUpdate() {
 
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test/unit/test_0185_fileInUse_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0185_fileInUse_xp_win_partial.js
@@ -220,16 +220,17 @@ function doUpdate() {
 
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test/unit/test_0186_rmrfdirFileInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0186_rmrfdirFileInUse_xp_win_complete.js
@@ -226,16 +226,17 @@ function doUpdate() {
 
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test/unit/test_0187_rmrfdirFileInUse_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0187_rmrfdirFileInUse_xp_win_partial.js
@@ -267,16 +267,17 @@ function doUpdate() {
 
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
copy from toolkit/mozapps/update/test/unit/test_0184_fileInUse_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0188_fileInUse_xp_win_complete.js
--- a/toolkit/mozapps/update/test/unit/test_0184_fileInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0188_fileInUse_xp_win_complete.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use complete MAR file background patch apply success test */
+/* File in use complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0184";
+const TEST_ID = "0188";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -226,19 +226,19 @@ function doUpdate() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test/unit/test_0185_fileInUse_xp_win_partial.js
copy to toolkit/mozapps/update/test/unit/test_0189_fileInUse_xp_win_partial.js
--- a/toolkit/mozapps/update/test/unit/test_0185_fileInUse_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0189_fileInUse_xp_win_partial.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use partial MAR file background patch apply success test */
+/* File in use partial MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0185";
+const TEST_ID = "0189";
 const MAR_IN_USE_WIN_FILE = "data/partial_win.mar";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
@@ -229,19 +229,19 @@ function doUpdate() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test/unit/test_0186_rmrfdirFileInUse_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0190_rmrfdirFileInUse_xp_win_complete.js
--- a/toolkit/mozapps/update/test/unit/test_0186_rmrfdirFileInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0190_rmrfdirFileInUse_xp_win_complete.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use inside removed dir complete MAR file background patch apply success test */
+/* File in use inside removed dir complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0186";
+const TEST_ID = "0190";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -235,19 +235,19 @@ function doUpdate() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test/unit/test_0187_rmrfdirFileInUse_xp_win_partial.js
copy to toolkit/mozapps/update/test/unit/test_0191_rmrfdirFileInUse_xp_win_partial.js
--- a/toolkit/mozapps/update/test/unit/test_0187_rmrfdirFileInUse_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0191_rmrfdirFileInUse_xp_win_partial.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use inside removed dir partial MAR file background patch apply success test */
+/* File in use inside removed dir partial MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0187";
+const TEST_ID = "0191";
 const MAR_IN_USE_WIN_FILE = "data/partial.mar";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
@@ -276,19 +276,19 @@ function doUpdate() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js
copy to toolkit/mozapps/update/test/unit/test_0203_app_launch_apply_update.js
--- a/toolkit/mozapps/update/test/unit/test_0201_app_launch_apply_update.js
+++ b/toolkit/mozapps/update/test/unit/test_0203_app_launch_apply_update.js
@@ -3,33 +3,41 @@
  */
 
 /**
  * Test applying an update by applying an update in the background and
  * launching an application
  */
 
 /**
+ * This test is identical to test_0201_app_launch_apply_update.js, except
+ * that it locks the application directory when the test is launched to
+ * make the updater fall back to apply the update regularly.
+ */
+
+/**
  * The MAR file used for this test should not contain a version 2 update
  * manifest file (e.g. updatev2.manifest).
  */
 
-const TEST_ID = "0201";
+const TEST_ID = "0203";
 
 // Backup the updater.ini and use a custom one to prevent the updater from
 // launching a post update executable.
 const FILE_UPDATER_INI_BAK = "updater.ini.bak";
 
 // Number of milliseconds for each do_timeout call.
 const CHECK_TIMEOUT_MILLI = 1000;
 
 // Maximum number of milliseconds the process that is launched can run before
 // the test will try to kill it.
 const APP_TIMER_TIMEOUT = 15000;
 
+Components.utils.import("resource://gre/modules/ctypes.jsm");
+
 let gAppTimer;
 let gProcess;
 let gActiveUpdate;
 
 // Override getUpdatesRootDir on Mac because we need to apply the update
 // inside the bundle directory.
 function symlinkUpdateFilesIntoBundleDirectory() {
   if (!shouldAdjustPathsOnMac()) {
@@ -166,16 +174,37 @@ function run_test() {
   checkUpdateApplied();
 }
 
 function switchApp() {
   let launchBin = getLaunchBin();
   let args = getProcessArgs();
   logTestInfo("launching " + launchBin.path + " " + args.join(" "));
 
+  // Lock the installation directory
+  const LPCWSTR = ctypes.jschar.ptr;
+  const DWORD = ctypes.uint32_t;
+  const LPVOID = ctypes.voidptr_t;
+  const GENERIC_READ = 0x80000000;
+  const FILE_SHARE_READ = 1;
+  const FILE_SHARE_WRITE = 2;
+  const OPEN_EXISTING = 3;
+  const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
+  const INVALID_HANDLE_VALUE = LPVOID(0xffffffff);
+  let kernel32 = ctypes.open("kernel32");
+  let CreateFile = kernel32.declare("CreateFileW", ctypes.default_abi,
+                                    LPVOID, LPCWSTR, DWORD, DWORD,
+                                    LPVOID, DWORD, DWORD, LPVOID);
+  logTestInfo(gWindowsBinDir.path);
+  let handle = CreateFile(gWindowsBinDir.path, GENERIC_READ,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE, LPVOID(0),
+                          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, LPVOID(0));
+  do_check_neq(handle.toString(), INVALID_HANDLE_VALUE.toString());
+  kernel32.close();
+
   gProcess = AUS_Cc["@mozilla.org/process/util;1"].
                 createInstance(AUS_Ci.nsIProcess);
   gProcess.init(launchBin);
 
   gAppTimer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
   gAppTimer.initWithCallback(gTimerCallback, APP_TIMER_TIMEOUT,
                              AUS_Ci.nsITimer.TYPE_ONE_SHOT);
 
--- a/toolkit/mozapps/update/test/unit/xpcshell_updater_windows.ini
+++ b/toolkit/mozapps/update/test/unit/xpcshell_updater_windows.ini
@@ -1,22 +1,29 @@
 [test_0113_versionDowngradeCheck.js]
 [test_0114_productChannelCheck.js]
 [test_0150_appBinReplaced_xp_win_complete.js]
 [test_0151_appBinPatched_xp_win_partial.js]
 [test_0152_appBinReplaced_xp_win_complete.js]
 [test_0153_appBinPatched_xp_win_partial.js]
 [test_0160_appInUse_xp_win_complete.js]
 [test_0161_appInUse_xp_win_complete.js]
+[test_0162_appInUse_xp_win_complete.js]
 [test_0170_fileLocked_xp_win_complete.js]
 [test_0171_fileLocked_xp_win_partial.js]
 [test_0172_fileLocked_xp_win_complete.js]
 [test_0173_fileLocked_xp_win_partial.js]
+[test_0174_fileLocked_xp_win_complete.js]
+[test_0175_fileLocked_xp_win_partial.js]
 [test_0180_fileInUse_xp_win_complete.js]
 [test_0181_fileInUse_xp_win_partial.js]
 [test_0182_rmrfdirFileInUse_xp_win_complete.js]
 [test_0183_rmrfdirFileInUse_xp_win_partial.js]
 [test_0184_fileInUse_xp_win_complete.js]
 [test_0185_fileInUse_xp_win_partial.js]
 [test_0186_rmrfdirFileInUse_xp_win_complete.js]
 [test_0187_rmrfdirFileInUse_xp_win_partial.js]
+[test_0188_fileInUse_xp_win_complete.js]
+[test_0189_fileInUse_xp_win_partial.js]
+[test_0190_rmrfdirFileInUse_xp_win_complete.js]
+[test_0191_rmrfdirFileInUse_xp_win_partial.js]
 [test_0202_app_launch_apply_update_dirlocked.js]
-skip-if = true
+[test_0203_app_launch_apply_update.js]
--- a/toolkit/mozapps/update/test_svc/unit/test_0161_appInUse_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0161_appInUse_xp_win_complete_svc.js
@@ -213,16 +213,17 @@ function doUpdate() {
 function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
copy from toolkit/mozapps/update/test_svc/unit/test_0161_appInUse_xp_win_complete_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0162_appInUse_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0161_appInUse_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0162_appInUse_xp_win_complete_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* Application in use complete MAR file background patch apply failure test */
+/* Application in use complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0161_svc";
+const TEST_ID = "0162_svc";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -222,21 +222,19 @@ function checkUpdateApplied() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  // The update status format for a failure is failed: # where # is the error
-  // code for the failure.
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
--- a/toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js
@@ -221,16 +221,17 @@ function doUpdate() {
 function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_FAILED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
@@ -222,16 +222,17 @@ function doUpdate() {
 function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_FAILED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
copy from toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0174_fileLocked_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0174_fileLocked_xp_win_complete_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File locked complete MAR file background patch apply failure test */
+/* File locked complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0172_svc";
+const TEST_ID = "0174_svc";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -230,21 +230,19 @@ function checkUpdateApplied() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  // The update status format for a failure is failed: # where # is the error
-  // code for the failure.
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0175_fileLocked_xp_win_partial_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0175_fileLocked_xp_win_partial_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File locked partial MAR file background patch apply failure test */
+/* File locked partial MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0173_svc";
+const TEST_ID = "0175_svc";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -231,21 +231,19 @@ function checkUpdateApplied() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  // The update status format for a failure is failed: # where # is the error
-  // code for the failure.
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
--- a/toolkit/mozapps/update/test_svc/unit/test_0184_fileInUse_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0184_fileInUse_xp_win_complete_svc.js
@@ -220,16 +220,17 @@ function doUpdate() {
 function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test_svc/unit/test_0185_fileInUse_xp_win_partial_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0185_fileInUse_xp_win_partial_svc.js
@@ -223,16 +223,17 @@ function doUpdate() {
 function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test_svc/unit/test_0186_rmrfdirFileInUse_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0186_rmrfdirFileInUse_xp_win_complete_svc.js
@@ -229,16 +229,17 @@ function doUpdate() {
 function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
--- a/toolkit/mozapps/update/test_svc/unit/test_0187_rmrfdirFileInUse_xp_win_partial_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0187_rmrfdirFileInUse_xp_win_partial_svc.js
@@ -270,16 +270,17 @@ function doUpdate() {
 function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_APPLIED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_APPLIED);
 
   // Now switch the application and its updated version
   gBackgroundUpdate = false;
   gSwitchApp = true;
+  gDisableReplaceFallback = true;
   exitValue = runUpdate();
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
copy from toolkit/mozapps/update/test_svc/unit/test_0184_fileInUse_xp_win_complete_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0188_fileInUse_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0184_fileInUse_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0188_fileInUse_xp_win_complete_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use complete MAR file background patch apply success test */
+/* File in use complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0184_svc";
+const TEST_ID = "0188_svc";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -229,19 +229,19 @@ function checkUpdateApplied() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test_svc/unit/test_0185_fileInUse_xp_win_partial_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0189_fileInUse_xp_win_partial_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0185_fileInUse_xp_win_partial_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0189_fileInUse_xp_win_partial_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use partial MAR file background patch apply success test */
+/* File in use partial MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0185_svc";
+const TEST_ID = "0189_svc";
 const MAR_IN_USE_WIN_FILE = "data/partial_win.mar";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
@@ -232,19 +232,19 @@ function checkUpdateApplied() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test_svc/unit/test_0186_rmrfdirFileInUse_xp_win_complete_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0190_rmrfdirFileInUse_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0186_rmrfdirFileInUse_xp_win_complete_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0190_rmrfdirFileInUse_xp_win_complete_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use inside removed dir complete MAR file background patch apply success test */
+/* File in use inside removed dir complete MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0186_svc";
+const TEST_ID = "0190_svc";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
   fileName         : "channel-prefs.js",
@@ -238,19 +238,19 @@ function checkUpdateApplied() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test_svc/unit/test_0187_rmrfdirFileInUse_xp_win_partial_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0191_rmrfdirFileInUse_xp_win_partial_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0187_rmrfdirFileInUse_xp_win_partial_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0191_rmrfdirFileInUse_xp_win_partial_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* File in use inside removed dir partial MAR file background patch apply success test */
+/* File in use inside removed dir partial MAR file background patch apply failure fallback test */
 
-const TEST_ID = "0187_svc";
+const TEST_ID = "0191_svc";
 const MAR_IN_USE_WIN_FILE = "data/partial.mar";
 
 // The files are listed in the same order as they are applied from the mar's
 // update.manifest. Complete updates have remove file and rmdir directory
 // operations located in the precomplete file performed first.
 const TEST_FILES = [
 {
   description      : "Should never change",
@@ -279,19 +279,19 @@ function checkUpdateApplied() {
   logTestInfo("testing updater binary process exitValue for failure when " +
               "switching to the updated application");
   do_check_eq(exitValue, 1);
 
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_PENDING);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
+  do_check_eq(readStatusFile(updatesDir), STATE_PENDING);
 
   checkFilesAfterUpdateFailure(getApplyDirFile);
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory does not exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
copy from toolkit/mozapps/update/test_svc/unit/test_0201_app_launch_apply_update_svc.js
copy to toolkit/mozapps/update/test_svc/unit/test_0203_app_launch_apply_update_svc.js
--- a/toolkit/mozapps/update/test_svc/unit/test_0201_app_launch_apply_update_svc.js
+++ b/toolkit/mozapps/update/test_svc/unit/test_0203_app_launch_apply_update_svc.js
@@ -3,33 +3,41 @@
  */
 
 /**
  * Test applying an update by applying an update in the background and
  * launching an application
  */
 
 /**
+ * This test is identical to test_0201_app_launch_apply_update_svc.js, except
+ * that it locks the application directory when the test is launched to
+ * make the updater fall back to apply the update regularly.
+ */
+
+/**
  * The MAR file used for this test should not contain a version 2 update
  * manifest file (e.g. updatev2.manifest).
  */
 
-const TEST_ID = "0201_svc";
+const TEST_ID = "0203_svc";
 
 // Backup the updater.ini and use a custom one to prevent the updater from
 // launching a post update executable.
 const FILE_UPDATER_INI_BAK = "updater.ini.bak";
 
 // Number of milliseconds for each do_timeout call.
 const CHECK_TIMEOUT_MILLI = 1000;
 
 // Maximum number of milliseconds the process that is launched can run before
 // the test will try to kill it.
 const APP_TIMER_TIMEOUT = 15000;
 
+Components.utils.import("resource://gre/modules/ctypes.jsm");
+
 let gAppTimer;
 let gProcess;
 let gActiveUpdate;
 
 // Override getUpdatesRootDir on Mac because we need to apply the update
 // inside the bundle directory.
 function symlinkUpdateFilesIntoBundleDirectory() {
   if (!shouldAdjustPathsOnMac()) {
@@ -174,16 +182,37 @@ function run_test() {
   checkUpdateApplied();
 }
 
 function switchApp() {
   let launchBin = getLaunchBin();
   let args = getProcessArgs();
   logTestInfo("launching " + launchBin.path + " " + args.join(" "));
 
+  // Lock the installation directory
+  const LPCWSTR = ctypes.jschar.ptr;
+  const DWORD = ctypes.uint32_t;
+  const LPVOID = ctypes.voidptr_t;
+  const GENERIC_READ = 0x80000000;
+  const FILE_SHARE_READ = 1;
+  const FILE_SHARE_WRITE = 2;
+  const OPEN_EXISTING = 3;
+  const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
+  const INVALID_HANDLE_VALUE = LPVOID(0xffffffff);
+  let kernel32 = ctypes.open("kernel32");
+  let CreateFile = kernel32.declare("CreateFileW", ctypes.default_abi,
+                                    LPVOID, LPCWSTR, DWORD, DWORD,
+                                    LPVOID, DWORD, DWORD, LPVOID);
+  logTestInfo(gWindowsBinDir.path);
+  let handle = CreateFile(gWindowsBinDir.path, GENERIC_READ,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE, LPVOID(0),
+                          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, LPVOID(0));
+  do_check_neq(handle.toString(), INVALID_HANDLE_VALUE.toString());
+  kernel32.close();
+
   gProcess = AUS_Cc["@mozilla.org/process/util;1"].
                 createInstance(AUS_Ci.nsIProcess);
   gProcess.init(launchBin);
 
   gAppTimer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
   gAppTimer.initWithCallback(gTimerCallback, APP_TIMER_TIMEOUT,
                              AUS_Ci.nsITimer.TYPE_ONE_SHOT);
 
--- a/toolkit/mozapps/update/test_svc/unit/xpcshell.ini
+++ b/toolkit/mozapps/update/test_svc/unit/xpcshell.ini
@@ -14,23 +14,31 @@ tail =
 [test_0114_general_svc.js]
 [test_0115_general_svc.js]
 [test_0150_appBinReplaced_xp_win_complete_svc.js]
 [test_0151_appBinPatched_xp_win_partial_svc.js]
 [test_0152_appBinReplaced_xp_win_complete_svc.js]
 [test_0153_appBinPatched_xp_win_partial_svc.js]
 [test_0160_appInUse_xp_win_complete_svc.js]
 [test_0161_appInUse_xp_win_complete_svc.js]
+[test_0162_appInUse_xp_win_complete_svc.js]
 [test_0170_fileLocked_xp_win_complete_svc.js]
 [test_0171_fileLocked_xp_win_partial_svc.js]
 [test_0172_fileLocked_xp_win_complete_svc.js]
 [test_0173_fileLocked_xp_win_partial_svc.js]
+[test_0174_fileLocked_xp_win_complete_svc.js]
+[test_0175_fileLocked_xp_win_partial_svc.js]
 [test_0180_fileInUse_xp_win_complete_svc.js]
 [test_0181_fileInUse_xp_win_partial_svc.js]
 [test_0182_rmrfdirFileInUse_xp_win_complete_svc.js]
 [test_0183_rmrfdirFileInUse_xp_win_partial_svc.js]
 [test_0184_fileInUse_xp_win_complete_svc.js]
 [test_0185_fileInUse_xp_win_partial_svc.js]
 [test_0186_rmrfdirFileInUse_xp_win_complete_svc.js]
 [test_0187_rmrfdirFileInUse_xp_win_partial_svc.js]
+[test_0188_fileInUse_xp_win_complete_svc.js]
+[test_0189_fileInUse_xp_win_partial_svc.js]
+[test_0190_rmrfdirFileInUse_xp_win_complete_svc.js]
+[test_0191_rmrfdirFileInUse_xp_win_partial_svc.js]
 [test_0200_app_launch_apply_update_svc.js]
 [test_0201_app_launch_apply_update_svc.js]
 [test_0202_app_launch_apply_update_dirlocked_svc.js]
+[test_0203_app_launch_apply_update_svc.js]
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -262,16 +262,17 @@ private:
 //-----------------------------------------------------------------------------
 
 static NS_tchar* gSourcePath;
 static NS_tchar gDestinationPath[MAXPATHLEN];
 static ArchiveReader gArchiveReader;
 static bool gSucceeded = false;
 static bool sBackgroundUpdate = false;
 static bool sReplaceRequest = false;
+static bool sUsingService = false;
 
 #ifdef XP_WIN
 // The current working directory specified in the command line.
 static NS_tchar* gDestPath;
 static NS_tchar gCallbackRelPath[MAXPATHLEN];
 static NS_tchar gCallbackBackupPath[MAXPATHLEN];
 #endif
 
@@ -545,37 +546,64 @@ static int ensure_parent_dir(const NS_tc
         rv = OK;
       }
     }
     *slash = NS_T('/');
   }
   return rv;
 }
 
+#ifdef XP_UNIX
+static int ensure_copy_symlink(const NS_tchar *path, const NS_tchar *dest)
+{
+  // Copy symlinks by creating a new symlink to the same target
+  NS_tchar target[MAXPATHLEN + 1] = {NS_T('\0')};
+  int rv = readlink(path, target, MAXPATHLEN);
+  if (rv == -1) {
+    LOG(("ensure_copy_symlink: failed to read the link: " LOG_S ", err: %d\n",
+         path, errno));
+    return READ_ERROR;
+  }
+  rv = symlink(target, dest);
+  if (rv == -1) {
+    LOG(("ensure_copy_symlink: failed to create the new link: " LOG_S ", target: " LOG_S " err: %d\n",
+         dest, target, errno));
+    return READ_ERROR;
+  }
+  return 0;
+}
+#endif
+
 // Copy the file named path onto a new file named dest.
 static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
 {
 #ifdef XP_WIN
   // Fast path for Windows
   bool result = CopyFileW(path, dest, false);
   if (!result) {
     LOG(("ensure_copy: failed to copy the file " LOG_S " over to " LOG_S ", lasterr: %x\n",
          path, dest, GetLastError()));
     return WRITE_ERROR;
   }
   return 0;
 #else
   struct stat ss;
-  int rv = NS_tstat(path, &ss);
+  int rv = NS_tlstat(path, &ss);
   if (rv) {
     LOG(("ensure_copy: failed to read file status info: " LOG_S ", err: %d\n",
           path, errno));
     return READ_ERROR;
   }
 
+#ifdef XP_UNIX
+  if (S_ISLNK(ss.st_mode)) {
+    return ensure_copy_symlink(path, dest);
+  }
+#endif
+
   AutoFile infile = ensure_open(path, NS_T("rb"), ss.st_mode);
   if (!infile) {
     LOG(("ensure_copy: failed to open the file for reading: " LOG_S ", err: %d\n",
           path, errno));
     return READ_ERROR;
   }
   AutoFile outfile = ensure_open(dest, NS_T("wb"), ss.st_mode);
   if (!outfile) {
@@ -640,22 +668,29 @@ struct copy_recursive_skiplist {
 
 // Copy all of the files and subdirectories under path to a new directory named dest.
 // The path names in the skiplist will be skipped and will not be copied.
 template <unsigned N>
 static int ensure_copy_recursive(const NS_tchar *path, const NS_tchar *dest,
                                  copy_recursive_skiplist<N>& skiplist)
 {
   struct stat sInfo;
-  int rv = NS_tstat(path, &sInfo);
+  int rv = NS_tlstat(path, &sInfo);
   if (rv) {
     LOG(("ensure_copy_recursive: path doesn't exist: " LOG_S ", rv: %d, err: %d\n",
           path, rv, errno));
     return READ_ERROR;
   }
+
+#ifdef XP_UNIX
+  if (S_ISLNK(sInfo.st_mode)) {
+    return ensure_copy_symlink(path, dest);
+  }
+#endif
+
   if (!S_ISDIR(sInfo.st_mode)) {
     return ensure_copy(path, dest);
   }
 
   rv = NS_tmkdir(dest, sInfo.st_mode);
   if (rv < 0 && errno != EEXIST) {
     LOG(("ensure_copy_recursive: could not create destination directory: " LOG_S ", rv: %d, err: %d\n",
          path, rv, errno));
@@ -1584,42 +1619,49 @@ LaunchCallbackApp(const NS_tchar *workin
     WinLaunchChild(argv[0], argc, argv, NULL);
   }
 #else
 # warning "Need implementaton of LaunchCallbackApp"
 #endif
 }
 
 static void
-WriteStatusFile(int status)
+WriteStatusText(const char* text)
 {
   // This is how we communicate our completion status to the main application.
 
   NS_tchar filename[MAXPATHLEN];
   NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
                NS_T("%s/update.status"), gSourcePath);
 
   AutoFile file = NS_tfopen(filename, NS_T("wb+"));
   if (file == NULL)
     return;
 
+  fwrite(text, strlen(text), 1, file);
+}
+
+static void
+WriteStatusFile(int status)
+{
   const char *text;
 
   char buf[32];
   if (status == OK) {
     if (sBackgroundUpdate) {
       text = "applied\n";
     } else {
       text = "succeeded\n";
     }
   } else {
     snprintf(buf, sizeof(buf)/sizeof(buf[0]), "failed: %d\n", status);
     text = buf;
   }
-  fwrite(text, strlen(text), 1, file);
+
+  WriteStatusText(text);
 }
 
 static bool
 WriteStatusFile(const char* aStatus)
 {
   NS_tchar filename[MAXPATHLEN];
   NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
                NS_T("%s/update.status"), gSourcePath);
@@ -2028,41 +2070,74 @@ UpdateThreadFunc(void *param)
     }
 
     if (rv == OK) {
       rv = DoUpdate();
       gArchiveReader.Close();
     }
   }
 
-  if (rv) {
-    LOG(("failed: %d\n", rv));
-  }
-  else {
+  bool reportRealResults = true;
+  if (sReplaceRequest && rv && !getenv("MOZ_NO_REPLACE_FALLBACK")) {
+    // When attempting to replace the application, we should fall back
+    // to non-staged updates in case of a failure.  We do this by
+    // setting the status to pending, exiting the updater, and
+    // launching the callback application.  The callback application's
+    // startup path will see the pending status, and will start the
+    // updater application again in order to apply the update without
+    // staging.
+    // The MOZ_NO_REPLACE_FALLBACK environment variable is used to
+    // bypass this fallback, and is used in the updater tests.
+    // The only special thing which we should do here is to remove the
+    // staged directory as it won't be useful any more.
+    NS_tchar installDir[MAXPATHLEN];
+    if (GetInstallationDir(installDir)) {
+      NS_tchar stageDir[MAXPATHLEN];
+      NS_tsnprintf(stageDir, sizeof(stageDir)/sizeof(stageDir[0]),
 #ifdef XP_MACOSX
-    // If the update was successful we need to update the timestamp
-    // on the top-level Mac OS X bundle directory so that Mac OS X's
-    // Launch Services picks up any major changes. Here we assume that
-    // the current working directory is the top-level bundle directory.
-    char* cwd = getcwd(NULL, 0);
-    if (cwd) {
-      if (utimes(cwd, NULL) != 0) {
-        LOG(("Couldn't set access/modification time on application bundle.\n"));
-      }
-      free(cwd);
+                   NS_T("%s/Updated.app"),
+#else
+                   NS_T("%s/updated"),
+#endif
+                   installDir);
+
+      ensure_remove_recursive(stageDir);
+      WriteStatusText(sUsingService ? "pending-service" : "pending");
+      putenv("MOZ_PROCESS_UPDATES="); // We need to use -process-updates again in the tests
+      reportRealResults = false; // pretend success
+    }
+  }
+
+  if (reportRealResults) {
+    if (rv) {
+      LOG(("failed: %d\n", rv));
     }
     else {
-      LOG(("Couldn't get current working directory for setting "
-           "access/modification time on application bundle.\n"));
-    }
+#ifdef XP_MACOSX
+      // If the update was successful we need to update the timestamp
+      // on the top-level Mac OS X bundle directory so that Mac OS X's
+      // Launch Services picks up any major changes. Here we assume that
+      // the current working directory is the top-level bundle directory.
+      char* cwd = getcwd(NULL, 0);
+      if (cwd) {
+        if (utimes(cwd, NULL) != 0) {
+          LOG(("Couldn't set access/modification time on application bundle.\n"));
+        }
+        free(cwd);
+      }
+      else {
+        LOG(("Couldn't get current working directory for setting "
+             "access/modification time on application bundle.\n"));
+      }
 #endif
 
-    LOG(("succeeded\n"));
+      LOG(("succeeded\n"));
+    }
+    WriteStatusFile(rv);
   }
-  WriteStatusFile(rv);
 
   LOG(("calling QuitProgressUI\n"));
   QuitProgressUI();
 }
 
 int NS_main(int argc, NS_tchar **argv)
 {
   InitProgressUI(&argc, &argv);
@@ -2222,32 +2297,31 @@ int NS_main(int argc, NS_tchar **argv)
 #endif
   }
 
   // The callback is the remaining arguments starting at callbackIndex.
   // The argument specified by callbackIndex is the callback executable and the
   // argument prior to callbackIndex is the working directory.
   const int callbackIndex = 5;
 
-  bool usingService = false;
 #if defined(XP_WIN)
-  usingService = getenv("MOZ_USING_SERVICE") != NULL;
+  sUsingService = getenv("MOZ_USING_SERVICE") != NULL;
   putenv(const_cast<char*>("MOZ_USING_SERVICE="));
   // lastFallbackError keeps track of the last error for the service not being 
   // used, in case of an error when fallback is not enabled we write the 
   // error to the update.status file. 
   // When fallback is disabled (MOZ_NO_SERVICE_FALLBACK does not exist) then
   // we will instead fallback to not using the service and display a UAC prompt.
   int lastFallbackError = FALLBACKKEY_UNKNOWN_ERROR;
 
   // Launch a second instance of the updater with the runas verb on Windows
   // when write access is denied to the installation directory.
   HANDLE updateLockFileHandle = INVALID_HANDLE_VALUE;
   NS_tchar elevatedLockFilePath[MAXPATHLEN] = {NS_T('\0')};
-  if (!usingService &&
+  if (!sUsingService &&
       (argc > callbackIndex || sBackgroundUpdate || sReplaceRequest)) {
     NS_tchar updateLockFilePath[MAXPATHLEN];
     if (sBackgroundUpdate) {
       // When updating in the background, the lock file is:
       // $INSTALLDIR\updated.update_in_progress.lock
       NS_tsnprintf(updateLockFilePath,
                    sizeof(updateLockFilePath)/sizeof(updateLockFilePath[0]),
                    NS_T("%s.update_in_progress.lock"), gDestinationPath);
@@ -2487,17 +2561,17 @@ int NS_main(int argc, NS_tchar **argv)
           CloseHandle(sinfo.hProcess);
         } else {
           WriteStatusFile(ELEVATION_CANCELED);
         }
       }
 
       if (argc > callbackIndex) {
         LaunchCallbackApp(argv[4], argc - callbackIndex,
-                          argv + callbackIndex, usingService);
+                          argv + callbackIndex, sUsingService);
       }
 
       CloseHandle(elevatedFileHandle);
 
       if (!useService && !noServiceFallback &&
           INVALID_HANDLE_VALUE == updateLockFileHandle) {
         // We didn't use the service and we did run the elevated updater.exe.
         // The elevated updater.exe is responsible for writing out the
@@ -2578,17 +2652,17 @@ int NS_main(int argc, NS_tchar **argv)
   if (!GetLongPathNameW(gDestinationPath, applyDirLongPath,
                         sizeof(applyDirLongPath)/sizeof(applyDirLongPath[0]))) {
     LOG(("NS_main: unable to find apply to dir: " LOG_S "\n", gDestinationPath));
     LogFinish();
     WriteStatusFile(WRITE_ERROR);
     EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
     if (argc > callbackIndex) {
       LaunchCallbackApp(argv[4], argc - callbackIndex,
-                        argv + callbackIndex, usingService);
+                        argv + callbackIndex, sUsingService);
     }
     return 1;
   }
 
   HANDLE callbackFile = INVALID_HANDLE_VALUE;
   if (argc > callbackIndex) {
     // If the callback executable is specified it must exist for a successful
     // update.  It is important we null out the whole buffer here because later
@@ -2623,17 +2697,17 @@ int NS_main(int argc, NS_tchar **argv)
       LOG(("NS_main: unable to find callback file: " LOG_S "\n", targetPath));
       LogFinish();
       WriteStatusFile(WRITE_ERROR);
       EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
       if (argc > callbackIndex) {
         LaunchCallbackApp(argv[4], 
                           argc - callbackIndex, 
                           argv + callbackIndex, 
-                          usingService);
+                          sUsingService);
       }
       return 1;
     }
 
     // Doing this is only necessary when we're actually applying a patch.
     if (!sReplaceRequest) {
       int len = NS_tstrlen(applyDirLongPath);
       NS_tchar *s = callbackLongPath;
@@ -2696,17 +2770,17 @@ int NS_main(int argc, NS_tchar **argv)
              "file: " LOG_S "\n", argv[callbackIndex]));
         LogFinish();
         WriteStatusFile(WRITE_ERROR);
         NS_tremove(gCallbackBackupPath);
         EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
         LaunchCallbackApp(argv[4],
                           argc - callbackIndex,
                           argv + callbackIndex,
-                          usingService);
+                          sUsingService);
         return 1;
       }
     }
   }
 
   // DELETE_DIR is not required in the case of background updates.
   if (!sBackgroundUpdate && !sReplaceRequest) {
     // The directory to move files that are in use to on Windows. This directory
@@ -2765,17 +2839,17 @@ int NS_main(int argc, NS_tchar **argv)
     if (gSucceeded) {
       // The service update will only be executed if it is already installed.
       // For first time installs of the service, the install will happen from
       // the PostUpdate process. We do the service update process here 
       // because it's possible we are updating with updater.exe without the 
       // service if the service failed to apply the update. We want to update
       // the service to a newer version in that case. If we are not running
       // through the service, then MOZ_USING_SERVICE will not exist.
-      if (!usingService) {
+      if (!sUsingService) {
         NS_tchar installDir[MAXPATHLEN];
         if (GetInstallationDir(installDir)) {
           if (!LaunchWinPostProcess(installDir, gSourcePath, false, NULL)) {
             LOG(("NS_main: The post update process could not be launched.\n"));
           }
 
           StartServiceUpdate(installDir);
         }
@@ -2786,17 +2860,17 @@ int NS_main(int argc, NS_tchar **argv)
 #ifdef XP_MACOSX
     if (gSucceeded) {
       LaunchMacPostProcess(argv[callbackIndex]);
     }
 #endif /* XP_MACOSX */
     LaunchCallbackApp(argv[4], 
                       argc - callbackIndex, 
                       argv + callbackIndex, 
-                      usingService);
+                      sUsingService);
   }
 
   return gSucceeded ? 0 : 1;
 }
 
 class ActionList
 {
 public: