Bug 603903 - Part 1: add RegisterApplicationRestart draft
authorAdam Gashlin <agashlin@mozilla.com>
Tue, 15 May 2018 15:37:45 -0700
changeset 795927 106ae0e237b290fda09c0c752a7dfa2ac718ed16
parent 795657 3c9d69736f4a421218e5eb01b6571d535d38318a
child 795928 26ef37bebe39c42e34ff1346ebb8d00936029f0b
push id110121
push userbmo:agashlin@mozilla.com
push dateWed, 16 May 2018 19:33:58 +0000
bugs603903
milestone62.0a1
Bug 603903 - Part 1: add RegisterApplicationRestart MozReview-Commit-ID: Eb389ih7CIn
browser/app/profile/firefox.js
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsAppRunner.h
toolkit/xre/nsWindowsRestart.cpp
xpcom/system/nsIXULRuntime.idl
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1280,16 +1280,26 @@ pref("browser.library.activity-stream.en
 // Enable the DOM fullscreen API.
 pref("full-screen-api.enabled", true);
 
 // Startup Crash Tracking
 // number of startup crashes that can occur before starting into safe mode automatically
 // (this pref has no effect if more than 6 hours have passed since the last crash)
 pref("toolkit.startup.max_resumed_crashes", 3);
 
+// Whether to use RegisterApplicationRestart to restart the browser and resume
+// the session on next Windows startup
+#if defined(XP_WIN)
+#if defined(NIGHTLY_BUILD)
+pref("toolkit.winRegisterApplicationRestart", true);
+#else
+pref("toolkit.winRegisterApplicationRestart", false);
+#endif
+#endif
+
 // Whether we use pdfium to view content with the pdf mime type.
 // Note: if the pref is set to false while Firefox is open, it won't
 // take effect until there are no open pdfium tabs.
 pref("pdfium.enabled", false);
 
 // Completely disable pdf.js as an option to preview pdfs within firefox.
 // Note: if this is not disabled it does not necessarily mean pdf.js is the pdf
 // handler just that it is an option.
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -253,16 +253,19 @@ char **gArgv;
 static const char gToolkitVersion[] = NS_STRINGIFY(GRE_MILESTONE);
 static const char gToolkitBuildID[] = NS_STRINGIFY(MOZ_BUILDID);
 
 static nsIProfileLock* gProfileLock;
 
 int    gRestartArgc;
 char **gRestartArgv;
 
+// If gRestartedByOS is set, we were automatically restarted by the OS.
+bool gRestartedByOS = false;
+
 bool gIsGtest = false;
 
 nsString gAbsoluteArgv0Path;
 
 #if defined(MOZ_WIDGET_GTK)
 #include <glib.h>
 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
 #define CLEANUP_MEMORY 1
@@ -1024,16 +1027,23 @@ nsXULAppInfo::GetWindowsDLLBlocklistStat
 #if defined(HAS_DLL_BLOCKLIST)
   *aResult = DllBlocklist_CheckStatus();
 #else
   *aResult = false;
 #endif
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXULAppInfo::GetRestartedByOS(bool* aResult)
+{
+  *aResult = gRestartedByOS;
+  return NS_OK;
+}
+
 #ifdef XP_WIN
 // Matches the enum in WinNT.h for the Vista SDK but renamed so that we can
 // safely build with the Vista SDK and without it.
 typedef enum
 {
   VistaTokenElevationTypeDefault = 1,
   VistaTokenElevationTypeFull,
   VistaTokenElevationTypeLimited
@@ -1748,17 +1758,54 @@ XRE_GetBinaryPath(nsIFile* *aResult)
   return mozilla::BinaryPath::GetFile(aResult);
 }
 
 #ifdef XP_WIN
 #include "nsWindowsRestart.cpp"
 #include <shellapi.h>
 
 typedef BOOL (WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags);
-#endif
+
+static void
+RegisterApplicationRestartChanged(const char* aPref, void* aData) {
+  DWORD cchCmdLine = 0;
+  HRESULT rc =
+    ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr, &cchCmdLine, nullptr);
+  bool wasRegistered = false;
+  if (rc == S_OK) {
+    wasRegistered = true;
+  }
+
+  if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART, false) && !wasRegistered) {
+    // Make the command line to use when restarting.
+    // Excludes argv[0] because RegisterApplicationRestart adds the
+    // executable name, replace that temporarily with -os-restarted
+    char* exeName = gRestartArgv[0];
+    gRestartArgv[0] = "-os-restarted";
+    wchar_t** restartArgvConverted =
+      AllocConvertUTF8toUTF16Strings(gRestartArgc, gRestartArgv);
+    gRestartArgv[0] = exeName;
+
+    mozilla::UniquePtr<wchar_t[]> restartCommandLine;
+    if (restartArgvConverted) {
+      restartCommandLine = mozilla::MakeCommandLine(gRestartArgc, restartArgvConverted);
+      FreeAllocStrings(gRestartArgc, restartArgvConverted);
+    }
+
+    if (restartCommandLine) {
+      // Flags RESTART_NO_PATCH and RESTART_NO_REBOOT are not set, so we
+      // should be restarted if terminated by an update or restart.
+      ::RegisterApplicationRestart(restartCommandLine.get(), RESTART_NO_CRASH |
+                                                             RESTART_NO_HANG);
+    }
+  } else if (wasRegistered) {
+    ::UnregisterApplicationRestart();
+  }
+}
+#endif // XP_WIN
 
 // If aBlankCommandLine is true, then the application will be launched with a
 // blank command line instead of being launched with the same command line that
 // it was initially started with.
 static nsresult LaunchChild(nsINativeAppSupport* aNative,
                             bool aBlankCommandLine = false)
 {
   aNative->Quit(); // release DDE mutex, if we're holding it
@@ -3433,16 +3480,22 @@ XREMain::XRE_mainInit(bool* aExitFlag)
     ProcessSerialNumber psn;
     if (::GetCurrentProcess(&psn) == noErr)
       ::SetFrontProcess(&psn);
   }
 #endif
 
   SaveToEnv("MOZ_LAUNCHED_CHILD=");
 
+  // On Windows, the -os-restarted command line switch lets us know when we are
+  // restarted via RegisterApplicationRestart. May be used for other OSes later.
+  if (CheckArg("os-restarted", nullptr, CheckArgFlag::RemoveArg) == ARG_FOUND) {
+    gRestartedByOS = true;
+  }
+
   gRestartArgc = gArgc;
   gRestartArgv = (char**) malloc(sizeof(char*) * (gArgc + 1 + (override ? 2 : 0)));
   if (!gRestartArgv) {
     return 1;
   }
 
   int i;
   for (i = 0; i < gArgc; ++i) {
@@ -4559,16 +4612,21 @@ XREMain::XRE_mainRun()
   SaveToEnv("NO_EM_RESTART=");
   SaveToEnv("XUL_APP_FILE=");
   SaveToEnv("XRE_BINARY_PATH=");
 
   if (!mShuttingDown) {
     rv = appStartup->CreateHiddenWindow();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
+#ifdef XP_WIN
+    Preferences::RegisterCallbackAndCall(RegisterApplicationRestartChanged,
+                                         PREF_WIN_REGISTER_APPLICATION_RESTART);
+#endif
+
 #if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK)
     nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit();
     if (toolkit && !mDesktopStartupID.IsEmpty()) {
       toolkit->SetDesktopStartupID(mDesktopStartupID);
     }
     // Clear the environment variable so it won't be inherited by
     // child processes and confuse things.
     g_unsetenv ("DESKTOP_STARTUP_ID");
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -100,16 +100,18 @@ MozExpectedExit();
 #ifdef XP_WIN
 void
 UseParentConsole();
 
 BOOL
 WinLaunchChild(const wchar_t *exePath, int argc,
                char **argv, HANDLE userToken = nullptr,
                HANDLE *hProcess = nullptr);
+
+#define PREF_WIN_REGISTER_APPLICATION_RESTART "toolkit.winRegisterApplicationRestart"
 #endif
 
 #define NS_NATIVEAPPSUPPORT_CONTRACTID "@mozilla.org/toolkit/native-app-support;1"
 
 namespace mozilla {
 namespace startup {
 Result<nsCOMPtr<nsIFile>, nsresult> GetIncompleteStartupFile(nsIFile* aProfLD);
 
--- a/toolkit/xre/nsWindowsRestart.cpp
+++ b/toolkit/xre/nsWindowsRestart.cpp
@@ -45,16 +45,32 @@ FreeAllocStrings(int argc, wchar_t **arg
   while (argc) {
     --argc;
     delete [] argv[argc];
   }
 
   delete [] argv;
 }
 
+static wchar_t**
+AllocConvertUTF8toUTF16Strings(int argc, char **argv)
+{
+  wchar_t **argvConverted = new wchar_t*[argc];
+  if (!argvConverted)
+    return nullptr;
+
+  for (int i = 0; i < argc; ++i) {
+    argvConverted[i] = reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i]));
+    if (!argvConverted[i]) {
+      FreeAllocStrings(i, argvConverted);
+      return nullptr;
+    }
+  }
+  return argvConverted;
+}
 
 
 /**
  * Launch a child process with the specified arguments.
  * @note argv[0] is ignored
  * @note The form of this function that takes char **argv expects UTF-8
  */
 
@@ -65,28 +81,20 @@ WinLaunchChild(const wchar_t *exePath,
                HANDLE *hProcess = nullptr);
 
 BOOL
 WinLaunchChild(const wchar_t *exePath,
                int argc, char **argv,
                HANDLE userToken,
                HANDLE *hProcess)
 {
-  wchar_t** argvConverted = new wchar_t*[argc];
+  wchar_t **argvConverted = AllocConvertUTF8toUTF16Strings(argc, argv);
   if (!argvConverted)
     return FALSE;
 
-  for (int i = 0; i < argc; ++i) {
-      argvConverted[i] = reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i]));
-    if (!argvConverted[i]) {
-      FreeAllocStrings(i, argvConverted);
-      return FALSE;
-    }
-  }
-
   BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess);
   FreeAllocStrings(argc, argvConverted);
   return ok;
 }
 
 BOOL
 WinLaunchChild(const wchar_t *exePath,
                int argc,
--- a/xpcom/system/nsIXULRuntime.idl
+++ b/xpcom/system/nsIXULRuntime.idl
@@ -189,9 +189,15 @@ interface nsIXULRuntime : nsISupports
    */
   readonly attribute AUTF8String distributionID;
 
   /**
    * True if Windows DLL blocklist initialized correctly. This is
    * primarily for automated testing purposes.
    */
   readonly attribute boolean windowsDLLBlocklistStatus;
+
+  /**
+   * True if this application was started by the OS as part of an automatic
+   * restart mechanism (such as RegisterApplicationRestart on Windows).
+   */
+  readonly attribute boolean restartedByOS;
 };