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 86211 7e565fc8516cfd7485721da1405fddf1603aeca1
parent 86210 1bd9f069576e63b5527ea9b4d99b7f3aca1bd5c4
child 86212 d5637e69d71ed290e6a7d7892b486a657f2a98ed
push id674
push userffxbld
push dateTue, 13 Mar 2012 21:17:50 +0000
treeherdermozilla-beta@e3c4c92dec31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong
bugs481815
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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"