Bug 603903 - use RegisterApplicationRestart, fix pre-login issues draft
authorAdam Gashlin <agashlin@mozilla.com>
Mon, 16 Apr 2018 17:40:47 -0700
changeset 783325 c31446801811ee29612070d4e116bb4706cfafd1
parent 783258 0ceabd10aac2272e83850e278c7876f32dbae42e
push id106671
push userbmo:agashlin@mozilla.com
push dateTue, 17 Apr 2018 00:41:07 +0000
bugs603903
milestone61.0a1
Bug 603903 - use RegisterApplicationRestart, fix pre-login issues MozReview-Commit-ID: JmGaGS5bcFt
browser/components/nsBrowserGlue.js
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsAppRunner.h
toolkit/xre/nsWindowsRestart.cpp
widget/PuppetWidget.cpp
widget/ScreenManager.cpp
widget/nsIScreenManager.idl
widget/windows/nsWindow.cpp
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2987,16 +2987,21 @@ var DefaultBrowserCheck = {
       let iconPixels = win.devicePixelRatio > 1 ? "32" : "16";
       let iconURL = "chrome://branding/content/icon" + iconPixels + ".png";
       const priority = notificationBox.PRIORITY_WARNING_HIGH;
       let callback = this._onNotificationEvent.bind(this);
       this._notification = notificationBox.appendNotification(promptMessage, "default-browser",
                                                               iconURL, priority, buttons,
                                                               callback);
     } else {
+      let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
+      if (screenManager.numScreens == 0) {
+        return;
+      }
+
       // Modal prompt
       let promptTitle = shellBundle.getString("setDefaultBrowserTitle");
       let promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
                                                          [brandShortName]);
       let askLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
                                                     [brandShortName]);
 
       let ps = Services.prompt;
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1879,17 +1879,49 @@ 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) {
+  if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART, true)) {
+    // Make the command line to use when restarting
+    wchar_t* restartCommandLine = nullptr;
+    if (gRestartArgc > 1) {
+      // excludes argv[0] because RegisterApplicationRestart adds the
+      // executable name
+      wchar_t** restartArgvConverted =
+        AllocConvertUTF8toUTF16Strings(gRestartArgc - 1, gRestartArgv + 1);
+
+      if (restartArgvConverted) {
+        restartCommandLine = MakeCommandLine(gRestartArgc - 1, restartArgvConverted);
+        FreeAllocStrings(gRestartArgc - 1, restartArgvConverted);
+      }
+    } else {
+      restartCommandLine = (wchar_t*) malloc(sizeof(wchar_t));
+      restartCommandLine[0] = L'\0';
+    }
+
+    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, RESTART_NO_CRASH |
+                                                       RESTART_NO_HANG);
+      free(restartCommandLine);
+    }
+  } else {
+    ::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
@@ -4730,16 +4762,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_WIN32
+    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
@@ -105,16 +105,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
@@ -108,18 +108,16 @@ static wchar_t* ArgToString(wchar_t *d, 
   }
 
   return d;
 }
 
 /**
  * Creates a command line from a list of arguments. The returned
  * string is allocated with "malloc" and should be "free"d.
- *
- * argv is UTF8
  */
 wchar_t*
 MakeCommandLine(int argc, wchar_t **argv)
 {
   int i;
   int len = 0;
 
   // The + 1 of the last argument handles the allocation for null termination
@@ -173,16 +171,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
  */
 
@@ -193,28 +207,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/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -1370,16 +1370,23 @@ PuppetScreenManager::ScreenForRect(int32
                                    int32_t inTop,
                                    int32_t inWidth,
                                    int32_t inHeight,
                                    nsIScreen** outScreen)
 {
   return GetPrimaryScreen(outScreen);
 }
 
+NS_IMETHODIMP
+PuppetScreenManager::GetNumScreens(size_t* aNumScreens)
+{
+  *aNumScreens = 1;
+  return NS_OK;
+}
+
 nsIWidgetListener*
 PuppetWidget::GetCurrentWidgetListener()
 {
   if (!mPreviouslyAttachedWidgetListener ||
       !mAttachedWidgetListener) {
     return mAttachedWidgetListener;
   }
 
--- a/widget/ScreenManager.cpp
+++ b/widget/ScreenManager.cpp
@@ -226,10 +226,20 @@ ScreenManager::GetPrimaryScreen(nsIScree
     return NS_OK;
   }
 
   RefPtr<Screen> ret = mScreenList[0];
   ret.forget(aPrimaryScreen);
   return NS_OK;
 }
 
+// The number of screens available.
+//
+
+NS_IMETHODIMP
+ScreenManager::GetNumScreens(size_t* aNumScreens)
+{
+  *aNumScreens = mScreenList.Length();
+  return NS_OK;
+}
+
 } // namespace widget
 } // namespace mozilla
--- a/widget/nsIScreenManager.idl
+++ b/widget/nsIScreenManager.idl
@@ -16,9 +16,12 @@ interface nsIScreenManager : nsISupports
     //
     // The coordinates are in pixels (not twips) and in screen coordinates.
     //
   nsIScreen screenForRect ( in long left, in long top, in long width, in long height ) ;
 
     // The screen with the menubar/taskbar. This shouldn't be needed very
     // often.
   readonly attribute nsIScreen primaryScreen;
+
+    // The number of screens available.
+  readonly attribute size_t numScreens;
 };
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -65,16 +65,17 @@
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 
 #include "mozilla/ipc/MessageChannel.h"
 #include <algorithm>
 #include <limits>
 
 #include "nsWindow.h"
+#include "nsAppRunner.h"
 
 #include <shellapi.h>
 #include <windows.h>
 #include <wtsapi32.h>
 #include <process.h>
 #include <commctrl.h>
 #include <unknwn.h>
 #include <psapi.h>
@@ -915,16 +916,20 @@ nsWindow::Create(nsIWidget* aParent,
   // Starting with Windows XP, a process always runs within a terminal services
   // session. In order to play nicely with RDP, fast user switching, and the
   // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
   // our HWND in order to receive this message.
   DebugOnly<BOOL> wtsRegistered = ::WTSRegisterSessionNotification(mWnd,
                                                        NOTIFY_FOR_THIS_SESSION);
   NS_ASSERTION(wtsRegistered, "WTSRegisterSessionNotification failed!\n");
 
+  // Ensure that we have the current screen setup, which may have changed due
+  // to a session unlock before we registered to receive WM_WTSESSION_CHANGE.
+  ScreenHelperWin::RefreshScreens();
+
   mDefaultIMC.Init(this);
   IMEHandler::InitInputContext(this, mInputContext);
 
   // Do some initialization work, but only if (a) it hasn't already been done,
   // and (b) this is the hidden window (which is conveniently created before
   // any visible windows but after the profile has been initialized).
   if (!sHaveInitializedPrefs && mWindowType == eWindowType_invisible) {
     sSwitchKeyboardLayout =
@@ -5145,16 +5150,26 @@ ExitThisProcessSafely()
   HANDLE process = GetCurrentProcess();
   if (TerminateProcess(GetCurrentProcess(), 0)) {
     // TerminateProcess is asynchronous, so we wait on our own process handle
     WaitForSingleObject(process, INFINITE);
   }
   MOZ_CRASH("Just in case extremis crash in ExitThisProcessSafely.");
 }
 
+const char16_t*
+GetQuitType()
+{
+  if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART, true)) {
+    return u"restart";
+  } else {
+    return nullptr;
+  }
+}
+
 // The main windows message processing method.
 bool
 nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
                          LRESULT *aRetValue)
 {
 #if defined(EVENT_DEBUG_OUTPUT)
   // First param shows all events, second param indicates whether
   // to show mouse move events. See nsWindowDbg for details.
@@ -5190,17 +5205,19 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       {
         // Ask if it's ok to quit, and store the answer until we
         // get WM_ENDSESSION signaling the round is complete.
         nsCOMPtr<nsIObserverService> obsServ =
           mozilla::services::GetObserverService();
         nsCOMPtr<nsISupportsPRBool> cancelQuit =
           do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
         cancelQuit->SetData(false);
-        obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
+
+        const char16_t* quitType = GetQuitType();;
+        obsServ->NotifyObservers(cancelQuit, "quit-application-requested", quitType);
 
         bool abortQuit;
         cancelQuit->GetData(&abortQuit);
         sCanQuit = abortQuit ? TRI_FALSE : TRI_TRUE;
       }
       *aRetValue = sCanQuit ? TRUE : FALSE;
       result = true;
       break;
@@ -5221,19 +5238,21 @@ nsWindow::ProcessMessage(UINT msg, WPARA
         // Let's fake a shutdown sequence without actually closing windows etc.
         // to avoid Windows killing us in the middle. A proper shutdown would
         // require having a chance to pump some messages. Unfortunately
         // Windows won't let us do that. Bug 212316.
         nsCOMPtr<nsIObserverService> obsServ =
           mozilla::services::GetObserverService();
         const char16_t* context = u"shutdown-persist";
         const char16_t* syncShutdown = u"syncShutdown";
+        const char16_t* quitType = GetQuitType();
+
         obsServ->NotifyObservers(nullptr, "quit-application-granted", syncShutdown);
         obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
-        obsServ->NotifyObservers(nullptr, "quit-application", nullptr);
+        obsServ->NotifyObservers(nullptr, "quit-application", quitType);
         obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context);
         obsServ->NotifyObservers(nullptr, "profile-change-teardown", context);
         obsServ->NotifyObservers(nullptr, "profile-before-change", context);
         obsServ->NotifyObservers(nullptr, "profile-before-change-qm", context);
         obsServ->NotifyObservers(nullptr, "profile-before-change-telemetry", context);
         ExitThisProcessSafely();
       }
       sCanQuit = TRI_UNKNOWN;
@@ -5259,16 +5278,20 @@ nsWindow::ProcessMessage(UINT msg, WPARA
     break;
 
     case WM_WTSSESSION_CHANGE:
     {
       switch (wParam) {
         case WTS_CONSOLE_CONNECT:
         case WTS_REMOTE_CONNECT:
         case WTS_SESSION_UNLOCK:
+          // We may have a new screen arrangement (WM_DISPLAYCHANGE is not
+          // sent when logging in)
+          ScreenHelperWin::RefreshScreens();
+
           // When a session becomes visible, we should invalidate.
           Invalidate(true, true, true);
           break;
         default:
           break;
       }
     }
     break;