-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1656526 - Show blank window prior to loading xul on Windows r=mho…
…well See bug for justification. This patch aims to display a blank window prior to loading/prefetching xul.dll. It also has a placeholder for drawing a skeleton UI into that window. Note that this is disabled by default based on a registry value, as there are still kinks to work out (for instance, what happens if we aren't actually going to display a window, because, say, Firefox is already running.) This just gives a basic implementation to dogfood, and facilitates distributing work across multiple contributors. Onto the details. The patch achieves its goal by creating a window and assigning its handle to a static variable, which will be consumed inside nsWindow::Create by the first toplevel window we want to make. nsWindow::Create will take ownership of the window handle, restyle it to its own liking, and then proceed as if everything is normal and it had created the window itself. Differential Revision: https://phabricator.services.mozilla.com/D86263
- Loading branch information
1 parent
0826bf6
commit 7534e5c
Showing
7 changed files
with
391 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,282 @@ | ||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */ | ||
/* 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 "EarlyBlankWindow.h" | ||
|
||
#include "mozilla/Assertions.h" | ||
#include "mozilla/Attributes.h" | ||
#include "mozilla/glue/Debug.h" | ||
#include "mozilla/WindowsVersion.h" | ||
|
||
namespace mozilla { | ||
|
||
static const wchar_t kEarlyBlankWindowKeyPath[] = | ||
L"SOFTWARE" | ||
L"\\" MOZ_APP_VENDOR L"\\" MOZ_APP_BASENAME L"\\EarlyBlankWindowSettings"; | ||
|
||
static bool sEarlyBlankWindowEnabled = false; | ||
static HWND sEarlyBlankWindowHandle; | ||
static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512); | ||
|
||
typedef BOOL(WINAPI* EnableNonClientDpiScalingProc)(HWND); | ||
static EnableNonClientDpiScalingProc sEnableNonClientDpiScaling = NULL; | ||
typedef int(WINAPI* GetSystemMetricsForDpiProc)(int, UINT); | ||
GetSystemMetricsForDpiProc sGetSystemMetricsForDpi = NULL; | ||
typedef UINT(WINAPI* GetDpiForWindowProc)(HWND); | ||
GetDpiForWindowProc sGetDpiForWindow = NULL; | ||
|
||
#if WINVER < 0x0605 | ||
WINUSERAPI DPI_AWARENESS_CONTEXT WINAPI GetThreadDpiAwarenessContext(); | ||
WINUSERAPI BOOL WINAPI AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT, | ||
DPI_AWARENESS_CONTEXT); | ||
#endif /* WINVER < 0x0605 */ | ||
|
||
static uint32_t sWindowWidth; | ||
static uint32_t sWindowHeight; | ||
static double sCSSToDevPixelScaling; | ||
|
||
// We style our initial blank window as a WS_POPUP to eliminate the window | ||
// caption and all that jazz. Alternatively, we could do a big dance in our | ||
// window proc to paint into the nonclient area similarly to what we do in | ||
// nsWindow, but it would be nontrivial code duplication, and the added | ||
// complexity would not be worth it, given that we can just change the | ||
// window style to our liking when we consume sEarlyBlankWindowHandle from | ||
// nsWindow. | ||
static DWORD sWindowStyle = WS_POPUP; | ||
|
||
// We add WS_EX_TOOLWINDOW here so that we do not produce a toolbar entry. | ||
// We were not able to avoid flickering in the toolbar without this change, | ||
// as the call to ::SetWindowLongPtrW to restyle the window inside | ||
// nsWindow causes the toolbar entry to momentarily disappear. Not sure of | ||
// the cause of this, but it doesn't feel too wrong to be missing a toolbar | ||
// entry only so long as we are displaying a skeleton UI. | ||
static DWORD sWindowStyleEx = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW; | ||
|
||
// We could use nsAutoRegKey, but including nsWindowsHelpers.h causes build | ||
// failures in random places because we're in mozglue. Overall it should be | ||
// simpler and cleaner to just step around that issue with this class: | ||
class MOZ_RAII AutoCloseRegKey { | ||
public: | ||
explicit AutoCloseRegKey(HKEY key) : mKey(key) {} | ||
~AutoCloseRegKey() { ::RegCloseKey(mKey); } | ||
|
||
private: | ||
HKEY mKey; | ||
}; | ||
|
||
int CSSToDevPixels(int cssPixels, double scaling) { | ||
double asDouble = (double)cssPixels * scaling; | ||
return floor(asDouble + 0.5); | ||
} | ||
|
||
void DrawSkeletonUI(HWND hWnd) { | ||
// TODO - here is where we will draw a skeleton UI for the application | ||
} | ||
|
||
LRESULT WINAPI EarlyBlankWindowProc(HWND hWnd, UINT msg, WPARAM wParam, | ||
LPARAM lParam) { | ||
if (msg == WM_NCCREATE && sEnableNonClientDpiScaling) { | ||
sEnableNonClientDpiScaling(hWnd); | ||
} | ||
|
||
return ::DefWindowProcW(hWnd, msg, wParam, lParam); | ||
} | ||
|
||
bool OpenEarlyBlankWindowRegKey(HKEY& key) { | ||
DWORD disposition; | ||
LSTATUS result = | ||
::RegCreateKeyExW(HKEY_CURRENT_USER, kEarlyBlankWindowKeyPath, 0, nullptr, | ||
0, KEY_ALL_ACCESS, nullptr, &key, &disposition); | ||
|
||
if (result != ERROR_SUCCESS) { | ||
return false; | ||
} | ||
|
||
if (disposition == REG_CREATED_NEW_KEY) { | ||
return false; | ||
} | ||
|
||
if (disposition == REG_OPENED_EXISTING_KEY) { | ||
return true; | ||
} | ||
|
||
::RegCloseKey(key); | ||
return false; | ||
} | ||
|
||
void CreateAndStoreEarlyBlankWindow(HINSTANCE hInstance) { | ||
HKEY regKey; | ||
if (!IsWin10OrLater() || !OpenEarlyBlankWindowRegKey(regKey)) { | ||
return; | ||
} | ||
AutoCloseRegKey closeKey(regKey); | ||
|
||
DWORD dataLen = sizeof(uint32_t); | ||
uint32_t enabled; | ||
LSTATUS result = | ||
::RegGetValueW(regKey, nullptr, L"enabled", RRF_RT_REG_DWORD, nullptr, | ||
reinterpret_cast<PBYTE>(&enabled), &dataLen); | ||
if (result != ERROR_SUCCESS || enabled == 0) { | ||
return; | ||
} | ||
sEarlyBlankWindowEnabled = true; | ||
|
||
// EnableNonClientDpiScaling must be called during the initialization of | ||
// the window, so we have to find it and store it before we create our | ||
// window in order to run it in our WndProc. | ||
HMODULE user32Dll = ::GetModuleHandleW(L"user32"); | ||
|
||
if (user32Dll) { | ||
auto getThreadDpiAwarenessContext = | ||
(decltype(GetThreadDpiAwarenessContext)*)::GetProcAddress( | ||
user32Dll, "GetThreadDpiAwarenessContext"); | ||
auto areDpiAwarenessContextsEqual = | ||
(decltype(AreDpiAwarenessContextsEqual)*)::GetProcAddress( | ||
user32Dll, "AreDpiAwarenessContextsEqual"); | ||
if (getThreadDpiAwarenessContext && areDpiAwarenessContextsEqual && | ||
areDpiAwarenessContextsEqual(getThreadDpiAwarenessContext(), | ||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) { | ||
// Only per-monitor v1 requires these workarounds. | ||
sEnableNonClientDpiScaling = | ||
(EnableNonClientDpiScalingProc)::GetProcAddress( | ||
user32Dll, "EnableNonClientDpiScaling"); | ||
} | ||
|
||
sGetSystemMetricsForDpi = (GetSystemMetricsForDpiProc)::GetProcAddress( | ||
user32Dll, "GetSystemMetricsForDpi"); | ||
sGetDpiForWindow = | ||
(GetDpiForWindowProc)::GetProcAddress(user32Dll, "GetDpiForWindow"); | ||
} | ||
|
||
WNDCLASSW wc; | ||
wc.style = CS_DBLCLKS; | ||
wc.lpfnWndProc = EarlyBlankWindowProc; | ||
wc.cbClsExtra = 0; | ||
wc.cbWndExtra = 0; | ||
wc.hInstance = hInstance; | ||
wc.hIcon = ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon); | ||
wc.hCursor = ::LoadCursor(nullptr, IDC_WAIT); | ||
wc.hbrBackground = nullptr; | ||
wc.lpszMenuName = nullptr; | ||
|
||
// TODO: just ensure we disable this if we've overridden the window class | ||
wc.lpszClassName = L"MozillaWindowClass"; | ||
|
||
if (!::RegisterClassW(&wc)) { | ||
printf_stderr("RegisterClassW error %lu\n", GetLastError()); | ||
return; | ||
} | ||
|
||
uint32_t screenX; | ||
result = ::RegGetValueW(regKey, nullptr, L"screenX", RRF_RT_REG_DWORD, | ||
nullptr, reinterpret_cast<PBYTE>(&screenX), &dataLen); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Error reading screenX %lu\n", GetLastError()); | ||
return; | ||
} | ||
|
||
uint32_t screenY; | ||
result = ::RegGetValueW(regKey, nullptr, L"screenY", RRF_RT_REG_DWORD, | ||
nullptr, reinterpret_cast<PBYTE>(&screenY), &dataLen); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Error reading screenY %lu\n", GetLastError()); | ||
return; | ||
} | ||
|
||
result = ::RegGetValueW(regKey, nullptr, L"width", RRF_RT_REG_DWORD, nullptr, | ||
reinterpret_cast<PBYTE>(&sWindowWidth), &dataLen); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Error reading width %lu\n", GetLastError()); | ||
return; | ||
} | ||
|
||
result = ::RegGetValueW(regKey, nullptr, L"height", RRF_RT_REG_DWORD, nullptr, | ||
reinterpret_cast<PBYTE>(&sWindowHeight), &dataLen); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Error reading height %lu\n", GetLastError()); | ||
return; | ||
} | ||
|
||
dataLen = sizeof(double); | ||
result = ::RegGetValueW( | ||
regKey, nullptr, L"cssToDevPixelScaling", RRF_RT_REG_BINARY, nullptr, | ||
reinterpret_cast<PBYTE>(&sCSSToDevPixelScaling), &dataLen); | ||
if (result != ERROR_SUCCESS || dataLen != sizeof(double)) { | ||
printf_stderr("Error reading cssToDevPixelScaling %lu\n", GetLastError()); | ||
return; | ||
} | ||
|
||
sEarlyBlankWindowHandle = | ||
::CreateWindowExW(sWindowStyleEx, L"MozillaWindowClass", L"", | ||
sWindowStyle, screenX, screenY, sWindowWidth, | ||
sWindowHeight, nullptr, nullptr, hInstance, nullptr); | ||
|
||
::ShowWindow(sEarlyBlankWindowHandle, SW_SHOWNORMAL); | ||
::SetWindowPos(sEarlyBlankWindowHandle, 0, 0, 0, 0, 0, | ||
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | | ||
SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER); | ||
DrawSkeletonUI(sEarlyBlankWindowHandle); | ||
::RedrawWindow(sEarlyBlankWindowHandle, NULL, NULL, RDW_INVALIDATE); | ||
} | ||
|
||
HWND ConsumeEarlyBlankWindowHandle() { | ||
HWND result = sEarlyBlankWindowHandle; | ||
sEarlyBlankWindowHandle = nullptr; | ||
return result; | ||
} | ||
|
||
void PersistEarlyBlankWindowValues(int screenX, int screenY, int width, | ||
int height, double cssToDevPixelScaling) { | ||
if (!sEarlyBlankWindowEnabled) { | ||
return; | ||
} | ||
|
||
HKEY regKey; | ||
if (!OpenEarlyBlankWindowRegKey(regKey)) { | ||
return; | ||
} | ||
AutoCloseRegKey closeKey(regKey); | ||
|
||
LSTATUS result; | ||
result = ::RegSetValueExW(regKey, L"screenX", 0, REG_DWORD, | ||
reinterpret_cast<PBYTE>(&screenX), sizeof(screenX)); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Failed persisting screenX to Windows registry\n"); | ||
return; | ||
} | ||
|
||
result = ::RegSetValueExW(regKey, L"screenY", 0, REG_DWORD, | ||
reinterpret_cast<PBYTE>(&screenY), sizeof(screenY)); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Failed persisting screenY to Windows registry\n"); | ||
return; | ||
} | ||
|
||
result = ::RegSetValueExW(regKey, L"width", 0, REG_DWORD, | ||
reinterpret_cast<PBYTE>(&width), sizeof(width)); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Failed persisting width to Windows registry\n"); | ||
return; | ||
} | ||
|
||
result = ::RegSetValueExW(regKey, L"height", 0, REG_DWORD, | ||
reinterpret_cast<PBYTE>(&height), sizeof(height)); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr("Failed persisting height to Windows registry\n"); | ||
return; | ||
} | ||
|
||
result = ::RegSetValueExW(regKey, L"cssToDevPixelScaling", 0, REG_BINARY, | ||
reinterpret_cast<PBYTE>(&cssToDevPixelScaling), | ||
sizeof(cssToDevPixelScaling)); | ||
if (result != ERROR_SUCCESS) { | ||
printf_stderr( | ||
"Failed persisting cssToDevPixelScaling to Windows registry\n"); | ||
return; | ||
} | ||
} | ||
|
||
} // namespace mozilla |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | ||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */ | ||
/* 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 https://mozilla.org/MPL/2.0/. */ | ||
|
||
#ifndef EarlyBlankWindow_h_ | ||
#define EarlyBlankWindow_h_ | ||
|
||
#include <windows.h> | ||
#include "mozilla/Types.h" | ||
|
||
namespace mozilla { | ||
|
||
MFBT_API void CreateAndStoreEarlyBlankWindow(HINSTANCE hInstance); | ||
MFBT_API HWND ConsumeEarlyBlankWindowHandle(); | ||
MFBT_API void PersistEarlyBlankWindowValues(int screenX, int screenY, int width, | ||
int height, | ||
double cssToDevPixelScaling); | ||
|
||
} // namespace mozilla | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.