Bug 481815 - Automated tests for updates using the maintenance service; r=rstrong.
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 04 Jan 2012 23:19:14 -0500
changeset 83769 7e565fc8516cfd7485721da1405fddf1603aeca1
parent 83768 1bd9f069576e63b5527ea9b4d99b7f3aca1bd5c4
child 83770 d5637e69d71ed290e6a7d7892b486a657f2a98ed
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersrstrong
bugs481815
milestone12.0a1
Bug 481815 - Automated tests for updates using the maintenance service; r=rstrong.
toolkit/mozapps/update/test/unit/head_update.js.in
toolkit/mozapps/update/test/unit/test_0000_bootstrap_svc.js
toolkit/mozapps/update/test/unit/test_0110_general_svc.js
toolkit/mozapps/update/test/unit/test_0111_general_svc.js
toolkit/mozapps/update/test/unit/test_0112_general_svc.js
toolkit/mozapps/update/test/unit/test_0120_channelChange_complete_svc.js
toolkit/mozapps/update/test/unit/test_0150_appBinReplaced_xp_win_complete_svc.js
toolkit/mozapps/update/test/unit/test_0151_appBinPatched_xp_win_partial_svc.js
toolkit/mozapps/update/test/unit/test_0160_appInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete_svc.js
toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial_svc.js
toolkit/mozapps/update/test/unit/test_0180_fileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test/unit/test_0181_fileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test/unit/test_0182_rmrfdirFileInUse_xp_win_complete_svc.js
toolkit/mozapps/update/test/unit/test_0183_rmrfdirFileInUse_xp_win_partial_svc.js
toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update_svc.js
toolkit/mozapps/update/test/unit/xpcshell.ini
toolkit/mozapps/update/test/unit/xpcshell_updater_windows_svc.ini
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsUpdateDriver.cpp
xpcom/io/SpecialSystemDirectory.cpp
xpcom/io/SpecialSystemDirectory.h
xpcom/io/nsDirectoryService.cpp
xpcom/io/nsDirectoryServiceAtomList.h
xpcom/io/nsDirectoryServiceDefs.h
--- a/toolkit/mozapps/update/test/unit/head_update.js.in
+++ b/toolkit/mozapps/update/test/unit/head_update.js.in
@@ -99,23 +99,29 @@ const LOG_PARTIAL_FAILURE = "data/partia
 
 const ERR_CALLBACK_FILE_IN_USE = "NS_main: file in use - failed to " +
                                  "exclusively open executable file:"
 
 const ERR_RENAME_FILE = "rename_file: failed to rename file";
 const ERR_UNABLE_OPEN_DEST = "unable to open destination file";
 const ERR_BACKUP_DISCARD = "backup_discard: unable to remove";
 
+const LOG_SVC_SUCCESSFUL_LAUNCH = "updater.exe was launched and run successfully!";
+
 // 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"];
 
 // Time to wait for the test helper process before continuing the test
 const TEST_HELPER_TIMEOUT = 100;
 
+// 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";
+
 var gTestserver;
 
 var gXHR;
 var gXHRCallback;
 
 var gCheckFunc;
 var gResponseBody;
 var gResponseStatusCode = 200;
@@ -440,16 +446,152 @@ function runUpdate() {
              concat(gCallbackArgs);
   let process = AUS_Cc["@mozilla.org/process/util;1"].
                 createInstance(AUS_Ci.nsIProcess);
   process.init(updateBin);
   process.run(true, args, args.length);
   return process.exitValue;
 }
 
+let gServiceLaunchedCallbackLog = null;
+let gServiceLaunchedCallbackArgs = null;
+
+/**
+ * Helper function for updater tests for launching the updater using the
+ * maintenance service to apply a mar file.
+ *
+ * @param aInitialStatus  the initial value of update.status
+ * @param aExpectedStatus the expected value of update.status when the test finishes
+ * @param aCallback       the function to be called when the update is finished
+ * @param aUpdatesDir     the updates root directory to use (optional)
+ * @param aCheckSvcLog    whether the service log should be checked (optional)
+ */
+function runUpdateUsingService(aInitialStatus, aExpectedStatus,
+                               aCallback, aUpdatesDir, aCheckSvcLog) {
+  // Check the service logs for a successful update
+  function checkServiceLogs(aOriginalContents) {
+    let contents = readServiceLogFile();
+    logTestInfo("The contents of maintenanceservice.log:\n" + contents + "\n");
+    do_check_neq(contents, aOriginalContents);
+    do_check_neq(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1);
+  }
+  function readServiceLogFile() {
+    let file = AUS_Cc["@mozilla.org/file/directory_service;1"].
+               getService(AUS_Ci.nsIProperties).
+               get("CmAppData", AUS_Ci.nsIFile);
+    file.append("Mozilla");
+    file.append("logs");
+    file.append("maintenanceservice.log");
+    return readFile(file);
+  }
+
+  // Prevent the cleanup function from begin run more than once
+  if (typeof(gRegisteredServiceCleanup) === "undefined") {
+    gRegisteredServiceCleanup = true;
+
+    do_register_cleanup(function serviceCleanup() {
+      resetEnvironment();
+
+      // Remove the copy of the application executable used for the test on
+      // Windows if it exists.
+      let appBinCopy = getCurrentProcessDir();
+      appBinCopy.append(FILE_WIN_TEST_EXE);
+      if (appBinCopy.exists()) {
+        appBinCopy.remove(false);
+      }
+
+      // This will delete the app console log file if it exists.
+      getAppConsoleLogPath();
+
+      // This will delete the app arguments log file if it exists.
+      getAppArgsLogPath();
+    });
+  }
+
+  if (aCheckSvcLog === undefined) {
+    aCheckSvcLog = true; // default to true
+  }
+
+  let svcOriginalLog;
+  if (aCheckSvcLog) {
+    svcOriginalLog = readServiceLogFile();
+  }
+
+  let appArgsLogPath = getAppArgsLogPath();
+  gServiceLaunchedCallbackLog = appArgsLogPath.replace(/^"|"$/g, "");
+
+  let updatesDir = aUpdatesDir || do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
+  let file = updatesDir.clone();
+  file.append(FILE_UPDATE_STATUS);
+  writeFile(file, aInitialStatus + "\n");
+
+  // sanity check
+  do_check_eq(readStatusFile(updatesDir), aInitialStatus);
+
+  gServiceLaunchedCallbackArgs = [
+    "-no-remote",
+    "-process-updates",
+    "-dump-args",
+    appArgsLogPath
+  ];
+
+  let launchBin = getLaunchBin();
+  let args = getProcessArgs(["-dump-args", appArgsLogPath]);
+  logTestInfo("launching " + launchBin.path + " " + args.join(" "));
+
+  let process = AUS_Cc["@mozilla.org/process/util;1"].
+                   createInstance(AUS_Ci.nsIProcess);
+  process.init(launchBin);
+
+  // Override the update root directory
+  gEnvUpdateRootOverride = updatesDir.path;
+  gEnvAppDirOverride = getApplyDirFile(null).path;
+
+  setEnvironment();
+
+  // We can't get sync behavior here since Firefox does not wait for the
+  // process launched through the service to finish.  Since the service
+  // launches the updater in the background, providing an observer argument
+  // doesn't solve anything either, so we will rely on watching the
+  // update.status file instead.
+  process.runAsync(args, args.length);
+
+  resetEnvironment();
+
+  function timerCallback(timer) {
+    // Wait for the expected status
+    let status = readStatusFile(updatesDir);
+    // For failed status, we don't care what the failure code is
+    if (aExpectedStatus == STATE_FAILED) {
+      status = status.split(": ")[0];
+    }
+    if (status == STATE_PENDING) {
+      logTestInfo("Still waiting to see the " + aExpectedStatus +
+                  " status, got " + status + " for now...");
+      return;
+    }
+    do_check_eq(status, aExpectedStatus);
+
+    timer.cancel();
+    timer = null;
+
+    // Give the service enough time to write its log and finish up
+    do_timeout(1000, function() {
+      if (aCheckSvcLog) {
+        checkServiceLogs(svcOriginalLog);
+      }
+
+      aCallback();
+    });
+  }
+
+  let timer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
+  timer.initWithCallback(timerCallback, 1000, timer.TYPE_REPEATING_SLACK);
+}
+
 /**
  * Gets the platform specific shell binary that is launched using nsIProcess and
  * in turn launches the updater.
  *
  * @return  nsIFile for the shell binary to launch using nsIProcess.
  * @throws  if the shell binary doesn't exist.
  */
 function getLaunchBin() {
@@ -695,16 +837,17 @@ function checkUpdateLogContents(aCompare
 
   do_check_eq(compareLogContents, updateLogContents);
 }
 
 function checkUpdateLogContains(aCheckString) {
   let updateLog = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX, true);
   updateLog.append(FILE_UPDATE_LOG);
   let updateLogContents = readFileBytes(updateLog);
+  logTestInfo("log file contents:\n" + updateLogContents + "\n");
   do_check_true(updateLogContents.indexOf(aCheckString) != -1);
 }
 
 /**
  * Helper function for updater binary tests for verifying the state of files and
  * directories after a successful update.
  */
 function checkFilesAfterUpdateSuccess() {
@@ -905,16 +1048,37 @@ function checkCallbackAppLog() {
               "and the expected command line arguments passed to it");
   do_check_eq(logContents, expectedLogContents);
 
   // Use a timeout to give any files that were in use additional time to close.
   do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
 }
 
 /**
+ * Helper function for updater service tests for verifying the contents of the
+ * updater callback application log which should contain the arguments passed to
+ * the callback application.
+ */
+function checkCallbackServiceLog() {
+  do_check_neq(gServiceLaunchedCallbackLog, null);
+
+  let expectedLogContents = gServiceLaunchedCallbackArgs.join("\n") + "\n";
+  let logFile = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile);
+  logFile.initWithPath(gServiceLaunchedCallbackLog);
+  let logContents = readFile(logFile);
+
+  logTestInfo("testing that the callback application successfully launched " +
+              "and the expected command line arguments passed to it");
+  do_check_eq(logContents, expectedLogContents);
+
+  // Use a timeout to give any files that were in use additional time to close.
+  do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
+}
+
+/**
  * Helper function for updater binary tests for verifying there are no update
  * backup files left behind after an update.
  *
  * @param   aFile
  *          An nsIFile to check if it has moz-backup for its extension.
  */
 function checkForBackupFiles(aFile) {
   do_check_neq(getFileExtension(aFile), "moz-backup");
@@ -1163,8 +1327,418 @@ var gDirProvider = {
   QueryInterface: function(iid) {
     if (iid.equals(AUS_Ci.nsIDirectoryServiceProvider) ||
         iid.equals(AUS_Ci.nsISupports))
       return this;
     throw AUS_Cr.NS_ERROR_NO_INTERFACE;
   }
 };
 Services.dirsvc.QueryInterface(AUS_Ci.nsIDirectoryService).registerProvider(gDirProvider);
+
+/**
+ * Returns the platform specific arguments used by nsIProcess when launching
+ * the application.
+ *
+ * @param aExtraArgs optional array of extra arguments
+ * @return  an array of arguments to be passed to nsIProcess.
+ *
+ * Notes:
+ * 1. Mozilla universal binaries that contain both i386 and x86_64 on Mac OS X
+ *    10.5.x must be launched using the i386 architecture.
+ * 2. A shell is necessary to pipe the application's console output which
+ *    would otherwise pollute the xpcshell log.
+ *
+ * Command line arguments used when launching the application:
+ * -no-remote prevents shell integration from being affected by an existing
+ * application process.
+ * -process-updates makes the application exits after being relaunched by the
+ * updater.
+ * 1> pipes stdout to a file.
+ * appConsoleLogPath is the file path to pipe the output from the shell.
+ * Otherwise the output from the application will end up in the xpchsell log.
+ * 2>&1 pipes stderr to sdout.
+ */
+function getProcessArgs(aExtraArgs) {
+  if (!aExtraArgs) {
+    aExtraArgs = [];
+  }
+
+  // Pipe the output from the launched application to a file so the output from
+  // its console isn't present in the xpcshell log.
+  let appConsoleLogPath = getAppConsoleLogPath();
+
+  let args;
+  if (IS_UNIX) {
+    let launchScript = getLaunchScript();
+    // Precreate the script with executable permissions
+    launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
+
+    let scriptContents = "#! /bin/sh\n";
+    // On Mac OS X versions prior to 10.6 the i386 acrhitecture must be used.
+    if (gIsLessThanMacOSX_10_6) {
+      scriptContents += "arch -arch i386 ";
+    }
+    scriptContents += gAppBinPath + " -no-remote -process-updates " +
+                      aExtraArgs.join(" ") + " 1> " +
+                      appConsoleLogPath + " 2>&1";
+    writeFile(launchScript, scriptContents);
+    logTestInfo("created " + launchScript.path + " containing:\n" +
+                scriptContents);
+    args = [launchScript.path];
+  }
+  else {
+    args = ["/D", "/Q", "/C", gAppBinPath, "-no-remote", "-process-updates"].
+           concat(aExtraArgs).
+           concat(["1>", appConsoleLogPath, "2>&1"]);
+  }
+  return args;
+}
+
+/**
+ * Gets a file path for piping the console output from the application so it
+ * doesn't appear in the xpcshell log file.
+ *
+ * @return  path to the file for piping the console output from the application.
+ */
+function getAppConsoleLogPath() {
+  let appConsoleLog = do_get_file("/", true);
+  appConsoleLog.append("app_console_log");
+  if (appConsoleLog.exists()) {
+    appConsoleLog.remove(false);
+  }
+  let appConsoleLogPath = appConsoleLog.path;
+  if (/ /.test(appConsoleLogPath)) {
+    appConsoleLogPath = '"' + appConsoleLogPath + '"';
+  }
+  return appConsoleLogPath;
+}
+
+/**
+ * Gets a file path for the application to dump its arguments into.  This is used
+ * to verify that a callback application is launched.
+ *
+ * @return  the file for the application to dump its arguments into.
+ */
+function getAppArgsLogPath() {
+  let appArgsLog = do_get_file("/", true);
+  appArgsLog.append("app_args_log");
+  if (appArgsLog.exists()) {
+    appArgsLog.remove(false);
+  }
+  let appArgsLogPath = appArgsLog.path;
+  if (/ /.test(appArgsLogPath)) {
+    appArgsLogPath = '"' + appArgsLogPath + '"';
+  }
+  return appArgsLogPath;
+}
+
+/**
+ * Gets the nsIFile reference for the shell script to launch the application. If
+ * the file exists it will be removed by this function.
+ *
+ * @return  the nsIFile for the shell script to launch the application.
+ */
+function getLaunchScript() {
+  let launchScript = do_get_file("/", true);
+  launchScript.append("launch.sh");
+  if (launchScript.exists()) {
+    launchScript.remove(false);
+  }
+  return launchScript;
+}
+
+// A shell script is used to get the OS version due to nsSystemInfo not
+// returning the actual OS version. It is possible to get the actual OS version
+// using ctypes but it would be more complicated than using a shell script.
+XPCOMUtils.defineLazyGetter(this, "gIsLessThanMacOSX_10_6", function test_gMacVer() {
+  if (!IS_MACOSX) {
+    return false;
+  }
+
+  let [versionScript, versionFile] = getVersionScriptAndFile();
+  // Precreate the script with executable permissions
+  versionScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
+  let scriptContents = "#! /bin/sh\nsw_vers -productVersion >> " + versionFile.path;
+  writeFile(versionScript, scriptContents);
+  logTestInfo("created " + versionScript.path + " shell script containing:\n" +
+              scriptContents);
+
+  let versionScriptPath = versionScript.path;
+  if (/ /.test(versionScriptPath)) {
+    versionScriptPath = '"' + versionScriptPath + '"';
+  }
+
+
+  let launchBin = getLaunchBin();
+  let args = [versionScriptPath];
+  let process = AUS_Cc["@mozilla.org/process/util;1"].
+                createInstance(AUS_Ci.nsIProcess);
+  process.init(launchBin);
+  process.run(true, args, args.length);
+  if (process.exitValue != 0) {
+    do_throw("Version script exited with " + process.exitValue + "... unable " +
+             "to get Mac OS X version!");
+  }
+
+  let version = readFile(versionFile).split("\n")[0];
+  logTestInfo("executing on Mac OS X verssion " + version);
+
+  return (Services.vc.compare(version, "10.6") < 0)
+});
+
+/**
+ * Checks for the existence of a platform specific application binary that can
+ * be used for the test and gets its path if it is found.
+ *
+ * Note: The application shell scripts for launching the application work on all
+ * platforms that provide a launch shell script except for Mac OS X 10.5 which
+ * is why this test uses the binaries to launch the application.
+ */
+XPCOMUtils.defineLazyGetter(this, "gAppBinPath", function test_gAppBinPath() {
+  let processDir = getAppDir();
+  let appBin = processDir.clone();
+  appBin.append(APP_BIN_NAME + APP_BIN_SUFFIX);
+  if (appBin.exists()) {
+    if (IS_WIN) {
+      let appBinCopy = processDir.clone();
+      appBinCopy.append(FILE_WIN_TEST_EXE);
+      if (appBinCopy.exists()) {
+        appBinCopy.remove(false);
+      }
+      appBin.copyTo(processDir, FILE_WIN_TEST_EXE);
+      appBin = processDir.clone();
+      appBin.append(FILE_WIN_TEST_EXE);
+    }
+    let appBinPath = appBin.path;
+    if (/ /.test(appBinPath)) {
+      appBinPath = '"' + appBinPath + '"';
+    }
+    return appBinPath;
+  }
+  return null;
+});
+
+let gWindowsBinDir = null;
+
+/**
+ * This dummy function just returns false.  Tests which wish to adjust the app
+ * directory on Mac OS X should define a real version of this function.
+ */
+function shouldAdjustPathsOnMac() {
+  return false;
+}
+
+/**
+ * This function returns the current process directory on Windows and Linux, and
+ * the application bundle directory on Mac.
+ */
+function getAppDir() {
+  let dir = getCurrentProcessDir();
+  if (shouldAdjustPathsOnMac()) {
+    // objdir/dist/bin/../NightlyDebug.app/Contents/MacOS
+    dir = dir.parent;
+    dir.append(BUNDLE_NAME);
+    dir.append("Contents");
+    dir.append("MacOS");
+  } else if (IS_WIN && gWindowsBinDir) {
+    dir = gWindowsBinDir.clone();
+  }
+  return dir;
+}
+
+/**
+ * Gets the nsIFile references for the shell script to retrieve the Mac OS X
+ * version and the nsIFile to pipe the output of the shell script. If either of
+ * these files exist they will be removed by this function.
+ *
+ * @return  array containing two nsIFile references. The first array member is
+ *          the nsIFile for the shell script to launch to get the Mac OS X
+ *          version and the second array member is the nsIFile for the piped
+ *          output from the shell script.
+ */
+function getVersionScriptAndFile() {
+  let versionScript = do_get_file("/", true);
+  let versionFile = versionScript.clone();
+  versionScript.append("get_version.sh");
+  if (versionScript.exists()) {
+    versionScript.remove(false);
+  }
+  versionFile.append("version.out");
+  if (versionFile.exists()) {
+    versionFile.remove(false);
+  }
+  return [versionScript, versionFile];
+}
+
+// Environment related globals
+let gShouldResetEnv = undefined;
+let gAddedEnvXRENoWindowsCrashDialog = false;
+let gEnvXPCOMDebugBreak;
+let gEnvXPCOMMemLeakLog;
+let gEnvDyldLibraryPath;
+let gEnvLdLibraryPath;
+let gEnvUpdateRootOverride = null;
+let gEnvAppDirOverride = null;
+
+/**
+ * Sets the environment that will be used by the application process when it is
+ * launched.
+ */
+function setEnvironment() {
+  // Prevent setting the environment more than once.
+  if (gShouldResetEnv !== undefined)
+    return;
+
+  gShouldResetEnv = true;
+
+  let env = AUS_Cc["@mozilla.org/process/environment;1"].
+            getService(AUS_Ci.nsIEnvironment);
+  if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) {
+    gAddedEnvXRENoWindowsCrashDialog = true;
+    logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
+                "variable to 1... previously it didn't exist");
+    env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1");
+  }
+
+  if (IS_UNIX) {
+    let appGreDir = Services.dirsvc.get("GreD", AUS_Ci.nsIFile);
+    let envGreDir = AUS_Cc["@mozilla.org/file/local;1"].
+                    createInstance(AUS_Ci.nsILocalFile);
+    let shouldSetEnv = true;
+    if (IS_MACOSX) {
+      if (env.exists("DYLD_LIBRARY_PATH")) {
+        gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH");
+        envGreDir.initWithPath(gEnvDyldLibraryPath);
+        if (envGreDir.path == appGreDir.path) {
+          gEnvDyldLibraryPath = null;
+          shouldSetEnv = false;
+        }
+      }
+
+      if (shouldSetEnv) {
+        logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " +
+                    appGreDir.path);
+        env.set("DYLD_LIBRARY_PATH", appGreDir.path);
+      }
+    }
+    else {
+      if (env.exists("LD_LIBRARY_PATH")) {
+        gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH");
+        envGreDir.initWithPath(gEnvLdLibraryPath);
+        if (envGreDir.path == appGreDir.path) {
+          gEnvLdLibraryPath = null;
+          shouldSetEnv = false;
+        }
+      }
+
+      if (shouldSetEnv) {
+        logTestInfo("setting LD_LIBRARY_PATH environment variable value to " +
+                    appGreDir.path);
+        env.set("LD_LIBRARY_PATH", appGreDir.path);
+      }
+    }
+  }
+
+  if (env.exists("XPCOM_MEM_LEAK_LOG")) {
+    gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG");
+    logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " +
+                "previous value " + gEnvXPCOMMemLeakLog);
+    env.set("XPCOM_MEM_LEAK_LOG", "");
+  }
+
+  if (env.exists("XPCOM_DEBUG_BREAK")) {
+    gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK");
+    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
+                "warn... previous value " + gEnvXPCOMDebugBreak);
+  }
+  else {
+    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
+                "warn... previously it didn't exist");
+  }
+
+  env.set("XPCOM_DEBUG_BREAK", "warn");
+
+  if (gEnvUpdateRootOverride) {
+    logTestInfo("setting the MOZ_UPDATE_ROOT_OVERRIDE environment variable to " +
+                gEnvUpdateRootOverride + "\n");
+    env.set("MOZ_UPDATE_ROOT_OVERRIDE", gEnvUpdateRootOverride);
+  }
+
+  if (gEnvAppDirOverride) {
+    logTestInfo("setting the MOZ_UPDATE_APPDIR_OVERRIDE environment variable to " +
+                gEnvAppDirOverride + "\n");
+    env.set("MOZ_UPDATE_APPDIR_OVERRIDE", gEnvAppDirOverride);
+  }
+}
+
+/**
+ * Sets the environment back to the original values after launching the
+ * application.
+ */
+function resetEnvironment() {
+  // Prevent resetting the environment more than once.
+  if (gShouldResetEnv !== true)
+    return;
+
+  gShouldResetEnv = false;
+
+  let env = AUS_Cc["@mozilla.org/process/environment;1"].
+            getService(AUS_Ci.nsIEnvironment);
+
+  if (gEnvXPCOMMemLeakLog) {
+    logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " +
+                gEnvXPCOMMemLeakLog);
+    env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog);
+  }
+
+  if (gEnvXPCOMDebugBreak) {
+    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " +
+                gEnvXPCOMDebugBreak);
+    env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak);
+  }
+  else {
+    logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable");
+    env.set("XPCOM_DEBUG_BREAK", "");
+  }
+
+  if (IS_UNIX) {
+    if (IS_MACOSX) {
+      if (gEnvDyldLibraryPath) {
+        logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " +
+                    "back to " + gEnvDyldLibraryPath);
+        env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath);
+      }
+      else {
+        logTestInfo("removing DYLD_LIBRARY_PATH environment variable");
+        env.set("DYLD_LIBRARY_PATH", "");
+      }
+    }
+    else {
+      if (gEnvLdLibraryPath) {
+        logTestInfo("setting LD_LIBRARY_PATH environment variable value back " +
+                    "to " + gEnvLdLibraryPath);
+        env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath);
+      }
+      else {
+        logTestInfo("removing LD_LIBRARY_PATH environment variable");
+        env.set("LD_LIBRARY_PATH", "");
+      }
+    }
+  }
+
+  if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) {
+    logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
+                "variable");
+    env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "");
+  }
+
+  if (gEnvUpdateRootOverride) {
+    logTestInfo("removing the MOZ_UPDATE_ROOT_OVERRIDE environment variable\n");
+    env.set("MOZ_UPDATE_ROOT_OVERRIDE", "");
+    gEnvUpdateRootOverride = null;
+  }
+
+  if (gEnvAppDirOverride) {
+    logTestInfo("removing the MOZ_UPDATE_APPDIR_OVERRIDE environment variable\n");
+    env.set("MOZ_UPDATE_APPDIR_OVERRIDE", "");
+    gEnvAppDirOverride = null;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/test/unit/test_0000_bootstrap_svc.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Bootstrap the tests using the service by installing our own version of the service */
+
+const TEST_ID = "0000_svc";
+
+const TEST_FILES = [
+{
+  description      : "the dummy file to make sure that the update worked",
+  fileName         : "dummy",
+  relPathDir       : "/",
+  originalContents : null,
+  compareContents  : "",
+  originalFile     : null,
+  compareFile      : null,
+  originalPerms    : null,
+  comparePerms     : null
+}
+];
+
+function run_test() {
+  do_test_pending();
+  do_register_cleanup(cleanupUpdaterTest);
+
+  setupUpdaterTest(MAR_COMPLETE_FILE);
+
+  let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
+  let applyToDir = getApplyDirFile();
+
+  // apply the complete mar
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied, null, false);
+}
+
+function checkUpdateApplied() {
+  checkFilesAfterUpdateSuccess();
+  do_test_finished();
+}
copy from toolkit/mozapps/update/test/unit/test_0110_general.js
copy to toolkit/mozapps/update/test/unit/test_0110_general_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0110_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0110_general_svc.js
@@ -10,21 +10,21 @@
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2008
+ * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Robert Strong <robert.bugzilla@gmail.com> (Original Author)
+ *   Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -33,17 +33,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK *****
  */
 
 /* General Complete MAR File Patch Apply Test */
 
-const TEST_ID = "0110";
+const TEST_ID = "0110_svc";
 // 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.
@@ -270,51 +270,27 @@ ADDITIONAL_TEST_DIRS = [
 
 function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   setupUpdaterTest(MAR_COMPLETE_FILE);
 
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  let applyToDir = getApplyDirFile();
-
-  // For Mac OS X set the last modified time for the root directory to a date in
-  // 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;
-  }
 
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a complete mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
-  // For Mac OS X check that the last modified time for a directory has been
-  // updated after a successful update (bug 600098).
-  if (IS_MACOSX) {
-    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) {
-    checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
-  }
+  checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0111_general.js
copy to toolkit/mozapps/update/test/unit/test_0111_general_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0111_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0111_general_svc.js
@@ -10,21 +10,21 @@
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2008
+ * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Robert Strong <robert.bugzilla@gmail.com> (Original Author)
+ *   Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -33,17 +33,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK *****
  */
 
 /* General Partial MAR File Patch Apply Test */
 
-const TEST_ID = "0111";
+const TEST_ID = "0111_svc";
 // 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.
@@ -272,56 +272,33 @@ ADDITIONAL_TEST_DIRS = [
 
 function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   setupUpdaterTest(MAR_PARTIAL_FILE);
 
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  let applyToDir = getApplyDirFile();
 
   // Check that trying to change channels for a partial update doesn't change
   // the update channel (the channel-prefs.js file should not be updated).
   let force = updatesDir.clone();
   force.append(CHANNEL_CHANGE_FILE);
   force.create(AUS_Ci.nsIFile.FILE_TYPE, PERMS_FILE);
 
-  // For Mac OS X set the last modified time for the root directory to a date in
-  // the past to test that the last modified time is updated on all updates since
-  // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
-  if (IS_MACOSX) {
-    let now = Date.now();
-    let yesterday = now - (1000 * 60 * 60 * 24);
-    applyToDir.lastModifiedTime = yesterday;
-  }
+  // apply the partial mar
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
-  // apply the partial mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a partial mar");
-  do_check_eq(exitValue, 0);
-
+function checkUpdateApplied() {
+  let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
-  // For Mac OS X check that the last modified time for a directory has been
-  // updated after a successful update (bug 600098).
-  if (IS_MACOSX) {
-    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) {
-    checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
-  }
+  checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0112_general.js
copy to toolkit/mozapps/update/test/unit/test_0112_general_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0112_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0112_general_svc.js
@@ -10,21 +10,21 @@
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2008
+ * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Robert Strong <robert.bugzilla@gmail.com> (Original Author)
+ *   Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -33,17 +33,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK *****
  */
 
 /* General Partial MAR File Patch Apply Failure Test */
 
-const TEST_ID = "0112";
+const TEST_ID = "0112_svc";
 // 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.
@@ -265,58 +265,35 @@ ADDITIONAL_TEST_DIRS = [
 
 function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   setupUpdaterTest(MAR_PARTIAL_FILE);
 
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  let applyToDir = getApplyDirFile();
 
   // Check that trying to change channels for a failed partial update doesn't
   // change the update channel (the channel-prefs.js file should not be updated).
   let force = updatesDir.clone();
   force.append(CHANNEL_CHANGE_FILE);
   force.create(AUS_Ci.nsIFile.FILE_TYPE, PERMS_FILE);
 
-  // For Mac OS X set the last modified time for the root directory to a date in
-  // the past to test that the last modified time is updated on all updates since
-  // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
-  if (IS_MACOSX) {
-    let now = Date.now();
-    let yesterday = now - (1000 * 60 * 60 * 24);
-    applyToDir.lastModifiedTime = yesterday;
-  }
+  // apply the partial mar
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED, checkUpdateApplied);
+}
 
-  // apply the partial mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a partial mar");
-  do_check_eq(exitValue, 0);
-
+function checkUpdateApplied() {
+  let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   logTestInfo("testing update.status should be " + STATE_FAILED);
   // 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);
 
-  // For Mac OS X check that the last modified time for a directory has been
-  // updated after a successful update (bug 600098).
-  if (IS_MACOSX) {
-    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);
-  }
-
   checkFilesAfterUpdateFailure();
-  // Sorting on Linux is different so skip this check for now.
-  if (!IS_UNIX) {
-    checkUpdateLogContents(LOG_PARTIAL_FAILURE);
-  }
+  checkUpdateLogContents(LOG_PARTIAL_FAILURE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0120_channelChange_complete.js
copy to toolkit/mozapps/update/test/unit/test_0120_channelChange_complete_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0120_channelChange_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0120_channelChange_complete_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 /* Channel change complete MAR file patch apply test */
 
-const TEST_ID = "0120";
+const TEST_ID = "0120_svc";
 // 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 in the same order as they are applied from the mar
 const TEST_FILES = [
 {
@@ -233,57 +233,32 @@ ADDITIONAL_TEST_DIRS = [
 
 function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   setupUpdaterTest(MAR_COMPLETE_FILE);
 
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
-  let applyToDir = getApplyDirFile();
 
   // Check that trying to change channels for a complete update changes the
   // update channel (the channel-prefs.js file should be updated).
   let channelchange = updatesDir.clone();
   channelchange.append(CHANNEL_CHANGE_FILE);
   channelchange.create(AUS_Ci.nsIFile.FILE_TYPE, PERMS_FILE);
 
-  // For Mac OS X set the last modified time for the root directory to a date in
-  // 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;
-  }
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
-  // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a complete mar");
-  do_check_eq(exitValue, 0);
-
+function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
-  // For Mac OS X check that the last modified time for a directory has been
-  // updated after a successful update (bug 600098).
-  if (IS_MACOSX) {
-    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) {
-    checkUpdateLogContents(LOG_COMPLETE_CC_SUCCESS);
-  }
+  checkUpdateLogContents(LOG_COMPLETE_CC_SUCCESS);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0150_appBinReplaced_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0150_appBinReplaced_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0150_appBinReplaced_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0150_appBinReplaced_xp_win_complete_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 /* Replace app binary complete MAR file patch apply success test */
 
-const TEST_ID = "0150";
+const TEST_ID = "0150_svc";
 const MAR_COMPLETE_WIN_FILE = "data/complete_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      : "Only added by update.manifest for complete updates " +
@@ -195,25 +195,24 @@ function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   setupUpdaterTest(MAR_COMPLETE_WIN_FILE);
 
   gCallbackBinFile = "exe0.exe";
 
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a complete mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
   checkFilesAfterUpdateSuccess();
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0151_appBinPatched_xp_win_partial.js
copy to toolkit/mozapps/update/test/unit/test_0151_appBinPatched_xp_win_partial_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0151_appBinPatched_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0151_appBinPatched_xp_win_partial_svc.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 /* Patch app binary partial MAR file patch apply success test */
 
-const TEST_ID = "0151";
+const TEST_ID = "0151_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      : "Only added by update.manifest for complete updates " +
@@ -197,25 +197,24 @@ function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   setupUpdaterTest(MAR_IN_USE_WIN_FILE);
 
   gCallbackBinFile = "exe0.exe";
 
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a partial mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
   checkFilesAfterUpdateSuccess();
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0160_appInUse_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0160_appInUse_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0160_appInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0160_appInUse_xp_win_complete_svc.js
@@ -1,192 +1,198 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 /* Application in use complete MAR file patch apply failure test */
 
-const TEST_ID = "0160";
+const TEST_ID = "0160_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      : "Only added by update.manifest for complete updates " +
                      "when there is a channel change (add-cc)",
   fileName         : "channel-prefs.js",
   relPathDir       : "a/b/defaults/pref/",
   originalContents : "ShouldNotBeReplaced\n",
   compareContents  : "ShouldNotBeReplaced\n",
   originalFile     : null,
   compareFile      : null
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add)",
   fileName         : "precomplete",
   relPathDir       : "",
   originalContents : null,
   compareContents  : null,
   originalFile     : "data/partial_precomplete",
-  compareFile      : "data/partial_precomplete"
+  compareFile      : "data/complete_precomplete"
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add)",
   fileName         : "searchpluginstext0",
   relPathDir       : "a/b/searchplugins/",
-  originalContents : "ShouldNotBeReplaced\n",
-  compareContents  : "ShouldNotBeReplaced\n",
+  originalContents : "ToBeReplacedWithFromComplete\n",
+  compareContents  : "FromComplete\n",
   originalFile     : null,
   compareFile      : null
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add)",
   fileName         : "searchpluginspng1.png",
   relPathDir       : "a/b/searchplugins/",
   originalContents : null,
   compareContents  : null,
-  originalFile     : "data/partial.png",
-  compareFile      : "data/partial.png"
+  originalFile     : null,
+  compareFile      : "data/complete.png"
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add)",
   fileName         : "searchpluginspng0.png",
   relPathDir       : "a/b/searchplugins/",
   originalContents : null,
   compareContents  : null,
   originalFile     : "data/partial.png",
-  compareFile      : "data/partial.png"
+  compareFile      : "data/complete.png"
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add)",
   fileName         : "removed-files",
   relPathDir       : "a/b/",
   originalContents : null,
   compareContents  : null,
   originalFile     : "data/partial_removed-files",
-  compareFile      : "data/partial_removed-files"
+  compareFile      : "data/complete_removed-files"
 }, {
-  description      : "Not added for failed update (add-if)",
+  description      : "Added by update.manifest if the parent directory " +
+                     "exists (add-if)",
   fileName         : "extensions1text0",
   relPathDir       : "a/b/extensions/extensions1/",
   originalContents : null,
-  compareContents  : null,
+  compareContents  : "FromComplete\n",
   originalFile     : null,
   compareFile      : null
 }, {
-  description      : "Not added for failed update (add-if)",
+  description      : "Added by update.manifest if the parent directory " +
+                     "exists (add-if)",
   fileName         : "extensions1png1.png",
   relPathDir       : "a/b/extensions/extensions1/",
   originalContents : null,
   compareContents  : null,
-  originalFile     : null,
-  compareFile      : null
+  originalFile     : "data/partial.png",
+  compareFile      : "data/complete.png"
 }, {
-  description      : "Not added for failed update (add-if)",
+  description      : "Added by update.manifest if the parent directory " +
+                     "exists (add-if)",
   fileName         : "extensions1png0.png",
   relPathDir       : "a/b/extensions/extensions1/",
   originalContents : null,
   compareContents  : null,
   originalFile     : null,
-  compareFile      : null
+  compareFile      : "data/complete.png"
 }, {
-  description      : "Not added for failed update (add-if)",
+  description      : "Added by update.manifest if the parent directory " +
+                     "exists (add-if)",
   fileName         : "extensions0text0",
   relPathDir       : "a/b/extensions/extensions0/",
-  originalContents : "ShouldNotBeReplaced\n",
-  compareContents  : "ShouldNotBeReplaced\n",
+  originalContents : "ToBeReplacedWithFromComplete\n",
+  compareContents  : "FromComplete\n",
   originalFile     : null,
   compareFile      : null
 }, {
-  description      : "Not added for failed update (add-if)",
+  description      : "Added by update.manifest if the parent directory " +
+                     "exists (add-if)",
   fileName         : "extensions0png1.png",
   relPathDir       : "a/b/extensions/extensions0/",
   originalContents : null,
   compareContents  : null,
-  originalFile     : "data/partial.png",
-  compareFile      : "data/partial.png"
+  originalFile     : null,
+  compareFile      : "data/complete.png"
 }, {
-  description      : "Not added for failed update (add-if)",
+  description      : "Added by update.manifest if the parent directory " +
+                     "exists (add-if)",
   fileName         : "extensions0png0.png",
   relPathDir       : "a/b/extensions/extensions0/",
   originalContents : null,
   compareContents  : null,
   originalFile     : null,
-  compareFile      : null
+  compareFile      : "data/complete.png"
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add)",
   fileName         : "exe0.exe",
   relPathDir       : "a/b/",
   originalContents : null,
   compareContents  : null,
-  originalFile     : "data/partial.png",
-  compareFile      : "data/partial.png"
+  originalFile     : "data/partial_in_use_win_before.exe",
+  compareFile      : "data/complete.png"
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add)",
   fileName         : "10text0",
   relPathDir       : "a/b/1/10/",
-  originalContents : "ShouldNotBeReplaced\n",
-  compareContents  : "ShouldNotBeReplaced\n",
+  originalContents : "ToBeReplacedWithFromComplete\n",
+  compareContents  : "FromComplete\n",
   originalFile     : null,
   compareFile      : null
 }, {
-  description      : "Not added for failed update (add)",
+  description      : "Added by update.manifest (add) file in use",
   fileName         : "0exe0.exe",
   relPathDir       : "a/b/0/",
   originalContents : null,
   compareContents  : null,
+  originalFile     : "data/partial_in_use_win_after.exe",
+  compareFile      : "data/complete.png"
+}, {
+  description      : "Added by update.manifest (add)",
+  fileName         : "00text1",
+  relPathDir       : "a/b/0/00/",
+  originalContents : "ToBeReplacedWithFromComplete\n",
+  compareContents  : "FromComplete\n",
+  originalFile     : null,
+  compareFile      : null
+}, {
+  description      : "Added by update.manifest (add)",
+  fileName         : "00text0",
+  relPathDir       : "a/b/0/00/",
+  originalContents : "ToBeReplacedWithFromComplete\n",
+  compareContents  : "FromComplete\n",
+  originalFile     : null,
+  compareFile      : null
+}, {
+  description      : "Added by update.manifest (add)",
+  fileName         : "00png0.png",
+  relPathDir       : "a/b/0/00/",
+  originalContents : null,
+  compareContents  : null,
+  originalFile     : null,
+  compareFile      : "data/complete.png"
+}, {
+  description      : "Removed by precomplete (remove)",
+  fileName         : "20text0",
+  relPathDir       : "a/b/2/20/",
+  originalContents : "ToBeDeleted\n",
+  compareContents  : null,
   originalFile     : null,
   compareFile      : null
 }, {
-  description      : "Not added for failed update (add)",
-  fileName         : "00text1",
-  relPathDir       : "a/b/0/00/",
-  originalContents : "ShouldNotBeReplaced\n",
-  compareContents  : "ShouldNotBeReplaced\n",
-  originalFile     : null,
-  compareFile      : null
-}, {
-  description      : "Not added for failed update (add)",
-  fileName         : "00text0",
-  relPathDir       : "a/b/0/00/",
-  originalContents : "ShouldNotBeReplaced\n",
-  compareContents  : "ShouldNotBeReplaced\n",
-  originalFile     : null,
-  compareFile      : null
-}, {
-  description      : "Not added for failed update (add)",
-  fileName         : "00png0.png",
-  relPathDir       : "a/b/0/00/",
-  originalContents : null,
-  compareContents  : null,
-  originalFile     : "data/partial.png",
-  compareFile      : "data/partial.png"
-}, {
-  description      : "Not removed for failed update (remove)",
-  fileName         : "20text0",
-  relPathDir       : "a/b/2/20/",
-  originalContents : "ShouldNotBeDeleted\n",
-  compareContents  : "ShouldNotBeDeleted\n",
-  originalFile     : null,
-  compareFile      : null
-}, {
-  description      : "Not removed for failed update (remove)",
+  description      : "Removed by precomplete (remove)",
   fileName         : "20png0.png",
   relPathDir       : "a/b/2/20/",
-  originalContents : "ShouldNotBeDeleted\n",
-  compareContents  : "ShouldNotBeDeleted\n",
+  originalContents : "ToBeDeleted\n",
+  compareContents  : null,
   originalFile     : null,
   compareFile      : null
 }];
 
 ADDITIONAL_TEST_DIRS = [
 {
-  description  : "Not removed for failed update (rmdir)",
+  description  : "Removed for complete update (rmdir)",
   relPathDir   : "a/b/2/20/",
-  dirRemoved   : false
+  dirRemoved   : true
 }, {
-  description  : "Not removed for failed update (rmdir)",
+  description  : "Removed for complete update (rmdir)",
   relPathDir   : "a/b/2/",
-  dirRemoved   : false
+  dirRemoved   : true
 }];
 
 function run_test() {
   do_test_pending();
   do_register_cleanup(cleanupUpdaterTest);
 
   setupUpdaterTest(MAR_COMPLETE_FILE);
 
@@ -198,32 +204,31 @@ function run_test() {
   callbackAppProcess.init(callbackApp);
   callbackAppProcess.run(false, args, args.length);
 
   do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
 }
 
 function doUpdate() {
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for failure when " +
-              "applying a complete mar");
-  do_check_eq(exitValue, 1);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  logTestInfo("testing update.status should be " + STATE_FAILED);
+  logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   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).split(": ")[0], STATE_SUCCEEDED);
 
-  checkFilesAfterUpdateFailure();
-  checkUpdateLogContains(ERR_CALLBACK_FILE_IN_USE);
+  checkFilesAfterUpdateSuccess();
+  checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0170_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 patch apply failure test */
 
-const TEST_ID = "0170";
+const TEST_ID = "0170_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      : "Only added by update.manifest for complete updates " +
                      "when there is a channel change (add-cc)",
@@ -206,21 +206,20 @@ function run_test() {
   lockFileProcess.init(helperBin);
   lockFileProcess.run(false, args, args.length);
 
   do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
 }
 
 function doUpdate() {
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a complete mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   setupHelperFinish();
 }
 
 function checkUpdate() {
   logTestInfo("testing update.status should be " + STATE_FAILED);
   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.
@@ -228,10 +227,10 @@ function checkUpdate() {
 
   checkFilesAfterUpdateFailure();
   checkUpdateLogContains(ERR_RENAME_FILE);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial.js
copy to toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0171_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 patch apply failure test */
 
-const TEST_ID = "0171";
+const TEST_ID = "0171_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      : "Only added by update.manifest for complete updates " +
                      "when there is a channel change (add-cc)",
@@ -207,21 +207,20 @@ function run_test() {
   lockFileProcess.init(helperBin);
   lockFileProcess.run(false, args, args.length);
 
   do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
 }
 
 function doUpdate() {
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a partial mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   setupHelperFinish();
 }
 
 function checkUpdate() {
   logTestInfo("testing update.status should be " + STATE_FAILED);
   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.
@@ -229,10 +228,10 @@ function checkUpdate() {
 
   checkFilesAfterUpdateFailure();
   checkUpdateLogContains(ERR_UNABLE_OPEN_DEST);
 
   logTestInfo("testing tobedeleted directory doesn't exist");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_false(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0180_fileInUse_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0180_fileInUse_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0180_fileInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0180_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 patch apply success test */
 
-const TEST_ID = "0180";
+const TEST_ID = "0180_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      : "Only added by update.manifest for complete updates " +
                      "when there is a channel change (add-cc)",
@@ -205,30 +205,29 @@ function run_test() {
   fileInUseProcess.init(fileInUseBin);
   fileInUseProcess.run(false, args, args.length);
 
   do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
 }
 
 function doUpdate() {
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a complete mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   setupHelperFinish();
 }
 
 function checkUpdate() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
   checkFilesAfterUpdateSuccess();
   checkUpdateLogContains(ERR_BACKUP_DISCARD);
 
   logTestInfo("testing tobedeleted directory exists");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_true(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0181_fileInUse_xp_win_partial.js
copy to toolkit/mozapps/update/test/unit/test_0181_fileInUse_xp_win_partial_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0181_fileInUse_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0181_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 patch apply success test */
 
-const TEST_ID = "0181";
+const TEST_ID = "0181_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      : "Only added by update.manifest for complete updates " +
@@ -208,30 +208,29 @@ function run_test() {
   fileInUseProcess.init(fileInUseBin);
   fileInUseProcess.run(false, args, args.length);
 
   do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
 }
 
 function doUpdate() {
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a partial mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   setupHelperFinish();
 }
 
 function checkUpdate() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
   checkFilesAfterUpdateSuccess();
   checkUpdateLogContains(ERR_BACKUP_DISCARD);
 
   logTestInfo("testing tobedeleted directory exists");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_true(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0182_rmrfdirFileInUse_xp_win_complete.js
copy to toolkit/mozapps/update/test/unit/test_0182_rmrfdirFileInUse_xp_win_complete_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0182_rmrfdirFileInUse_xp_win_complete.js
+++ b/toolkit/mozapps/update/test/unit/test_0182_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 patch apply success test */
 
-const TEST_ID = "0182";
+const TEST_ID = "0182_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      : "Only added by update.manifest for complete updates " +
                      "when there is a channel change (add-cc)",
@@ -215,30 +215,29 @@ function run_test() {
   fileInUseProcess.init(fileInUseBin);
   fileInUseProcess.run(false, args, args.length);
 
   do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
 }
 
 function doUpdate() {
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a complete mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   setupHelperFinish();
 }
 
 function checkUpdate() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
   checkFilesAfterUpdateSuccess();
   checkUpdateLogContains(ERR_BACKUP_DISCARD);
 
   logTestInfo("testing tobedeleted directory exists");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_true(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
copy from toolkit/mozapps/update/test/unit/test_0183_rmrfdirFileInUse_xp_win_partial.js
copy to toolkit/mozapps/update/test/unit/test_0183_rmrfdirFileInUse_xp_win_partial_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0183_rmrfdirFileInUse_xp_win_partial.js
+++ b/toolkit/mozapps/update/test/unit/test_0183_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 patch apply success test */
 
-const TEST_ID = "0183";
+const TEST_ID = "0183_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      : "Only added by update.manifest for complete updates " +
@@ -256,30 +256,29 @@ function run_test() {
   fileInUseProcess.init(fileInUseBin);
   fileInUseProcess.run(false, args, args.length);
 
   do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
 }
 
 function doUpdate() {
   // apply the complete mar
-  let exitValue = runUpdate();
-  logTestInfo("testing updater binary process exitValue for success when " +
-              "applying a partial mar");
-  do_check_eq(exitValue, 0);
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
+}
 
+function checkUpdateApplied() {
   setupHelperFinish();
 }
 
 function checkUpdate() {
   logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
   let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
   do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
 
   checkFilesAfterUpdateSuccess();
   checkUpdateLogContains(ERR_BACKUP_DISCARD);
 
   logTestInfo("testing tobedeleted directory exists");
   let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
   do_check_true(toBeDeletedDir.exists());
 
-  checkCallbackAppLog();
+  checkCallbackServiceLog();
 }
--- a/toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
+++ b/toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
@@ -4,113 +4,30 @@
 
 /* Test applying an update by staging an update and launching an application */
 
 /**
  * The MAR file used for this test should not contain a version 2 update
  * manifest file (e.g. updatev2.manifest).
  */
 
-// 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";
-
 // 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;
 
 let gAppTimer;
 let gProcess;
 
-// Environment related globals
-let gShouldResetEnv = undefined;
-let gAddedEnvXRENoWindowsCrashDialog = false;
-let gEnvXPCOMDebugBreak;
-let gEnvXPCOMMemLeakLog;
-let gEnvDyldLibraryPath;
-let gEnvLdLibraryPath;
-
-// A shell script is used to get the OS version due to nsSystemInfo not
-// returning the actual OS version. It is possible to get the actual OS version
-// using ctypes but it would be more complicated than using a shell script.
-XPCOMUtils.defineLazyGetter(this, "gIsLessThanMacOSX_10_6", function test_gMacVer() {
-  if (!IS_MACOSX) {
-    return false;
-  }
-
-  let [versionScript, versionFile] = getVersionScriptAndFile();
-  // Precreate the script with executable permissions
-  versionScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
-  let scriptContents = "#! /bin/sh\nsw_vers -productVersion >> " + versionFile.path;
-  writeFile(versionScript, scriptContents);
-  logTestInfo("created " + versionScript.path + " shell script containing:\n" +
-              scriptContents);
-
-  let versionScriptPath = versionScript.path;
-  if (/ /.test(versionScriptPath)) {
-    versionScriptPath = '"' + versionScriptPath + '"';
-  }
-
-
-  let launchBin = getLaunchBin();
-  let args = [versionScriptPath];
-  let process = AUS_Cc["@mozilla.org/process/util;1"].
-                createInstance(AUS_Ci.nsIProcess);
-  process.init(launchBin);
-  process.run(true, args, args.length);
-  if (process.exitValue != 0) {
-    do_throw("Version script exited with " + process.exitValue + "... unable " +
-             "to get Mac OS X version!");
-  }
-
-  let version = readFile(versionFile).split("\n")[0];
-  logTestInfo("executing on Mac OS X verssion " + version);
-
-  return (Services.vc.compare(version, "10.6") < 0)
-});
-
-/**
- * Checks for the existence of a platform specific application binary that can
- * be used for the test and gets its path if it is found.
- *
- * Note: The application shell scripts for launching the application work on all
- * platforms that provide a launch shell script except for Mac OS X 10.5 which
- * is why this test uses the binaries to launch the application.
- */
-XPCOMUtils.defineLazyGetter(this, "gAppBinPath", function test_gAppBinPath() {
-  let processDir = getCurrentProcessDir();
-  let appBin = processDir.clone();
-  appBin.append(APP_BIN_NAME + APP_BIN_SUFFIX);
-  if (appBin.exists()) {
-    if (IS_WIN) {
-      let appBinCopy = processDir.clone();
-      appBinCopy.append(FILE_WIN_TEST_EXE);
-      if (appBinCopy.exists()) {
-        appBinCopy.remove(false);
-      }
-      appBin.copyTo(processDir, FILE_WIN_TEST_EXE);
-      appBin = processDir.clone();
-      appBin.append(FILE_WIN_TEST_EXE);
-    }
-    let appBinPath = appBin.path;
-    if (/ /.test(appBinPath)) {
-      appBinPath = '"' + appBinPath + '"';
-    }
-    return appBinPath;
-  }
-  return null;
-});
-
 function run_test() {
   do_test_pending();
   do_register_cleanup(end_test);
 
   removeUpdateDirsAndFiles();
 
   if (!gAppBinPath) {
     do_throw("Main application binary not found... expected: " +
@@ -288,283 +205,32 @@ let gTimerCallback = {
       gProcess.kill();
     }
     do_throw("launch application timer expired");
   },
   QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsITimerCallback])
 };
 
 /**
- * Sets the environment that will be used by the application process when it is
- * launched.
- */
-function setEnvironment() {
-  // Prevent setting the environment more than once.
-  if (gShouldResetEnv !== undefined)
-    return;
-
-  gShouldResetEnv = true;
-
-  let env = AUS_Cc["@mozilla.org/process/environment;1"].
-            getService(AUS_Ci.nsIEnvironment);
-  if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) {
-    gAddedEnvXRENoWindowsCrashDialog = true;
-    logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
-                "variable to 1... previously it didn't exist");
-    env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1");
-  }
-
-  if (IS_UNIX) {
-    let appGreDir = Services.dirsvc.get("GreD", AUS_Ci.nsIFile);
-    let envGreDir = AUS_Cc["@mozilla.org/file/local;1"].
-                    createInstance(AUS_Ci.nsILocalFile);
-    let shouldSetEnv = true;
-    if (IS_MACOSX) {
-      if (env.exists("DYLD_LIBRARY_PATH")) {
-        gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH");
-        envGreDir.initWithPath(gEnvDyldLibraryPath);
-        if (envGreDir.path == appGreDir.path) {
-          gEnvDyldLibraryPath = null;
-          shouldSetEnv = false;
-        }
-      }
-
-      if (shouldSetEnv) {
-        logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " +
-                    appGreDir.path);
-        env.set("DYLD_LIBRARY_PATH", appGreDir.path);
-      }
-    }
-    else {
-      if (env.exists("LD_LIBRARY_PATH")) {
-        gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH");
-        envGreDir.initWithPath(gEnvLdLibraryPath);
-        if (envGreDir.path == appGreDir.path) {
-          gEnvLdLibraryPath = null;
-          shouldSetEnv = false;
-        }
-      }
-
-      if (shouldSetEnv) {
-        logTestInfo("setting LD_LIBRARY_PATH environment variable value to " +
-                    appGreDir.path);
-        env.set("LD_LIBRARY_PATH", appGreDir.path);
-      }
-    }
-  }
-
-  if (env.exists("XPCOM_MEM_LEAK_LOG")) {
-    gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG");
-    logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " +
-                "previous value " + gEnvXPCOMMemLeakLog);
-    env.set("XPCOM_MEM_LEAK_LOG", "");
-  }
-
-  if (env.exists("XPCOM_DEBUG_BREAK")) {
-    gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK");
-    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
-                "warn... previous value " + gEnvXPCOMDebugBreak);
-  }
-  else {
-    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
-                "warn... previously it didn't exist");
-  }
-
-  env.set("XPCOM_DEBUG_BREAK", "warn");
-}
-
-/**
- * Sets the environment back to the original values after launching the
- * application.
- */
-function resetEnvironment() {
-  // Prevent resetting the environment more than once.
-  if (gShouldResetEnv !== true)
-    return;
-
-  gShouldResetEnv = false;
-
-  let env = AUS_Cc["@mozilla.org/process/environment;1"].
-            getService(AUS_Ci.nsIEnvironment);
-
-  if (gEnvXPCOMMemLeakLog) {
-    logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " +
-                gEnvXPCOMMemLeakLog);
-    env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog);
-  }
-
-  if (gEnvXPCOMDebugBreak) {
-    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " +
-                gEnvXPCOMDebugBreak);
-    env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak);
-  }
-  else {
-    logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable");
-    env.set("XPCOM_DEBUG_BREAK", "");
-  }
-
-  if (IS_UNIX) {
-    if (IS_MACOSX) {
-      if (gEnvDyldLibraryPath) {
-        logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " +
-                    "back to " + gEnvDyldLibraryPath);
-        env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath);
-      }
-      else {
-        logTestInfo("removing DYLD_LIBRARY_PATH environment variable");
-        env.set("DYLD_LIBRARY_PATH", "");
-      }
-    }
-    else {
-      if (gEnvLdLibraryPath) {
-        logTestInfo("setting LD_LIBRARY_PATH environment variable value back " +
-                    "to " + gEnvLdLibraryPath);
-        env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath);
-      }
-      else {
-        logTestInfo("removing LD_LIBRARY_PATH environment variable");
-        env.set("LD_LIBRARY_PATH", "");
-      }
-    }
-  }
-
-  if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) {
-    logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
-                "variable");
-    env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "");
-  }
-}
-
-/**
- * Returns the platform specific arguments used by nsIProcess when launching
- * the application.
- *
- * @return  an array of arguments to be passed to nsIProcess.
- *
- * Notes:
- * 1. Mozilla universal binaries that contain both i386 and x86_64 on Mac OS X
- *    10.5.x must be launched using the i386 architecture.
- * 2. A shell is necessary to pipe the application's console output which
- *    would otherwise pollute the xpcshell log.
- *
- * Command line arguments used when launching the application:
- * -no-remote prevents shell integration from being affected by an existing
- * application process.
- * -process-updates makes the application exits after being relaunched by the
- * updater.
- * 1> pipes stdout to a file.
- * appConsoleLogPath is the file path to pipe the output from the shell.
- * Otherwise the output from the application will end up in the xpchsell log.
- * 2>&1 pipes stderr to sdout.
- */
-function getProcessArgs() {
-  // Pipe the output from the launched application to a file so the output from
-  // its console isn't present in the xpcshell log.
-  let appConsoleLogPath = getAppConsoleLogPath();
-
-  let args;
-  if (IS_UNIX) {
-    let launchScript = getLaunchScript();
-    // Precreate the script with executable permissions
-    launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
-
-    let scriptContents = "#! /bin/sh\n";
-    // On Mac OS X versions prior to 10.6 the i386 acrhitecture must be used.
-    if (gIsLessThanMacOSX_10_6) {
-      scriptContents += "arch -arch i386 ";
-    }
-    scriptContents += gAppBinPath + " -no-remote -process-updates 1> " +
-                      appConsoleLogPath + " 2>&1";
-    writeFile(launchScript, scriptContents);
-    logTestInfo("created " + launchScript.path + " containing:\n" +
-                scriptContents);
-    args = [launchScript.path];
-  }
-  else {
-    args = ["/D", "/Q", "/C", gAppBinPath, "-no-remote", "-process-updates",
-            "1>", appConsoleLogPath, "2>&1"];
-  }
-  return args;
-}
-
-/**
  * Gets the directory where the update adds / removes the files contained in the
  * update.
  *
  * @return  nsIFile for the directory where the update adds / removes the files
  *          contained in the update mar.
  */
 function getUpdateTestDir() {
   let updateTestDir = getCurrentProcessDir();
   if (IS_MACOSX) {
     updateTestDir = updateTestDir.parent.parent;
   }
   updateTestDir.append("update_test");
   return updateTestDir;
 }
 
 /**
- * Gets a file path for piping the console output from the application so it
- * doesn't appear in the xpcshell log file.
- *
- * @return  path to the file for piping the console output from the application.
- */
-function getAppConsoleLogPath() {
-  let appConsoleLog = do_get_file("/", true);
-  appConsoleLog.append("app_console_log");
-  if (appConsoleLog.exists()) {
-    appConsoleLog.remove(false);
-  }
-  let appConsoleLogPath = appConsoleLog.path;
-  if (/ /.test(appConsoleLogPath)) {
-    appConsoleLogPath = '"' + appConsoleLogPath + '"';
-  }
-  return appConsoleLogPath;
-}
-
-/**
- * Gets the nsIFile references for the shell script to retrieve the Mac OS X
- * version and the nsIFile to pipe the output of the shell script. If either of
- * these files exist they will be removed by this function.
- *
- * @return  array containing two nsIFile references. The first array member is
- *          the nsIFile for the shell script to launch to get the Mac OS X
- *          version and the second array member is the nsIFile for the piped
- *          output from the shell script.
- */
-function getVersionScriptAndFile() {
-  let versionScript = do_get_file("/", true);
-  let versionFile = versionScript.clone();
-  versionScript.append("get_version.sh");
-  if (versionScript.exists()) {
-    versionScript.remove(false);
-  }
-  versionFile.append("version.out");
-  if (versionFile.exists()) {
-    versionFile.remove(false);
-  }
-  return [versionScript, versionFile];
-}
-
-/**
- * Gets the nsIFile reference for the shell script to launch the application. If
- * the file exists it will be removed by this function.
- *
- * @return  the nsIFile for the shell script to launch the application.
- */
-function getLaunchScript() {
-  let launchScript = do_get_file("/", true);
-  launchScript.append("launch.sh");
-  if (launchScript.exists()) {
-    launchScript.remove(false);
-  }
-  return launchScript;
-}
-
-/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function checkUpdateFinished() {
   // Don't proceed until the update.log has been created.
   let log = getUpdatesDir();
   log.append("0");
   log.append(FILE_UPDATE_LOG);
copy from toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
copy to toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update_svc.js
--- a/toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update.js
+++ b/toolkit/mozapps/update/test/unit/test_0200_app_launch_apply_update_svc.js
@@ -4,113 +4,27 @@
 
 /* Test applying an update by staging an update and launching an application */
 
 /**
  * The MAR file used for this test should not contain a version 2 update
  * manifest file (e.g. updatev2.manifest).
  */
 
-// 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";
-
 // 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;
 
-let gAppTimer;
-let gProcess;
-
-// Environment related globals
-let gShouldResetEnv = undefined;
-let gAddedEnvXRENoWindowsCrashDialog = false;
-let gEnvXPCOMDebugBreak;
-let gEnvXPCOMMemLeakLog;
-let gEnvDyldLibraryPath;
-let gEnvLdLibraryPath;
-
-// A shell script is used to get the OS version due to nsSystemInfo not
-// returning the actual OS version. It is possible to get the actual OS version
-// using ctypes but it would be more complicated than using a shell script.
-XPCOMUtils.defineLazyGetter(this, "gIsLessThanMacOSX_10_6", function test_gMacVer() {
-  if (!IS_MACOSX) {
-    return false;
-  }
-
-  let [versionScript, versionFile] = getVersionScriptAndFile();
-  // Precreate the script with executable permissions
-  versionScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
-  let scriptContents = "#! /bin/sh\nsw_vers -productVersion >> " + versionFile.path;
-  writeFile(versionScript, scriptContents);
-  logTestInfo("created " + versionScript.path + " shell script containing:\n" +
-              scriptContents);
-
-  let versionScriptPath = versionScript.path;
-  if (/ /.test(versionScriptPath)) {
-    versionScriptPath = '"' + versionScriptPath + '"';
-  }
-
-
-  let launchBin = getLaunchBin();
-  let args = [versionScriptPath];
-  let process = AUS_Cc["@mozilla.org/process/util;1"].
-                createInstance(AUS_Ci.nsIProcess);
-  process.init(launchBin);
-  process.run(true, args, args.length);
-  if (process.exitValue != 0) {
-    do_throw("Version script exited with " + process.exitValue + "... unable " +
-             "to get Mac OS X version!");
-  }
-
-  let version = readFile(versionFile).split("\n")[0];
-  logTestInfo("executing on Mac OS X verssion " + version);
-
-  return (Services.vc.compare(version, "10.6") < 0)
-});
-
-/**
- * Checks for the existence of a platform specific application binary that can
- * be used for the test and gets its path if it is found.
- *
- * Note: The application shell scripts for launching the application work on all
- * platforms that provide a launch shell script except for Mac OS X 10.5 which
- * is why this test uses the binaries to launch the application.
- */
-XPCOMUtils.defineLazyGetter(this, "gAppBinPath", function test_gAppBinPath() {
-  let processDir = getCurrentProcessDir();
-  let appBin = processDir.clone();
-  appBin.append(APP_BIN_NAME + APP_BIN_SUFFIX);
-  if (appBin.exists()) {
-    if (IS_WIN) {
-      let appBinCopy = processDir.clone();
-      appBinCopy.append(FILE_WIN_TEST_EXE);
-      if (appBinCopy.exists()) {
-        appBinCopy.remove(false);
-      }
-      appBin.copyTo(processDir, FILE_WIN_TEST_EXE);
-      appBin = processDir.clone();
-      appBin.append(FILE_WIN_TEST_EXE);
-    }
-    let appBinPath = appBin.path;
-    if (/ /.test(appBinPath)) {
-      appBinPath = '"' + appBinPath + '"';
-    }
-    return appBinPath;
-  }
-  return null;
-});
-
 function run_test() {
   do_test_pending();
   do_register_cleanup(end_test);
 
   removeUpdateDirsAndFiles();
 
   if (!gAppBinPath) {
     do_throw("Main application binary not found... expected: " +
@@ -130,17 +44,16 @@ function run_test() {
   let processDir = getCurrentProcessDir();
   let file = processDir.clone();
   file.append("application.ini");
   let ini = AUS_Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
             getService(AUS_Ci.nsIINIParserFactory).
             createINIParser(file);
   let version = ini.getString("App", "Version");
   writeVersionFile(version);
-  writeStatusFile(STATE_PENDING);
 
   // This is the directory where the update files will be located
   let updateTestDir = getUpdateTestDir();
   try {
     removeDirRecursive(updateTestDir);
   }
   catch (e) {
     logTestInfo("unable to remove directory - path: " + updateTestDir.path +
@@ -180,424 +93,122 @@ function run_test() {
   let updaterIniContents = "[Strings]\n" +
                            "Title=Update Test\n" +
                            "Info=Application Update XPCShell Test - " +
                            "test_0200_general.js\n";
   updaterIni = processDir.clone();
   updaterIni.append(FILE_UPDATER_INI);
   writeFile(updaterIni, updaterIniContents);
 
-  let launchBin = getLaunchBin();
-  let args = getProcessArgs();
-  logTestInfo("launching " + launchBin.path + " " + args.join(" "));
-
-  gProcess = AUS_Cc["@mozilla.org/process/util;1"].
-                createInstance(AUS_Ci.nsIProcess);
-  gProcess.init(launchBin);
+  let updatesRootDir = processDir.clone();
+  updatesRootDir.append("updates");
+  updatesRootDir.append("0");
+  getApplyDirPath = function() {
+    return processDir.path;
+  }
+  getApplyDirFile = function (aRelPath, allowNonexistent) {
+    let base = AUS_Cc["@mozilla.org/file/local;1"].
+               createInstance(AUS_Ci.nsILocalFile);
+    base.initWithPath(getApplyDirPath());
+    let path = (aRelPath ? aRelPath : "");
+    let bits = path.split("/");
+    for (let i = 0; i < bits.length; i++) {
+      if (bits[i]) {
+        if (bits[i] == "..")
+          base = base.parent;
+        else
+          base.append(bits[i]);
+      }
+    }
 
-  gAppTimer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
-  gAppTimer.initWithCallback(gTimerCallback, APP_TIMER_TIMEOUT,
-                             AUS_Ci.nsITimer.TYPE_ONE_SHOT);
+    if (!allowNonexistent && !base.exists()) {
+      _passed = false;
+      var stack = Components.stack.caller;
+      _dump("TEST-UNEXPECTED-FAIL | " + stack.filename + " | [" +
+            stack.name + " : " + stack.lineNumber + "] " + base.path +
+            " does not exist\n");
+    }
 
-  setEnvironment();
-
-  gProcess.runAsync(args, args.length, gProcessObserver);
-
-  resetEnvironment();
+    return base;
+  }
+  runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateFinished, updatesRootDir);
 }
 
 function end_test() {
-  if (gProcess.isRunning) {
-    logTestInfo("attempt to kill process");
-    gProcess.kill();
-  }
-
-  if (gAppTimer) {
-    logTestInfo("cancelling timer");
-    gAppTimer.cancel();
-    gAppTimer = null;
-  }
-
   resetEnvironment();
 
   let processDir = getCurrentProcessDir();
   // Restore the backed up updater.ini
   let updaterIni = processDir.clone();
   updaterIni.append(FILE_UPDATER_INI_BAK);
   updaterIni.moveTo(processDir, FILE_UPDATER_INI);
 
-  if (IS_WIN) {
-    // Remove the copy of the application executable used for the test on
-    // Windows if it exists.
-    let appBinCopy = processDir.clone();
-    appBinCopy.append(FILE_WIN_TEST_EXE);
-    if (appBinCopy.exists()) {
-      appBinCopy.remove(false);
-    }
+  // Remove the copy of the application executable used for the test on
+  // Windows if it exists.
+  let appBinCopy = processDir.clone();
+  appBinCopy.append(FILE_WIN_TEST_EXE);
+  if (appBinCopy.exists()) {
+    appBinCopy.remove(false);
   }
 
   // Remove the files added by the update.
   let updateTestDir = getUpdateTestDir();
   try {
     logTestInfo("removing update test directory " + updateTestDir.path);
     removeDirRecursive(updateTestDir);
   }
   catch (e) {
     logTestInfo("unable to remove directory - path: " + updateTestDir.path +
                 ", exception: " + e);
   }
 
   // This will delete the app console log file if it exists.
   getAppConsoleLogPath();
 
-  if (IS_UNIX) {
-    // This will delete the launch script if it exists.
-    getLaunchScript();
-    if (IS_MACOSX) {
-      // This will delete the version script and version file if they exist.
-      getVersionScriptAndFile();
-    }
-  }
-
   cleanUp();
 }
 
 /**
- * The observer for the call to nsIProcess:runAsync.
- */
-let gProcessObserver = {
-  observe: function PO_observe(subject, topic, data) {
-    logTestInfo("topic " + topic + ", process exitValue " + gProcess.exitValue);
-    if (gAppTimer) {
-      gAppTimer.cancel();
-      gAppTimer = null;
-    }
-    if (topic != "process-finished" || gProcess.exitValue != 0) {
-      do_throw("Failed to launch application");
-    }
-    do_timeout(CHECK_TIMEOUT_MILLI, checkUpdateFinished);
-  },
-  QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver])
-};
-
-/**
- * The timer callback to kill the process if it takes too long.
- */
-let gTimerCallback = {
-  notify: function TC_notify(aTimer) {
-    gAppTimer = null;
-    if (gProcess.isRunning) {
-      gProcess.kill();
-    }
-    do_throw("launch application timer expired");
-  },
-  QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsITimerCallback])
-};
-
-/**
- * Sets the environment that will be used by the application process when it is
- * launched.
- */
-function setEnvironment() {
-  // Prevent setting the environment more than once.
-  if (gShouldResetEnv !== undefined)
-    return;
-
-  gShouldResetEnv = true;
-
-  let env = AUS_Cc["@mozilla.org/process/environment;1"].
-            getService(AUS_Ci.nsIEnvironment);
-  if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) {
-    gAddedEnvXRENoWindowsCrashDialog = true;
-    logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
-                "variable to 1... previously it didn't exist");
-    env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1");
-  }
-
-  if (IS_UNIX) {
-    let appGreDir = Services.dirsvc.get("GreD", AUS_Ci.nsIFile);
-    let envGreDir = AUS_Cc["@mozilla.org/file/local;1"].
-                    createInstance(AUS_Ci.nsILocalFile);
-    let shouldSetEnv = true;
-    if (IS_MACOSX) {
-      if (env.exists("DYLD_LIBRARY_PATH")) {
-        gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH");
-        envGreDir.initWithPath(gEnvDyldLibraryPath);
-        if (envGreDir.path == appGreDir.path) {
-          gEnvDyldLibraryPath = null;
-          shouldSetEnv = false;
-        }
-      }
-
-      if (shouldSetEnv) {
-        logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " +
-                    appGreDir.path);
-        env.set("DYLD_LIBRARY_PATH", appGreDir.path);
-      }
-    }
-    else {
-      if (env.exists("LD_LIBRARY_PATH")) {
-        gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH");
-        envGreDir.initWithPath(gEnvLdLibraryPath);
-        if (envGreDir.path == appGreDir.path) {
-          gEnvLdLibraryPath = null;
-          shouldSetEnv = false;
-        }
-      }
-
-      if (shouldSetEnv) {
-        logTestInfo("setting LD_LIBRARY_PATH environment variable value to " +
-                    appGreDir.path);
-        env.set("LD_LIBRARY_PATH", appGreDir.path);
-      }
-    }
-  }
-
-  if (env.exists("XPCOM_MEM_LEAK_LOG")) {
-    gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG");
-    logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " +
-                "previous value " + gEnvXPCOMMemLeakLog);
-    env.set("XPCOM_MEM_LEAK_LOG", "");
-  }
-
-  if (env.exists("XPCOM_DEBUG_BREAK")) {
-    gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK");
-    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
-                "warn... previous value " + gEnvXPCOMDebugBreak);
-  }
-  else {
-    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
-                "warn... previously it didn't exist");
-  }
-
-  env.set("XPCOM_DEBUG_BREAK", "warn");
-}
-
-/**
- * Sets the environment back to the original values after launching the
- * application.
- */
-function resetEnvironment() {
-  // Prevent resetting the environment more than once.
-  if (gShouldResetEnv !== true)
-    return;
-
-  gShouldResetEnv = false;
-
-  let env = AUS_Cc["@mozilla.org/process/environment;1"].
-            getService(AUS_Ci.nsIEnvironment);
-
-  if (gEnvXPCOMMemLeakLog) {
-    logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " +
-                gEnvXPCOMMemLeakLog);
-    env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog);
-  }
-
-  if (gEnvXPCOMDebugBreak) {
-    logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " +
-                gEnvXPCOMDebugBreak);
-    env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak);
-  }
-  else {
-    logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable");
-    env.set("XPCOM_DEBUG_BREAK", "");
-  }
-
-  if (IS_UNIX) {
-    if (IS_MACOSX) {
-      if (gEnvDyldLibraryPath) {
-        logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " +
-                    "back to " + gEnvDyldLibraryPath);
-        env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath);
-      }
-      else {
-        logTestInfo("removing DYLD_LIBRARY_PATH environment variable");
-        env.set("DYLD_LIBRARY_PATH", "");
-      }
-    }
-    else {
-      if (gEnvLdLibraryPath) {
-        logTestInfo("setting LD_LIBRARY_PATH environment variable value back " +
-                    "to " + gEnvLdLibraryPath);
-        env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath);
-      }
-      else {
-        logTestInfo("removing LD_LIBRARY_PATH environment variable");
-        env.set("LD_LIBRARY_PATH", "");
-      }
-    }
-  }
-
-  if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) {
-    logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
-                "variable");
-    env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "");
-  }
-}
-
-/**
- * Returns the platform specific arguments used by nsIProcess when launching
- * the application.
- *
- * @return  an array of arguments to be passed to nsIProcess.
- *
- * Notes:
- * 1. Mozilla universal binaries that contain both i386 and x86_64 on Mac OS X
- *    10.5.x must be launched using the i386 architecture.
- * 2. A shell is necessary to pipe the application's console output which
- *    would otherwise pollute the xpcshell log.
- *
- * Command line arguments used when launching the application:
- * -no-remote prevents shell integration from being affected by an existing
- * application process.
- * -process-updates makes the application exits after being relaunched by the
- * updater.
- * 1> pipes stdout to a file.
- * appConsoleLogPath is the file path to pipe the output from the shell.
- * Otherwise the output from the application will end up in the xpchsell log.
- * 2>&1 pipes stderr to sdout.
- */
-function getProcessArgs() {
-  // Pipe the output from the launched application to a file so the output from
-  // its console isn't present in the xpcshell log.
-  let appConsoleLogPath = getAppConsoleLogPath();
-
-  let args;
-  if (IS_UNIX) {
-    let launchScript = getLaunchScript();
-    // Precreate the script with executable permissions
-    launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
-
-    let scriptContents = "#! /bin/sh\n";
-    // On Mac OS X versions prior to 10.6 the i386 acrhitecture must be used.
-    if (gIsLessThanMacOSX_10_6) {
-      scriptContents += "arch -arch i386 ";
-    }
-    scriptContents += gAppBinPath + " -no-remote -process-updates 1> " +
-                      appConsoleLogPath + " 2>&1";
-    writeFile(launchScript, scriptContents);
-    logTestInfo("created " + launchScript.path + " containing:\n" +
-                scriptContents);
-    args = [launchScript.path];
-  }
-  else {
-    args = ["/D", "/Q", "/C", gAppBinPath, "-no-remote", "-process-updates",
-            "1>", appConsoleLogPath, "2>&1"];
-  }
-  return args;
-}
-
-/**
  * Gets the directory where the update adds / removes the files contained in the
  * update.
  *
  * @return  nsIFile for the directory where the update adds / removes the files
  *          contained in the update mar.
  */
 function getUpdateTestDir() {
   let updateTestDir = getCurrentProcessDir();
-  if (IS_MACOSX) {
-    updateTestDir = updateTestDir.parent.parent;
-  }
   updateTestDir.append("update_test");
   return updateTestDir;
 }
 
 /**
- * Gets a file path for piping the console output from the application so it
- * doesn't appear in the xpcshell log file.
- *
- * @return  path to the file for piping the console output from the application.
- */
-function getAppConsoleLogPath() {
-  let appConsoleLog = do_get_file("/", true);
-  appConsoleLog.append("app_console_log");
-  if (appConsoleLog.exists()) {
-    appConsoleLog.remove(false);
-  }
-  let appConsoleLogPath = appConsoleLog.path;
-  if (/ /.test(appConsoleLogPath)) {
-    appConsoleLogPath = '"' + appConsoleLogPath + '"';
-  }
-  return appConsoleLogPath;
-}
-
-/**
- * Gets the nsIFile references for the shell script to retrieve the Mac OS X
- * version and the nsIFile to pipe the output of the shell script. If either of
- * these files exist they will be removed by this function.
- *
- * @return  array containing two nsIFile references. The first array member is
- *          the nsIFile for the shell script to launch to get the Mac OS X
- *          version and the second array member is the nsIFile for the piped
- *          output from the shell script.
- */
-function getVersionScriptAndFile() {
-  let versionScript = do_get_file("/", true);
-  let versionFile = versionScript.clone();
-  versionScript.append("get_version.sh");
-  if (versionScript.exists()) {
-    versionScript.remove(false);
-  }
-  versionFile.append("version.out");
-  if (versionFile.exists()) {
-    versionFile.remove(false);
-  }
-  return [versionScript, versionFile];
-}
-
-/**
- * Gets the nsIFile reference for the shell script to launch the application. If
- * the file exists it will be removed by this function.
- *
- * @return  the nsIFile for the shell script to launch the application.
- */
-function getLaunchScript() {
-  let launchScript = do_get_file("/", true);
-  launchScript.append("launch.sh");
-  if (launchScript.exists()) {
-    launchScript.remove(false);
-  }
-  return launchScript;
-}
-
-/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function checkUpdateFinished() {
   // Don't proceed until the update.log has been created.
   let log = getUpdatesDir();
   log.append("0");
   log.append(FILE_UPDATE_LOG);
   if (!log.exists()) {
     do_timeout(CHECK_TIMEOUT_MILLI, checkUpdateFinished);
     return;
   }
 
-  // Don't proceed until the update status is no longer pending or applying.
-  let status = readStatusFile();
-  if (status == STATE_PENDING || status == STATE_APPLYING) {
-    do_timeout(CHECK_TIMEOUT_MILLI, checkUpdateFinished);
-    return;
-  }
-
   // Log the contents of the update.log so it is simpler to diagnose a test
   // failure. For example, on Windows if the application binary is in use the
   // updater will not apply the update.
   let contents = readFile(log);
   logTestInfo("contents of " + log.path + ":\n" +  
               contents.replace(/\r\n/g, "\n"));
 
-  if (IS_WIN && contents.indexOf("NS_main: file in use") != -1) {
+  if (contents.indexOf("NS_main: file in use") != -1) {
     do_throw("the application can't be in use when running this test");
   }
 
-  do_check_eq(status, STATE_SUCCEEDED);
-
   standardInit();
 
   let update = gUpdateManager.getUpdateAt(0);
   do_check_eq(update.state, STATE_SUCCEEDED);
 
   let updateTestDir = getUpdateTestDir();
 
   let file = updateTestDir.clone();
--- a/toolkit/mozapps/update/test/unit/xpcshell.ini
+++ b/toolkit/mozapps/update/test/unit/xpcshell.ini
@@ -23,8 +23,11 @@ tail =
 skip-if = os == 'android'
 ; Platform-specific updater tests
 [include:xpcshell_updater_windows.ini]
 run-if = os == 'win'
 [include:xpcshell_updater_xp_unix.ini]
 run-if = os == 'linux' || os == 'mac'
 [test_bug497578.js]
 [test_bug595059.js]
+; Tests using the maintenance service
+[include:xpcshell_updater_windows_svc.ini]
+run-if = os == 'win'
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/test/unit/xpcshell_updater_windows_svc.ini
@@ -0,0 +1,15 @@
+[test_0000_bootstrap_svc.js]
+[test_0110_general_svc.js]
+[test_0111_general_svc.js]
+[test_0112_general_svc.js]
+[test_0120_channelChange_complete_svc.js]
+[test_0150_appBinReplaced_xp_win_complete_svc.js]
+[test_0151_appBinPatched_xp_win_partial_svc.js]
+[test_0160_appInUse_xp_win_complete_svc.js]
+[test_0170_fileLocked_xp_win_complete_svc.js]
+[test_0171_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_0200_app_launch_apply_update_svc.js]
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3147,16 +3147,30 @@ XRE_main(int argc, char* argv[], const n
     ProcessUpdates(dirProvider.GetGREDir(),
                    dirProvider.GetAppDir(),
                    updRoot,
                    gRestartArgc,
                    gRestartArgv,
                    appData.version);
     if (EnvHasValue("MOZ_PROCESS_UPDATES")) {
       SaveToEnv("MOZ_PROCESS_UPDATES=");
+
+      // If the caller has asked us to log our arguments, do so.  This is used
+      // to make sure that the maintenance service successfully launches the
+      // callback application.
+      const char *logFile = nsnull;
+      if (ARG_FOUND == CheckArg("dump-args", false, &logFile)) {
+        FILE* logFP = fopen(logFile, "wb");
+        if (logFP) {
+          for (i = 1; i < gRestartArgc; ++i) {
+            fprintf(logFP, "%s\n", gRestartArgv[i]);
+          }
+          fclose(logFP);
+        }
+      }
       return 0;
     }
 #endif
 
     nsCOMPtr<nsIProfileLock> profileLock;
     bool startOffline = false;
     nsCAutoString profileName;
 
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -553,16 +553,43 @@ ProcessUpdates(nsIFile *greDir, nsIFile 
 
   rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
   if (NS_FAILED(rv))
     return rv;
 
   rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
   if (NS_FAILED(rv))
     return rv;
+ 
+  const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES");
+  if (processingUpdates && *processingUpdates) {
+    // Enable the tests to request us to use a different update root directory
+    const char *updRootOverride = PR_GetEnv("MOZ_UPDATE_ROOT_OVERRIDE");
+    if (updRootOverride && *updRootOverride) {
+      nsCOMPtr<nsILocalFile> overrideDir;
+      nsCAutoString path(updRootOverride);
+      rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(overrideDir));
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+      updatesDir = do_QueryInterface(overrideDir);
+    }
+    // Enable the tests to request us to use a different app directory
+    const char *appDirOverride = PR_GetEnv("MOZ_UPDATE_APPDIR_OVERRIDE");
+    if (appDirOverride && *appDirOverride) {
+      nsCOMPtr<nsILocalFile> overrideDir;
+      nsCAutoString path(appDirOverride);
+      rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(overrideDir));
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+      NS_RELEASE(appDir);
+      NS_ADDREF(appDir = overrideDir);
+    }
+  }
 
   nsCOMPtr<nsILocalFile> statusFile;
   bool isPendingService;
   if (GetStatusFile(updatesDir, statusFile) && 
       IsPending(statusFile, isPendingService)) {
     nsCOMPtr<nsILocalFile> versionFile;
     nsCOMPtr<nsILocalFile> channelChangeFile;
     // Remove the update if the update application version file doesn't exist
--- a/xpcom/io/SpecialSystemDirectory.cpp
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -767,16 +767,20 @@ GetSpecialSystemDirectory(SystemDirector
         case Win_Common_Startup:
         {
             return GetWindowsFolder(CSIDL_COMMON_STARTUP, aFile);
         }
         case Win_Common_Desktopdirectory:
         {
             return GetWindowsFolder(CSIDL_COMMON_DESKTOPDIRECTORY, aFile);
         }
+        case Win_Common_AppData:
+        {
+            return GetWindowsFolder(CSIDL_COMMON_APPDATA, aFile);
+        }
         case Win_Printhood:
         {
             return GetWindowsFolder(CSIDL_PRINTHOOD, aFile);
         }
         case Win_Cookies:
         {
             return GetWindowsFolder(CSIDL_COOKIES, aFile);
         }
--- a/xpcom/io/SpecialSystemDirectory.h
+++ b/xpcom/io/SpecialSystemDirectory.h
@@ -104,16 +104,17 @@ enum SystemDirectories {
   Win_Common_Startup        =   223,   
   Win_Common_Desktopdirectory = 224,   
   Win_Appdata               =   225,   
   Win_Printhood             =   226,   
   Win_Cookies               =   227, 
   Win_LocalAppdata          =   228,
   Win_ProgramFiles          =   229,
   Win_Downloads             =   230,
+  Win_Common_AppData        =   231,
   
   Unix_LocalDirectory       =   301,   
   Unix_LibDirectory         =   302,   
   Unix_HomeDirectory        =   303,
   Unix_XDG_Desktop          =   304,
   Unix_XDG_Documents        =   305,
   Unix_XDG_Download         =   306,
   Unix_XDG_Music            =   307,
--- a/xpcom/io/nsDirectoryService.cpp
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -852,16 +852,20 @@ nsDirectoryService::GetFile(const char *
     else if (inAtom == nsDirectoryService::sCommon_Startup)
     {
         rv = GetSpecialSystemDirectory(Win_Common_Startup, getter_AddRefs(localFile)); 
     }
     else if (inAtom == nsDirectoryService::sCommon_Desktopdirectory)
     {
         rv = GetSpecialSystemDirectory(Win_Common_Desktopdirectory, getter_AddRefs(localFile)); 
     }
+    else if (inAtom == nsDirectoryService::sCommon_AppData)
+    {
+        rv = GetSpecialSystemDirectory(Win_Common_AppData, getter_AddRefs(localFile)); 
+    }
     else if (inAtom == nsDirectoryService::sAppdata)
     {
         rv = GetSpecialSystemDirectory(Win_Appdata, getter_AddRefs(localFile)); 
     }
     else if (inAtom == nsDirectoryService::sLocalAppdata)
     {
         rv = GetSpecialSystemDirectory(Win_LocalAppdata, getter_AddRefs(localFile)); 
     }
--- a/xpcom/io/nsDirectoryServiceAtomList.h
+++ b/xpcom/io/nsDirectoryServiceAtomList.h
@@ -95,16 +95,17 @@ DIR_ATOM(sDrives, NS_WIN_DRIVES_DIR)
 DIR_ATOM(sNetwork, NS_WIN_NETWORK_DIR)
 DIR_ATOM(sNethood, NS_WIN_NETHOOD_DIR)
 DIR_ATOM(sFonts, NS_WIN_FONTS_DIR)
 DIR_ATOM(sTemplates, NS_WIN_TEMPLATES_DIR)
 DIR_ATOM(sCommon_Startmenu, NS_WIN_COMMON_STARTMENU_DIR)
 DIR_ATOM(sCommon_Programs, NS_WIN_COMMON_PROGRAMS_DIR)
 DIR_ATOM(sCommon_Startup, NS_WIN_COMMON_STARTUP_DIR)
 DIR_ATOM(sCommon_Desktopdirectory, NS_WIN_COMMON_DESKTOP_DIRECTORY)
+DIR_ATOM(sCommon_AppData, NS_WIN_COMMON_APPDATA_DIR)
 DIR_ATOM(sAppdata, NS_WIN_APPDATA_DIR)
 DIR_ATOM(sLocalAppdata, NS_WIN_LOCAL_APPDATA_DIR)
 DIR_ATOM(sPrinthood, NS_WIN_PRINTHOOD)
 DIR_ATOM(sWinCookiesDirectory, NS_WIN_COOKIES_DIR)
 DIR_ATOM(sDefaultDownloadDirectory, NS_WIN_DEFAULT_DOWNLOAD_DIR)
 #elif defined (XP_UNIX)
 DIR_ATOM(sLocalDirectory, NS_UNIX_LOCAL_DIR)
 DIR_ATOM(sLibDirectory, NS_UNIX_LIB_DIR)
--- a/xpcom/io/nsDirectoryServiceDefs.h
+++ b/xpcom/io/nsDirectoryServiceDefs.h
@@ -144,16 +144,17 @@
     #define NS_WIN_NETWORK_DIR                  "NetW"
     #define NS_WIN_NETHOOD_DIR                  "netH"
     #define NS_WIN_FONTS_DIR                    "Fnts"
     #define NS_WIN_TEMPLATES_DIR                "Tmpls"
     #define NS_WIN_COMMON_STARTMENU_DIR         "CmStrt"
     #define NS_WIN_COMMON_PROGRAMS_DIR          "CmPrgs"
     #define NS_WIN_COMMON_STARTUP_DIR           "CmStrt"
     #define NS_WIN_COMMON_DESKTOP_DIRECTORY     "CmDeskP"
+    #define NS_WIN_COMMON_APPDATA_DIR           "CmAppData"
     #define NS_WIN_APPDATA_DIR                  "AppData"
     #define NS_WIN_LOCAL_APPDATA_DIR            "LocalAppData"
     #define NS_WIN_PRINTHOOD                    "PrntHd"
     #define NS_WIN_COOKIES_DIR                  "CookD"
     #define NS_WIN_DEFAULT_DOWNLOAD_DIR         "DfltDwnld"
 #elif defined (XP_UNIX)
     #define NS_UNIX_LOCAL_DIR                   "Locl"
     #define NS_UNIX_LIB_DIR                     "LibD"