Bug 692255 - Get rid of prefetch files on Windows for faster startup. r=rstrong
authorBrian R. Bondy <netzen@gmail.com>
Sun, 27 May 2012 22:40:48 -0400
changeset 95066 4dd2d5f25910f940557cd728ef645a05afb0f116
parent 95065 4c3f2ddd82e8c35cbababbbb2004571aea077be4
child 95067 21ae85ff42865880dbaaf3d1fbc6713dcf500acf
push id22777
push userbbondy@mozilla.com
push dateMon, 28 May 2012 02:41:10 +0000
treeherdermozilla-central@93018b4bf4da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong
bugs692255
milestone15.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 692255 - Get rid of prefetch files on Windows for faster startup. r=rstrong
browser/components/shell/src/nsWindowsShellService.cpp
browser/components/shell/src/nsWindowsShellService.h
browser/installer/windows/nsis/shared.nsh
toolkit/components/maintenanceservice/Makefile.in
toolkit/components/maintenanceservice/maintenanceservice.cpp
toolkit/components/maintenanceservice/prefetch.cpp
toolkit/components/maintenanceservice/prefetch.h
toolkit/components/maintenanceservice/registrycertificates.cpp
toolkit/components/maintenanceservice/workmonitor.cpp
toolkit/mozapps/update/common/Makefile.in
toolkit/mozapps/update/common/pathhash.h
toolkit/mozapps/update/common/updatehelper.cpp
toolkit/mozapps/update/common/updatehelper.h
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -21,16 +21,17 @@
 #include "nsBrowserCompsCID.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIWindowsRegKey.h"
 #include "nsUnicharUtils.h"
 #include "nsIWinTaskbar.h"
 #include "nsISupportsPrimitives.h"
+#include "nsThreadUtils.h"
 
 #include "windows.h"
 #include "shellapi.h"
 
 #ifdef _WIN32_WINNT
 #undef _WIN32_WINNT
 #endif
 #define _WIN32_WINNT 0x0600
@@ -47,16 +48,21 @@
 #define REG_SUCCEEDED(val) \
   (val == ERROR_SUCCESS)
 
 #define REG_FAILED(val) \
   (val != ERROR_SUCCESS)
 
 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
 
+// We clear the prefetch files one time after the browser is started after
+// 60 seconds.  After this is done once we set a pref so this will never happen
+// again except in updater code.
+#define CLEAR_PREFETCH_TIMEOUT_MS 60000
+
 NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
 
 static nsresult
 OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
 {
   const nsString &flatName = PromiseFlatString(aKeyName);
 
   DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
@@ -184,16 +190,26 @@ static SETTING gDDESettings[] = {
   { MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
 
   // Protocol Handlers
   { MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
   { MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
   { MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
 };
 
+#if defined(MOZ_MAINTENANCE_SERVICE)
+
+#define ONLY_SERVICE_LAUNCHING
+#include "updatehelper.h"
+#include "updatehelper.cpp"
+
+static const char kPrefetchClearedPref[] = "app.update.service.prefetchCleared";
+static nsCOMPtr<nsIThread> sThread;
+#endif
+
 nsresult
 GetHelperPath(nsAutoString& aPath)
 {
   nsresult rv;
   nsCOMPtr<nsIProperties> directoryService = 
     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -903,16 +919,133 @@ nsWindowsShellService::SetDesktopBackgro
 
   rv = regKey->WriteStringValue(NS_LITERAL_STRING("Background"),
                                 nsDependentString(rgb));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return regKey->Close();
 }
 
+nsWindowsShellService::nsWindowsShellService() : 
+  mCheckedThisSession(false) 
+{
+#if defined(MOZ_MAINTENANCE_SERVICE)
+
+  // Check to make sure the service is installed
+  PRUint32 installed = 0;
+  nsCOMPtr<nsIWindowsRegKey> regKey = 
+    do_CreateInstance("@mozilla.org/windows-registry-key;1");
+  if (!regKey || 
+      NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+                             NS_LITERAL_STRING(
+                               "SOFTWARE\\Mozilla\\MaintenanceService"),
+                             nsIWindowsRegKey::ACCESS_READ |
+                             nsIWindowsRegKey::WOW64_64)) ||
+      NS_FAILED(regKey->ReadIntValue(NS_LITERAL_STRING("Installed"), 
+                &installed)) ||
+      !installed) {
+    return;
+  }
+
+  // check to see if we have attempted to do the one time operation of clearing
+  // the prefetch.
+  bool prefetchCleared;
+  nsCOMPtr<nsIPrefBranch> prefBranch;
+  nsCOMPtr<nsIPrefService> prefs =
+    do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (!prefs || 
+      NS_FAILED(prefs->GetBranch(nsnull, getter_AddRefs(prefBranch))) ||
+      (NS_SUCCEEDED(prefBranch->GetBoolPref(kPrefetchClearedPref, 
+                                            &prefetchCleared)) &&
+       prefetchCleared)) {
+    return;
+  }
+
+  // In a minute after startup is definitely complete, launch the
+  // service command.
+  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  if (mTimer) {
+    mTimer->InitWithFuncCallback(
+      nsWindowsShellService::LaunchPrefetchClearCommand, 
+      nsnull, CLEAR_PREFETCH_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
+  }
+#endif
+}
+
+nsWindowsShellService::~nsWindowsShellService()
+{
+#if defined(MOZ_MAINTENANCE_SERVICE)
+ if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nsnull;
+  }
+  if (sThread) {
+    sThread->Shutdown();
+    sThread = nsnull;
+  }
+#endif
+}
+
+#if defined(MOZ_MAINTENANCE_SERVICE)
+
+class ClearPrefetchEvent : public nsRunnable {
+public:
+  ClearPrefetchEvent()
+  {
+  }
+
+  NS_IMETHOD Run() 
+  {
+    // Start the service command
+    LPCWSTR updaterServiceArgv[2];
+    updaterServiceArgv[0] = L"MozillaMaintenance";
+    updaterServiceArgv[1] = L"clear-prefetch";
+    // If this command fails, it is not critical as prefetch will be cleared
+    // on the next software update.
+    StartServiceCommand(NS_ARRAY_LENGTH(updaterServiceArgv), 
+                        updaterServiceArgv);
+    return NS_OK;
+  }
+};
+#endif
+
+/**
+ * For faster startup we attempt to clear the prefetch if the maintenance
+ * service is installed.  Please see the definition of ClearPrefetch()
+ * in toolkit/components/maintenanceservice/prefetch.cpp for more info.
+ * For now the only application that gets prefetch cleaned is Firefox
+ * since we have not done performance checking for other applications.
+ * This is done on every update but also there is a one time operation done
+ * from within the program for first time installs.
+ */ 
+#if defined(MOZ_MAINTENANCE_SERVICE)
+void
+nsWindowsShellService::LaunchPrefetchClearCommand(nsITimer *aTimer, void*)
+{
+  // Make sure we don't call this again from the application, it will be
+  // called on each application update instead.
+  nsCOMPtr<nsIPrefBranch> prefBranch;
+  nsCOMPtr<nsIPrefService> prefs =
+    do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (prefs) {
+    if (NS_SUCCEEDED(prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)))) {
+      prefBranch->SetBoolPref(kPrefetchClearedPref, true);
+    }
+  }
+
+  // Starting the sevice can take a bit of time and we don't want to block the 
+  // main thread, so start an event on another thread to handle the operation
+  NS_NewThread(getter_AddRefs(sThread));
+  if (sThread) {
+    nsCOMPtr<nsIRunnable> prefetchEvent = new ClearPrefetchEvent();
+    sThread->Dispatch(prefetchEvent, NS_DISPATCH_NORMAL);
+  }
+}
+#endif
+
 NS_IMETHODIMP
 nsWindowsShellService::OpenApplicationWithURI(nsILocalFile* aApplication,
                                               const nsACString& aURI)
 {
   nsresult rv;
   nsCOMPtr<nsIProcess> process = 
     do_CreateInstance("@mozilla.org/process/util;1", &rv);
   if (NS_FAILED(rv))
--- a/browser/components/shell/src/nsWindowsShellService.h
+++ b/browser/components/shell/src/nsWindowsShellService.h
@@ -4,30 +4,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nswindowsshellservice_h____
 #define nswindowsshellservice_h____
 
 #include "nscore.h"
 #include "nsStringAPI.h"
 #include "nsIWindowsShellService.h"
+#include "nsITimer.h"
 
 #include <windows.h>
 #include <ole2.h>
 
 class nsWindowsShellService : public nsIWindowsShellService
 {
 public:
-  nsWindowsShellService() : mCheckedThisSession(false) {}; 
-  virtual ~nsWindowsShellService() {};
+  nsWindowsShellService();
+  virtual ~nsWindowsShellService();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHELLSERVICE
   NS_DECL_NSIWINDOWSSHELLSERVICE
 
 protected:
   bool      IsDefaultBrowserVista(bool* aIsDefaultBrowser);
 
 private:
+#if defined(MOZ_MAINTENANCE_SERVICE)
+  static void LaunchPrefetchClearCommand(nsITimer *aTimer, void*);
+  nsCOMPtr<nsITimer> mTimer;
+#endif
   bool      mCheckedThisSession;
 };
 
 #endif // nswindowsshellservice_h____
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -582,16 +582,17 @@
     ; for example: $R0\1, but each individual binary can be signed
     ; with at most one certificate.  A fallback certificate can only be used
     ; if the binary is replaced with a different certificate.
     ; We always use the 64bit registry for certs.
     ${If} ${RunningX64}
       SetRegView 64
     ${EndIf}
     DeleteRegKey HKLM "$R0"
+    WriteRegStr HKLM "$R0" "prefetchProcessName" "FIREFOX"
     WriteRegStr HKLM "$R0\0" "name" "${CERTIFICATE_NAME}"
     WriteRegStr HKLM "$R0\0" "issuer" "${CERTIFICATE_ISSUER}"
     ${If} ${RunningX64}
       SetRegView lastused
     ${EndIf}
     ClearErrors
   ${EndIf} 
   ; Restore the previously used value back
--- a/toolkit/components/maintenanceservice/Makefile.in
+++ b/toolkit/components/maintenanceservice/Makefile.in
@@ -11,16 +11,17 @@ include $(DEPTH)/config/autoconf.mk
 
 CPPSRCS = \
   maintenanceservice.cpp \
   serviceinstall.cpp \
   workmonitor.cpp \
   certificatecheck.cpp \
   servicebase.cpp \
   registrycertificates.cpp \
+  prefetch.cpp \
   $(NULL)
 
 # For debugging purposes only
 #DEFINES += -DDISABLE_UPDATER_AUTHENTICODE_CHECK
 
 PROGRAM = maintenanceservice$(BIN_SUFFIX)
 DIST_PROGRAM = maintenanceservice$(BIN_SUFFIX)
 
--- a/toolkit/components/maintenanceservice/maintenanceservice.cpp
+++ b/toolkit/components/maintenanceservice/maintenanceservice.cpp
@@ -9,28 +9,41 @@
 #include <shlobj.h>
 
 #include "serviceinstall.h"
 #include "maintenanceservice.h"
 #include "servicebase.h"
 #include "workmonitor.h"
 #include "uachelper.h"
 #include "updatehelper.h"
+#include "prefetch.h"
 
 SERVICE_STATUS gSvcStatus = { 0 }; 
 SERVICE_STATUS_HANDLE gSvcStatusHandle = NULL; 
 HANDLE gWorkDoneEvent = NULL;
 HANDLE gThread = NULL;
 bool gServiceControlStopping = false;
 
 // logs are pretty small, about 20 lines, so 10 seems reasonable.
 #define LOGS_TO_KEEP 10
 
 BOOL GetLogDirectoryPath(WCHAR *path);
 
+/**
+ * Wraps all commands that should be executed by the service on each install
+ * and upgrade.
+*/
+void
+RunCommandsForEachInstallAndUpgrade()
+{
+  LOG(("Running install/upgrade commands...\n"));
+  ClearKnownPrefetch();
+  LOG(("Finished install/upgrade commands\n"));
+}
+
 int 
 wmain(int argc, WCHAR **argv)
 {
   // If command-line parameter is "install", install the service
   // or upgrade if already installed
   // If command line parameter is "forceinstall", install the service
   // even if it is older than what is already installed.
   // If command-line parameter is "upgrade", upgrade the service
@@ -47,34 +60,39 @@ wmain(int argc, WCHAR **argv)
     LOG(("Installing service"));
     SvcInstallAction action = InstallSvc;
     if (forceInstall) {
       action = ForceInstallSvc;
       LOG((" with force specified"));
     }
     LOG(("...\n"));
 
-    if (!SvcInstall(action)) {
+    bool ret = SvcInstall(action);
+    RunCommandsForEachInstallAndUpgrade();
+    if (!ret) {
       LOG(("Could not install service (%d)\n", GetLastError()));
       LogFinish();
       return 1;
     }
 
     LOG(("The service was installed successfully\n"));
     LogFinish();
     return 0;
   } 
 
   if (!lstrcmpi(argv[1], L"upgrade")) {
     WCHAR updatePath[MAX_PATH + 1];
     if (GetLogDirectoryPath(updatePath)) {
       LogInit(updatePath, L"maintenanceservice-install.log");
     }
     LOG(("Upgrading service if installed...\n"));
-    if (!SvcInstall(UpgradeSvc)) {
+
+    bool ret = SvcInstall(UpgradeSvc);
+    RunCommandsForEachInstallAndUpgrade();
+    if (!ret) {
       LOG(("Could not upgrade service (%d)\n", GetLastError()));
       LogFinish();
       return 1;
     }
 
     LOG(("The service was upgraded successfully\n"));
     LogFinish();
     return 0;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/prefetch.cpp
@@ -0,0 +1,255 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * 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 Maintenance service prefetch cleaning.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * 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 ***** */
+
+#include <shlwapi.h>
+#include "servicebase.h"
+#include "updatehelper.h"
+#include "nsWindowsHelpers.h"
+#define MAX_KEY_LENGTH 255
+
+/** 
+  * We found that prefetch actually causes large applications like Firefox
+  * to startup slower.  This will get rid of the Windows prefetch files for
+  * applications like firefox (FIREFOX-*.pf files) and instead replace them
+  * with 0 byte files which are read only.  Windows will not use prefetch
+  * if the associated prefetch file is a 0 byte read only file.
+  * Some of the problems with prefetch relating to Windows and Firefox:
+  * - Prefetch reads in a lot before we even run (fonts, plugins, etc...)
+  * - Prefetch happens before our code runs, so it delays UI showing.
+  * - Prefetch does not use windows readahead.
+  * Previous test data on an acer i7 laptop with a 5400rpm HD showed startup
+  * time difference was 1.6s (without prefetch) vs 2.6s+ with prefetch.
+  * The "Windows Internals" book mentions that prefetch can be disabled for 
+  * the whole system only, so there is no other application specific way to
+  * disable it.
+  *
+  * @param prefetchProcessName The name of the process who's prefetch files
+  *                            should be cleared.
+  * @return TRUE if no errors occurred during the clear operation.
+*/
+BOOL
+ClearPrefetch(LPCWSTR prefetchProcessName)
+{
+  LOG(("Clearing prefetch files...\n"));
+  size_t prefetchProcessNameLength = wcslen(prefetchProcessName);
+
+  // Make sure we were passed in a safe value for the prefetch process name
+  // because it will be appended to a filepath and deleted.
+  // We check for < 2 to avoid things like "." as the path 
+  // We check for a max path of MAX_PATH - 10 because we add \\ and '.EXE-*.pf'
+  if (wcsstr(prefetchProcessName, L"..") ||
+      wcsstr(prefetchProcessName, L"\\") ||
+      wcsstr(prefetchProcessName, L"*") ||
+      wcsstr(prefetchProcessName, L"/") ||
+      prefetchProcessNameLength < 2 ||
+      prefetchProcessNameLength >= (MAX_PATH - 10)) {
+    LOG(("Prefetch path to clear is not safe\n"));
+    return FALSE;
+  }
+
+  // Quick shortcut to make sure we don't try to clear multiple times for
+  // different FIREFOX installs.
+  static WCHAR lastPrefetchProcessName[MAX_PATH - 10] = { '\0' };
+  if (!wcscmp(lastPrefetchProcessName, prefetchProcessName)) {
+    LOG(("Already processed process name\n"));
+    return FALSE;
+  }
+  wcscpy(lastPrefetchProcessName, prefetchProcessName);
+
+  // Obtain the windows prefetch directory path.
+  WCHAR prefetchPath[MAX_PATH + 1];
+  if (!GetWindowsDirectoryW(prefetchPath,
+                            sizeof(prefetchPath) / 
+                            sizeof(prefetchPath[0]))) {
+    LOG(("Could not obtain windows directory\n"));
+    return FALSE;
+  }
+  if (!PathAppendSafe(prefetchPath, L"prefetch")) {
+    LOG(("Could not obtain prefetch directory\n"));
+    return FALSE;
+  }
+
+  size_t prefetchDirLen = wcslen(prefetchPath);
+  WCHAR prefetchSearchFile[MAX_PATH + 1];
+  // We know this is safe based on the check at the start of this function
+  wsprintf(prefetchSearchFile, L"\\%s.EXE-*.pf", prefetchProcessName);
+  // Append the search file to the full path
+  wcscpy(prefetchPath + prefetchDirLen, prefetchSearchFile);
+
+  // Find the first path matching and get a find handle for future calls.
+  WIN32_FIND_DATAW findFileData;
+  HANDLE findHandle = FindFirstFileW(prefetchPath, &findFileData);
+  if (INVALID_HANDLE_VALUE == findHandle) {
+    if (GetLastError() == ERROR_FILE_NOT_FOUND) {
+      LOG(("No files matching firefox.exe prefetch path.\n"));
+      return TRUE;
+    } else {
+      LOG(("Error finding firefox.exe prefetch files. (%d)\n",
+           GetLastError()));
+      return FALSE;
+    }
+  }
+  
+  BOOL deletedAllFFPrefetch = TRUE;
+  do {
+    // Reset back to the prefetch directory, we know from an above check that
+    // we aren't exceeding MAX_PATH + 1 characters with prefetchDirLen + 1.
+    // From above we know: prefetchPath[prefetchDirLen] == L'\\';
+    prefetchPath[prefetchDirLen + 1] = L'\0';
+
+    // Get the new file's prefetch path
+    LPWSTR filenameOffsetInBuffer = prefetchPath + prefetchDirLen + 1;
+    if (wcslen(findFileData.cFileName) + prefetchDirLen + 1 > MAX_PATH) {
+      LOG(("Error appending prefetch path %ls, path is too long. (%d)\n",
+           findFileData.cFileName, GetLastError()));
+      deletedAllFFPrefetch = FALSE;
+      continue;
+    }
+    if (!PathAppendSafe(filenameOffsetInBuffer, findFileData.cFileName)) {
+      LOG(("Error appending prefetch path %ls. (%d)\n", findFileData.cFileName,
+           GetLastError()));
+      deletedAllFFPrefetch = FALSE;
+      continue;
+    }
+
+    // Delete the prefetch file and replace it with a blank read only file
+    HANDLE prefetchFile =
+      CreateFile(prefetchPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+    if (INVALID_HANDLE_VALUE == prefetchFile) {
+      LOG(("Error replacing prefetch path %ls. (%d)\n", findFileData.cFileName, 
+           GetLastError()));
+      deletedAllFFPrefetch = FALSE;
+      continue;
+    }
+
+    CloseHandle(prefetchFile);
+
+    DWORD attributes = GetFileAttributes(prefetchPath);
+    if (INVALID_FILE_ATTRIBUTES == attributes) {
+      LOG(("Could not get/set attributes on prefetch file: %ls. (%d)\n", 
+           findFileData.cFileName, GetLastError()));
+      continue;
+    }
+
+    if (!SetFileAttributes(prefetchPath, 
+                          attributes | FILE_ATTRIBUTE_READONLY)) {
+      LOG(("Could not set read only on prefetch file: %ls. (%d)\n", 
+           findFileData.cFileName, GetLastError()));
+      continue;
+    } 
+
+    LOG(("Prefetch file cleared and set to read-only successfully: %ls\n", 
+         prefetchPath));
+  } while (FindNextFileW(findHandle, &findFileData));
+  LOG(("Done searching prefetch paths. (%d)\n", GetLastError()));
+
+  // Cleanup after ourselves.
+  FindClose(findHandle);
+  return deletedAllFFPrefetch;
+}
+
+/**
+ * Clears all prefetch files specified in the installation dirctories
+ * @return FALSE if there was an error clearing the known prefetch directories
+*/
+BOOL
+ClearKnownPrefetch()
+{
+  // The service always uses the 64-bit registry key
+  HKEY baseKeyRaw;
+  LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                               BASE_SERVICE_REG_KEY, 0,
+                               KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
+  if (retCode != ERROR_SUCCESS) {
+    LOG(("Could not open maintenance service base key. (%d)\n", retCode));
+    return FALSE;
+  }
+  nsAutoRegKey baseKey(baseKeyRaw);
+
+  // Get the number of subkeys.
+  DWORD subkeyCount = 0;
+  retCode = RegQueryInfoKeyW(baseKey, NULL, NULL, NULL, &subkeyCount, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL);
+  if (retCode != ERROR_SUCCESS) {
+    LOG(("Could not query info key: %d\n", retCode));
+    return FALSE;
+  }
+
+  // Enumerate the subkeys, each subkey represents an install
+  for (DWORD i = 0; i < subkeyCount; i++) { 
+    WCHAR subkeyBuffer[MAX_KEY_LENGTH];
+    DWORD subkeyBufferCount = MAX_KEY_LENGTH;  
+    retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer, 
+                            &subkeyBufferCount, NULL, 
+                            NULL, NULL, NULL); 
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not enum installations: %d\n", retCode));
+      return FALSE;
+    }
+
+    // Open the subkey
+    HKEY subKeyRaw;
+    retCode = RegOpenKeyExW(baseKey, 
+                            subkeyBuffer, 
+                            0, 
+                            KEY_READ | KEY_WOW64_64KEY, 
+                            &subKeyRaw);
+    nsAutoRegKey subKey(subKeyRaw);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not open subkey: %d\n", retCode));
+      continue; // Try the next subkey
+    }
+
+    const int MAX_CHAR_COUNT = 256;
+    DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
+    WCHAR prefetchProcessName[MAX_CHAR_COUNT] = { L'\0' };
+
+    // Get the prefetch process name from the registry
+    retCode = RegQueryValueExW(subKey, L"prefetchProcessName", 0, NULL, 
+                               (LPBYTE)prefetchProcessName, &valueBufSize);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not obtain process name from registry: %d\n", retCode));
+      continue; // Try the next subkey
+    }
+
+    // The value for prefetch process name comes from HKLM so is trusted.
+    // We will do some sanity checks on it though inside ClearPrefetch.
+    ClearPrefetch(prefetchProcessName);
+  }
+
+  return TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/prefetch.h
@@ -0,0 +1,38 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * 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 Maintenance service prefetch cleaning.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * 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 ***** */
+
+BOOL ClearKnownPrefetch();
--- a/toolkit/components/maintenanceservice/registrycertificates.cpp
+++ b/toolkit/components/maintenanceservice/registrycertificates.cpp
@@ -5,16 +5,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <windows.h>
 
 #include "registrycertificates.h"
 #include "pathhash.h"
 #include "nsWindowsHelpers.h"
 #include "servicebase.h"
+#include "updatehelper.h"
 #define MAX_KEY_LENGTH 255
 
 /**
  * Verifies if the file path matches any certificate stored in the registry.
  *
  * @param  filePath The file path of the application to check if allowed.
  * @return TRUE if the binary matches any of the allowed certificates.
  */
--- a/toolkit/components/maintenanceservice/workmonitor.cpp
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -19,16 +19,17 @@
 
 #include "workmonitor.h"
 #include "serviceinstall.h"
 #include "servicebase.h"
 #include "registrycertificates.h"
 #include "uachelper.h"
 #include "updatehelper.h"
 #include "errors.h"
+#include "prefetch.h"
 
 // Wait 15 minutes for an update operation to run at most.
 // Updates usually take less than a minute so this seems like a 
 // significantly large and safe amount of time to wait.
 static const int TIME_TO_WAIT_ON_UPDATER = 15 * 60 * 1000;
 PRUnichar* MakeCommandLine(int argc, PRUnichar **argv);
 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
 BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,  LPCWSTR siblingFilePath, 
@@ -477,16 +478,18 @@ ExecuteServiceCommand(int argc, LPWSTR *
 
   BOOL result = FALSE;
   if (!lstrcmpi(argv[2], L"software-update")) {
     result = ProcessSoftwareUpdateCommand(argc - 3, argv + 3);
     // We might not reach here if the service install succeeded
     // because the service self updates itself and the service
     // installer will stop the service.
     LOG(("Service command %ls complete.\n", argv[2]));
+  } else if (!lstrcmpi(argv[2], L"clear-prefetch")) {
+    result = ClearKnownPrefetch();
   } else {
     LOG(("Service command not recognized: %ls.\n", argv[2]));
     // result is already set to FALSE
   }
 
   LOG(("service command %ls complete with result: %ls.\n", 
        argv[1], (result ? L"Success" : L"Failure")));
   return TRUE;
--- a/toolkit/mozapps/update/common/Makefile.in
+++ b/toolkit/mozapps/update/common/Makefile.in
@@ -29,15 +29,16 @@ EXPORTS = updatelogging.h \
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 CPPSRCS += updatehelper.cpp \
   uachelper.cpp \
   pathhash.cpp \
   $(NULL)
 
 EXPORTS = updatehelper.h \
+  updatehelper.cpp \
   uachelper.h \
   pathhash.h \
   $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
--- a/toolkit/mozapps/update/common/pathhash.h
+++ b/toolkit/mozapps/update/common/pathhash.h
@@ -11,18 +11,9 @@
  * @param  filePath     The input file path to get a registry path from
  * @param  registryPath A buffer to write the registry path to, must 
  *                      be of size in WCHARs MAX_PATH + 1
  * @return TRUE if successful
 */
 BOOL CalculateRegistryPathFromFilePath(const LPCWSTR filePath, 
                                        LPWSTR registryPath);
 
-// The test only fallback key, as its name implies, is only present on machines
-// that will use automated tests.  Since automated tests always run from a 
-// different directory for each test, the presence of this key bypasses the
-// "This is a valid installation directory" check.  This key also stores
-// the allowed name and issuer for cert checks so that the cert check
-// code can still be run unchanged.
-#define TEST_ONLY_FALLBACK_KEY_PATH \
-  L"SOFTWARE\\Mozilla\\MaintenanceService\\3932ecacee736d366d6436db0f55bce4"
-
 #endif
--- a/toolkit/mozapps/update/common/updatehelper.cpp
+++ b/toolkit/mozapps/update/common/updatehelper.cpp
@@ -1,22 +1,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <windows.h>
+
+// Needed for CreateToolhelp32Snapshot
+#include <tlhelp32.h>
+#ifndef ONLY_SERVICE_LAUNCHING
+
 #include <stdio.h>
 #include "shlobj.h"
 #include "updatehelper.h"
 #include "pathhash.h"
 
 // Needed for PathAppendW
 #include <shlwapi.h>
-// Needed for CreateToolhelp32Snapshot
-#include <tlhelp32.h>
 #pragma comment(lib, "shlwapi.lib") 
 
 WCHAR* MakeCommandLine(int argc, WCHAR **argv);
 BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
 
 /**
  * Obtains the path of a file in the same directory as the specified file.
  *
@@ -231,16 +234,18 @@ StartServiceUpdate(int argc, LPWSTR *arg
                                                 NULL, argv[2], &si, &pi);
   if (svcUpdateProcessStarted) {
     CloseHandle(pi.hProcess);
     CloseHandle(pi.hThread);
   }
   return svcUpdateProcessStarted;
 }
 
+#endif 
+
 /**
  * Executes a maintenance service command
  * 
  * @param  argc    The total number of arguments in argv
  * @param  argv    An array of null terminated strings to pass to the service, 
  * @return ERROR_SUCCESS if the service command was started.
  *         Less than 16000, a windows system error code from StartServiceW
  *         More than 20000, 20000 + the last state of the service constant if
@@ -289,16 +294,18 @@ StartServiceCommand(int argc, LPCWSTR* a
     Sleep(100);
     currentWaitMS += 100;
   }
   CloseServiceHandle(service);
   CloseServiceHandle(serviceManager);
   return lastError;
 }
 
+#ifndef ONLY_SERVICE_LAUNCHING
+
 /**
  * Launch a service initiated action for a software update with the 
  * specified arguments.
  *
  * @param  exePath The path of the executable to run
  * @param  argc    The total number of arguments in argv
  * @param  argv    An array of null terminated strings to pass to the exePath, 
  *                 argv[0] must be the path to the updater.exe
@@ -398,16 +405,18 @@ WriteStatusFailure(LPCWSTR updateDirPath
   DWORD toWrite = strlen(failure);
   DWORD wrote;
   BOOL ok = WriteFile(statusFile, failure, 
                       toWrite, &wrote, NULL); 
   CloseHandle(statusFile);
   return ok && wrote == toWrite;
 }
 
+#endif
+
 /**
  * Waits for a service to enter a stopped state.
  * This function does not stop the service, it just blocks until the service
  * is stopped.
  *
  * @param  serviceName     The service to wait for.
  * @param  maxWaitSeconds  The maximum number of seconds to wait
  * @return state of the service after a timeout or when stopped.
@@ -530,16 +539,18 @@ WaitForServiceStop(LPCWSTR serviceName, 
   }
 
   lastServiceState = ssp.dwCurrentState;
   CloseServiceHandle(service);
   CloseServiceHandle(serviceManager);
   return lastServiceState;
 }
 
+#ifndef ONLY_SERVICE_LAUNCHING
+
 /**
  * Determines if there is at least one process running for the specified
  * application. A match will be found across any session for any user.
  *
  * @param process The process to check for existance
  * @return ERROR_NOT_FOUND if the process was not found
  *         ERROR_SUCCESS if the process was found and there were no errors
  *         Other Win32 system error code for other errors
@@ -615,16 +626,18 @@ DoesFallbackKeyExist()
                     &testOnlyFallbackKey) != ERROR_SUCCESS) {
     return FALSE;
   }
 
   RegCloseKey(testOnlyFallbackKey);
   return TRUE;
 }
 
+#endif
+
 /**
  * Determines if the file system for the specified file handle is local
  * @param file path to check the filesystem type for, must be at most MAX_PATH
  * @param isLocal out parameter which will hold TRUE if the drive is local
  * @return TRUE if the call succeeded
 */
 BOOL
 IsLocalFile(LPCWSTR file, BOOL &isLocal)
--- a/toolkit/mozapps/update/common/updatehelper.h
+++ b/toolkit/mozapps/update/common/updatehelper.h
@@ -10,10 +10,23 @@ BOOL StartServiceUpdate(int argc, LPWSTR
 BOOL GetUpdateDirectoryPath(LPWSTR path);
 DWORD LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR *argv);
 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
 BOOL WriteStatusPending(LPCWSTR updateDirPath);
 DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
 DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
 BOOL DoesFallbackKeyExist();
 BOOL IsLocalFile(LPCWSTR file, BOOL &isLocal);
+DWORD StartServiceCommand(int argc, LPCWSTR* argv);
 
 #define SVC_NAME L"MozillaMaintenance"
+
+#define BASE_SERVICE_REG_KEY \
+  L"SOFTWARE\\Mozilla\\MaintenanceService"
+
+// The test only fallback key, as its name implies, is only present on machines
+// that will use automated tests.  Since automated tests always run from a 
+// different directory for each test, the presence of this key bypasses the
+// "This is a valid installation directory" check.  This key also stores
+// the allowed name and issuer for cert checks so that the cert check
+// code can still be run unchanged.
+#define TEST_ONLY_FALLBACK_KEY_PATH \
+  BASE_SERVICE_REG_KEY L"\\3932ecacee736d366d6436db0f55bce4"