Bug 603903 - register for restart, restore session, fix pre-login issues
Three main things are in the patch:
- Use RegisterApplicationRestart behind the preference
toolkit.winRegisterApplicationRestart
- A workaround in ScreenHelperWin to get the info for all monitors even when
run before login (a Windows 10 "feature" when automatically restarted),
EnumDisplayMonitors with the "DISPLAY" DC doesn't work.
- If the pref is set, force restoring session state (act as if it was a
restart)
MozReview-Commit-ID: G9rIqpfEAVH
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1879,17 +1879,57 @@ 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
+ 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 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
@@ -4730,16 +4770,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/windows/ScreenHelperWin.cpp
+++ b/widget/windows/ScreenHelperWin.cpp
@@ -11,48 +11,57 @@
#include "WinUtils.h"
static mozilla::LazyLogModule sScreenLog("WidgetScreen");
namespace mozilla {
namespace widget {
BOOL CALLBACK
-CollectMonitors(HMONITOR aMon, HDC hDCScreen, LPRECT, LPARAM ioParam)
+CollectMonitors(HMONITOR aMon, HDC, LPRECT, LPARAM ioParam)
{
auto screens = reinterpret_cast<nsTArray<RefPtr<Screen>>*>(ioParam);
BOOL success = FALSE;
- MONITORINFO info;
- info.cbSize = sizeof(MONITORINFO);
+ MONITORINFOEX info;
+ info.cbSize = sizeof(MONITORINFOEX);
success = ::GetMonitorInfoW(aMon, &info);
if (!success) {
MOZ_LOG(sScreenLog, LogLevel::Error, ("GetMonitorInfoW failed"));
return TRUE; // continue the enumeration
}
+
double scale = WinUtils::LogToPhysFactor(aMon);
DesktopToLayoutDeviceScale contentsScaleFactor;
if (WinUtils::IsPerMonitorDPIAware()) {
contentsScaleFactor.scale = 1.0;
} else {
contentsScaleFactor.scale = scale;
}
CSSToLayoutDeviceScale defaultCssScaleFactor(scale);
LayoutDeviceIntRect rect(info.rcMonitor.left, info.rcMonitor.top,
info.rcMonitor.right - info.rcMonitor.left,
info.rcMonitor.bottom - info.rcMonitor.top);
LayoutDeviceIntRect availRect(info.rcWork.left, info.rcWork.top,
info.rcWork.right - info.rcWork.left,
info.rcWork.bottom - info.rcWork.top);
- uint32_t pixelDepth = ::GetDeviceCaps(hDCScreen, BITSPIXEL);
+
+ HDC hDC = CreateDC(nullptr, info.szDevice, nullptr, nullptr);
+ if (!hDC) {
+ MOZ_LOG(sScreenLog, LogLevel::Error, ("CollectMonitors CreateDC failed"));
+ return TRUE;
+ }
+ uint32_t pixelDepth = ::GetDeviceCaps(hDC, BITSPIXEL);
+ DeleteDC(hDC);
if (pixelDepth == 32) {
// If a device uses 32 bits per pixel, it's still only using 8 bits
// per color component, which is what our callers want to know.
// (Some devices report 32 and some devices report 24.)
pixelDepth = 24;
}
+
float dpi = WinUtils::MonitorDPI(aMon);
MOZ_LOG(sScreenLog, LogLevel::Debug,
("New screen [%d %d %d %d (%d %d %d %d) %d %f %f %f]",
rect.X(), rect.Y(), rect.Width(), rect.Height(),
availRect.X(), availRect.Y(), availRect.Width(), availRect.Height(),
pixelDepth, contentsScaleFactor.scale, defaultCssScaleFactor.scale,
dpi));
auto screen = new Screen(rect, availRect,
@@ -69,22 +78,19 @@ CollectMonitors(HMONITOR aMon, HDC hDCSc
}
void
ScreenHelperWin::RefreshScreens()
{
MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
AutoTArray<RefPtr<Screen>, 4> screens;
- HDC hdc = ::CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
- NS_ASSERTION(hdc,"CreateDC Failure");
- BOOL result = ::EnumDisplayMonitors(hdc, nullptr,
+ BOOL result = ::EnumDisplayMonitors(nullptr, nullptr,
(MONITORENUMPROC)CollectMonitors,
(LPARAM)&screens);
- ::DeleteDC(hdc);
if (!result) {
NS_WARNING("Unable to EnumDisplayMonitors");
}
ScreenManager& screenManager = ScreenManager::GetSingleton();
screenManager.Refresh(Move(screens));
}
} // namespace widget
--- 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>
@@ -5152,16 +5153,33 @@ 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, false)) {
+ DWORD cchCmdLine = 0;
+ HRESULT rc =
+ ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr, &cchCmdLine, nullptr);
+ if (rc == S_OK) {
+ return u"restart";
+ } else {
+ return nullptr;
+ }
+ } 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.
@@ -5197,17 +5215,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;
@@ -5228,19 +5248,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;