Merge m-c to inbound
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 27 May 2012 23:32:46 -0400
changeset 99161 e2cd95fa1ce49bd5e04522d5eac23f4cd2d8601f
parent 99160 36f327aaffa5d7c0ed4bf1c84b488a18b6332754 (current diff)
parent 99125 93018b4bf4da0594bf368748b21ce1ea1446f4df (diff)
child 99162 34af06cc57f6cd99f59aec2e773a7afb40ec1b5a
push idunknown
push userunknown
push dateunknown
milestone15.0a1
Merge m-c to inbound
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -152,16 +152,48 @@ static int do_main(int argc, char* argv[
     int result = XRE_main(argc, argv, appData, 0);
     XRE_FreeAppData(appData);
     return result;
   }
 
   return XRE_main(argc, argv, &sAppData, 0);
 }
 
+#ifdef XP_WIN
+/**
+ * Determines if the registry is disabled via the service or not.
+ * 
+ * @return true if prefetch is disabled
+ *         false if prefetch is not disabled or an error occurred.
+*/
+bool IsPrefetchDisabledViaService()
+{
+  // We don't need to return false when we don't have MOZ_MAINTENANCE_SERVICE
+  // defined.  The reason is because another product installed that has it
+  // defined may have cleared our prefetch for us.  There is no known way
+  // to figure out which prefetch files are associated with which apps
+  // because of the prefetch hash.  So we disable all of them that start
+  // with FIREFOX.
+  HKEY baseKey;
+  LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
+                               L"SOFTWARE\\Mozilla\\MaintenanceService", 0,
+                               KEY_READ | KEY_WOW64_64KEY, &baseKey);
+  if (retCode != ERROR_SUCCESS) {
+    return false;
+  }
+  DWORD disabledValue = 0;
+  DWORD disabledValueSize = sizeof(DWORD);
+  RegQueryValueExW(baseKey, L"FFPrefetchDisabled", 0, NULL,
+                   reinterpret_cast<LPBYTE>(&disabledValue),
+                   &disabledValueSize);
+  RegCloseKey(baseKey);
+  return disabledValue == 1;
+}
+#endif
+
 int main(int argc, char* argv[])
 {
   char exePath[MAXPATHLEN];
 
 #ifdef XP_MACOSX
   TriggerQuirks();
 #endif
 
@@ -177,30 +209,45 @@ int main(int argc, char* argv[])
 
   strcpy(++lastSlash, XPCOM_DLL);
 
   int gotCounters;
 #if defined(XP_UNIX)
   struct rusage initialRUsage;
   gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
 #elif defined(XP_WIN)
+  // Don't change the order of these enumeration constants, the order matters
+  // for reporting telemetry data.  If new values are added adjust the
+  // STARTUP_USING_PRELOAD histogram.
+  enum PreloadReason { PRELOAD_NONE, PRELOAD_SERVICE, PRELOAD_IOCOUNT };
+  PreloadReason preloadReason = PRELOAD_NONE;
+
   // GetProcessIoCounters().ReadOperationCount seems to have little to
   // do with actual read operations. It reports 0 or 1 at this stage
   // in the program. Luckily 1 coincides with when prefetch is
   // enabled. If Windows prefetch didn't happen we can do our own
   // faster dll preloading.
+  // The MozillaMaintenance service issues a command to disable the
+  // prefetch by replacing all found .pf files with 0 byte read only
+  // files.
   IO_COUNTERS ioCounters;
   gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
-  if (gotCounters && !ioCounters.ReadOperationCount)
+
+  if (IsPrefetchDisabledViaService()) {
+    preloadReason = PRELOAD_SERVICE;
+  } else if ((gotCounters && !ioCounters.ReadOperationCount)) {
+    preloadReason = PRELOAD_IOCOUNT;
+  }
+
+  if (preloadReason != PRELOAD_NONE)
 #endif
   {
       XPCOMGlueEnablePreload();
   }
 
-
   rv = XPCOMGlueStartup(exePath);
   if (NS_FAILED(rv)) {
     Output("Couldn't load XPCOM.\n");
     return 255;
   }
   // Reset exePath so that it is the directory name and not the xpcom dll name
   *lastSlash = 0;
 
@@ -209,16 +256,21 @@ int main(int argc, char* argv[])
     Output("Couldn't load XRE functions.\n");
     return 255;
   }
 
 #ifdef XRE_HAS_DLL_BLOCKLIST
   XRE_SetupDllBlocklist();
 #endif
 
+#if defined(XP_WIN)
+  XRE_TelemetryAccumulate(mozilla::Telemetry::STARTUP_USING_PRELOAD,
+                          preloadReason);
+#endif
+
   if (gotCounters) {
 #if defined(XP_WIN)
     XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS,
                             int(ioCounters.ReadOperationCount));
     XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER,
                             int(ioCounters.ReadTransferCount / 1024));
     IO_COUNTERS newIoCounters;
     if (GetProcessIoCounters(GetCurrentProcess(), &newIoCounters)) {
--- 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
+// 3 minutes.  After this is done once we set a pref so this will never happen
+// again except in updater code.
+#define CLEAR_PREFETCH_TIMEOUT_MS 180000
+
 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
+RunCommandsForEachUpgrade()
+{
+  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,38 @@ 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);
+    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);
+    RunCommandsForEachUpgrade();
+    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,313 @@
+/* ***** 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
+
+/**
+ * Writes a registry DWORD with a value of 1 at BASE_SERVICE_REG_KEY
+ * if the prefetch was cleared successfully.
+ *
+ * @return TRUE if successful.
+*/
+BOOL
+WritePrefetchClearedReg()
+{
+  HKEY baseKeyRaw;
+  LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
+                               BASE_SERVICE_REG_KEY, 0,
+                               KEY_WRITE | KEY_WOW64_64KEY, &baseKeyRaw);
+  if (retCode != ERROR_SUCCESS) {
+    LOG(("Could not open key for prefetch. (%d)\n", retCode));
+    return FALSE;
+  }
+  nsAutoRegKey baseKey(baseKeyRaw);
+  DWORD disabledValue = 1;
+  if (RegSetValueExW(baseKey, L"FFPrefetchDisabled", 0, REG_DWORD,
+                     reinterpret_cast<LPBYTE>(&disabledValue),
+                     sizeof(disabledValue)) != ERROR_SUCCESS) {
+    LOG(("Could not write prefetch cleared value to registry. (%d)\n",
+         GetLastError()));
+    return FALSE;
+  }
+  return TRUE;
+}
+
+static BOOL
+PrefetchFileAlreadyCleared(LPCWSTR prefetchPath)
+{
+  DWORD attributes = GetFileAttributes(prefetchPath);
+  BOOL alreadyCleared = attributes != INVALID_FILE_ATTRIBUTES &&
+                        attributes & FILE_ATTRIBUTE_READONLY;
+
+  nsAutoHandle prefetchFile(CreateFile(prefetchPath, GENERIC_READ, 0, NULL,
+                                       OPEN_EXISTING, 0, NULL));
+  LARGE_INTEGER fileSize = { 0, 0 };
+  alreadyCleared &= prefetchFile != INVALID_HANDLE_VALUE &&
+                    GetFileSizeEx(prefetchFile, &fileSize) &&
+                    fileSize.QuadPart == 0;
+  return alreadyCleared;
+}
+
+/** 
+  * 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;
+  size_t deletedCount = 0;
+  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;
+    }
+
+    if (PrefetchFileAlreadyCleared(prefetchPath)) {
+      ++deletedCount;
+      LOG(("Prefetch file already cleared: %ls\n", prefetchPath));
+      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;
+    } 
+
+    ++deletedCount;
+    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);
+
+  if (deletedAllFFPrefetch && deletedCount > 0) {
+    WritePrefetchClearedReg();
+  }
+
+  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/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -80,16 +80,17 @@ HISTOGRAM(LOW_MEMORY_EVENTS_PHYSICAL, 1,
 HISTOGRAM(LOW_MEMORY_EVENTS_COMMIT_SPACE, 1, 1024, 21, EXPONENTIAL, "Number of low-commit-space events fired since last ping")
 #endif
 
 #if defined(XP_WIN)
 HISTOGRAM(EARLY_GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount before glue startup")
 HISTOGRAM(EARLY_GLUESTARTUP_READ_TRANSFER, 1, 50 * 1024, 12, EXPONENTIAL, "ProcessIoCounters.ReadTransferCount before glue startup (KB)")
 HISTOGRAM(GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount after glue startup")
 HISTOGRAM(GLUESTARTUP_READ_TRANSFER, 1, 50 * 1024, 12, EXPONENTIAL, "ProcessIoCounters.ReadTransferCount after glue startup (KB)")
+HISTOGRAM(STARTUP_USING_PRELOAD, 1, 2, 3, LINEAR, "Preload heuristic. 0: none, 1: preload from service, 2: preload from ioCounters.ReadOperationCount")
 #elif defined(XP_UNIX)
 HISTOGRAM(EARLY_GLUESTARTUP_HARD_FAULTS, 1, 100, 12, LINEAR, "Hard faults count before glue startup")
 HISTOGRAM(GLUESTARTUP_HARD_FAULTS, 1, 500, 12, EXPONENTIAL, "Hard faults count after glue startup")
 HISTOGRAM(PAGE_FAULTS_HARD, 8, 64 * 1024, 13, EXPONENTIAL, "Hard page faults (since last telemetry ping)")
 #endif
 HISTOGRAM(FONTLIST_INITOTHERFAMILYNAMES, 1, 30000, 50, EXPONENTIAL, "Time(ms) spent on reading other family names from all fonts")
 HISTOGRAM(FONTLIST_INITFACENAMELISTS, 1, 30000, 50, EXPONENTIAL, "Time(ms) spent on reading family names from all fonts")
 #if defined(XP_WIN)
--- 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"