Windows: Fix per-monitor DPI scaling of top-level browser windows and placement of context menus (issue #2313)

This commit is contained in:
Marshall Greenblatt 2018-02-21 16:52:02 -05:00
parent 57b9cf9ddd
commit a5a5e7ff08
5 changed files with 119 additions and 57 deletions

View File

@ -20,6 +20,7 @@
#include "base/memory/ref_counted_memory.h" #include "base/memory/ref_counted_memory.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h" #include "base/win/registry.h"
#include "base/win/win_util.h"
#include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/native_web_keyboard_event.h"
#include "third_party/WebKit/public/platform/WebMouseEvent.h" #include "third_party/WebKit/public/platform/WebMouseEvent.h"
#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" #include "third_party/WebKit/public/platform/WebMouseWheelEvent.h"
@ -95,6 +96,31 @@ void ExecuteExternalProtocol(const GURL& url) {
ShellExecuteA(NULL, "open", address.c_str(), NULL, NULL, SW_SHOWNORMAL); ShellExecuteA(NULL, "open", address.c_str(), NULL, NULL, SW_SHOWNORMAL);
} }
// DPI value for 1x scale factor.
#define DPI_1X 96.0f
float GetWindowScaleFactor(HWND hwnd) {
DCHECK(hwnd);
if (base::win::IsProcessPerMonitorDpiAware()) {
// Let Windows tell us the correct DPI.
static auto get_dpi_for_window_func = []() {
return reinterpret_cast<decltype(::GetDpiForWindow)*>(
GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow"));
}();
if (get_dpi_for_window_func)
return static_cast<float>(get_dpi_for_window_func(hwnd)) / DPI_1X;
}
// Fallback to the monitor that contains the window center point.
RECT cr;
GetWindowRect(hwnd, &cr);
return display::Screen::GetScreen()
->GetDisplayNearestPoint(
gfx::Point((cr.right - cr.left) / 2, (cr.bottom - cr.top) / 2))
.device_scale_factor();
}
} // namespace } // namespace
CefBrowserPlatformDelegateNativeWin::CefBrowserPlatformDelegateNativeWin( CefBrowserPlatformDelegateNativeWin::CefBrowserPlatformDelegateNativeWin(
@ -117,40 +143,49 @@ void CefBrowserPlatformDelegateNativeWin::BrowserDestroyed(
bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() { bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() {
RegisterWindowClass(); RegisterWindowClass();
has_frame_ = !(window_info_.style & WS_CHILD);
std::wstring windowName(CefString(&window_info_.window_name)); std::wstring windowName(CefString(&window_info_.window_name));
// Create the new browser window. // Create the new browser window.
window_info_.window = CreateWindowEx( CreateWindowEx(window_info_.ex_style, GetWndClass(), windowName.c_str(),
window_info_.ex_style, GetWndClass(), windowName.c_str(), window_info_.style, window_info_.x, window_info_.y,
window_info_.style, window_info_.x, window_info_.y, window_info_.width, window_info_.width, window_info_.height,
window_info_.height, window_info_.parent_window, window_info_.menu, window_info_.parent_window, window_info_.menu,
::GetModuleHandle(NULL), NULL); ::GetModuleHandle(NULL), this);
// It's possible for CreateWindowEx to fail if the parent window was // It's possible for CreateWindowEx to fail if the parent window was
// destroyed between the call to CreateBrowser and the above one. // destroyed between the call to CreateBrowser and the above one.
DCHECK(window_info_.window != NULL); DCHECK(window_info_.window);
if (!window_info_.window) if (!window_info_.window)
return false; return false;
host_window_created_ = true; host_window_created_ = true;
// Set window user data to this object for future reference from the window
// procedure.
gfx::SetWindowUserData(window_info_.window, this);
// Add a reference that will later be released in DestroyBrowser(). // Add a reference that will later be released in DestroyBrowser().
browser_->AddRef(); browser_->AddRef();
RECT cr; if (!called_enable_non_client_dpi_scaling_ && has_frame_ &&
GetClientRect(window_info_.window, &cr); base::win::IsProcessPerMonitorDpiAware()) {
// This call gets Windows to scale the non-client area when WM_DPICHANGED
// is fired on Windows versions < 10.0.14393.0.
// Derived signature; not available in headers.
static auto enable_child_window_dpi_message_func = []() {
using EnableChildWindowDpiMessagePtr = LRESULT(WINAPI*)(HWND, BOOL);
return reinterpret_cast<EnableChildWindowDpiMessagePtr>(GetProcAddress(
GetModuleHandle(L"user32.dll"), "EnableChildWindowDpiMessage"));
}();
if (enable_child_window_dpi_message_func)
enable_child_window_dpi_message_func(window_info_.window, TRUE);
}
DCHECK(!window_widget_); DCHECK(!window_widget_);
// Adjust for potential display scaling. // Convert from device coordinates to logical coordinates.
RECT cr;
GetClientRect(window_info_.window, &cr);
gfx::Point point = gfx::Point(cr.right, cr.bottom); gfx::Point point = gfx::Point(cr.right, cr.bottom);
float scale = display::Screen::GetScreen() const float scale = GetWindowScaleFactor(window_info_.window);
->GetDisplayNearestPoint(point)
.device_scale_factor();
point = point =
gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(point), 1.0f / scale)); gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(point), 1.0f / scale));
@ -270,19 +305,19 @@ gfx::Point CefBrowserPlatformDelegateNativeWin::GetScreenPoint(
if (windowless_handler_) if (windowless_handler_)
return windowless_handler_->GetParentScreenPoint(view); return windowless_handler_->GetParentScreenPoint(view);
if (!window_widget_) if (!window_info_.window)
return view; return view;
aura::Window* window = window_widget_->GetNativeView(); // Convert from logical coordinates to device coordinates.
const gfx::Rect& bounds_in_screen = window->GetBoundsInScreen(); const float scale = GetWindowScaleFactor(window_info_.window);
const gfx::Point& screen_point = gfx::Point(bounds_in_screen.x() + view.x(), const gfx::Point& device_pt =
bounds_in_screen.y() + view.y()); gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(view), scale));
// Adjust for potential display scaling. // Convert from client coordinates to screen coordinates.
float scale = display::Screen::GetScreen() POINT screen_pt = {device_pt.x(), device_pt.y()};
->GetDisplayNearestPoint(screen_point) ClientToScreen(window_info_.window, &screen_pt);
.device_scale_factor();
return gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(screen_point), scale)); return gfx::Point(screen_pt.x, screen_pt.y);
} }
void CefBrowserPlatformDelegateNativeWin::ViewText(const std::string& text) { void CefBrowserPlatformDelegateNativeWin::ViewText(const std::string& text) {
@ -562,12 +597,15 @@ LRESULT CALLBACK CefBrowserPlatformDelegateNativeWin::WndProc(HWND hwnd,
UINT message, UINT message,
WPARAM wParam, WPARAM wParam,
LPARAM lParam) { LPARAM lParam) {
CefBrowserPlatformDelegateNativeWin* platform_delegate = CefBrowserPlatformDelegateNativeWin* platform_delegate = nullptr;
static_cast<CefBrowserPlatformDelegateNativeWin*>(
gfx::GetWindowUserData(hwnd));
CefBrowserHostImpl* browser = nullptr; CefBrowserHostImpl* browser = nullptr;
if (platform_delegate)
browser = platform_delegate->browser_; if (message != WM_NCCREATE) {
platform_delegate = static_cast<CefBrowserPlatformDelegateNativeWin*>(
gfx::GetWindowUserData(hwnd));
if (platform_delegate)
browser = platform_delegate->browser_;
}
switch (message) { switch (message) {
case WM_CLOSE: case WM_CLOSE:
@ -579,6 +617,31 @@ LRESULT CALLBACK CefBrowserPlatformDelegateNativeWin::WndProc(HWND hwnd,
// Allow the close. // Allow the close.
break; break;
case WM_NCCREATE: {
CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
platform_delegate =
reinterpret_cast<CefBrowserPlatformDelegateNativeWin*>(
cs->lpCreateParams);
DCHECK(platform_delegate);
// Associate |platform_delegate| with the window handle.
gfx::SetWindowUserData(hwnd, platform_delegate);
platform_delegate->window_info_.window = hwnd;
if (platform_delegate->has_frame_ &&
base::win::IsProcessPerMonitorDpiAware()) {
// This call gets Windows to scale the non-client area when
// WM_DPICHANGED is fired on Windows versions >= 10.0.14393.0.
static auto enable_non_client_dpi_scaling_func = []() {
return reinterpret_cast<decltype(::EnableNonClientDpiScaling)*>(
GetProcAddress(GetModuleHandle(L"user32.dll"),
"EnableNonClientDpiScaling"));
}();
platform_delegate->called_enable_non_client_dpi_scaling_ =
!!(enable_non_client_dpi_scaling_func &&
enable_non_client_dpi_scaling_func(hwnd));
}
} break;
case WM_NCDESTROY: case WM_NCDESTROY:
if (platform_delegate) { if (platform_delegate) {
// Clear the user data pointer. // Clear the user data pointer.
@ -617,6 +680,17 @@ LRESULT CALLBACK CefBrowserPlatformDelegateNativeWin::WndProc(HWND hwnd,
case WM_ERASEBKGND: case WM_ERASEBKGND:
return 0; return 0;
case WM_DPICHANGED:
if (platform_delegate && platform_delegate->has_frame_) {
// Suggested size and position of the current window scaled for the
// new DPI.
const RECT* rect = reinterpret_cast<RECT*>(lParam);
SetWindowPos(platform_delegate->GetHostWindowHandle(), NULL, rect->left,
rect->top, rect->right - rect->left,
rect->bottom - rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
} }
return DefWindowProc(hwnd, message, wParam, lParam); return DefWindowProc(hwnd, message, wParam, lParam);

View File

@ -68,6 +68,9 @@ class CefBrowserPlatformDelegateNativeWin
// Widget hosting the web contents. It will be deleted automatically when the // Widget hosting the web contents. It will be deleted automatically when the
// associated root window is destroyed. // associated root window is destroyed.
views::Widget* window_widget_; views::Widget* window_widget_;
bool has_frame_ = false;
bool called_enable_non_client_dpi_scaling_ = false;
}; };
#endif // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_WIN_H_ #endif // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_NATIVE_WIN_H_

View File

@ -77,15 +77,15 @@ bool IsProcessPerMonitorDpiAware() {
} }
// DPI value for 1x scale factor. // DPI value for 1x scale factor.
#define DPI_1X 96 #define DPI_1X 96.0f
int GetWindowScaleFactor(HWND hwnd) { float GetWindowScaleFactor(HWND hwnd) {
if (hwnd && IsProcessPerMonitorDpiAware()) { if (hwnd && IsProcessPerMonitorDpiAware()) {
typedef UINT(WINAPI * GetDpiForWindowPtr)(HWND); typedef UINT(WINAPI * GetDpiForWindowPtr)(HWND);
static GetDpiForWindowPtr func_ptr = reinterpret_cast<GetDpiForWindowPtr>( static GetDpiForWindowPtr func_ptr = reinterpret_cast<GetDpiForWindowPtr>(
GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow")); GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow"));
if (func_ptr) if (func_ptr)
return func_ptr(hwnd) / DPI_1X; return static_cast<float>(func_ptr(hwnd)) / DPI_1X;
} }
return client::GetDeviceScaleFactor(); return client::GetDeviceScaleFactor();
@ -638,6 +638,7 @@ void RootWindowWin::OnSize(bool minimized) {
const int font_height = LogicalToDevice(14, GetWindowScaleFactor(hwnd_)); const int font_height = LogicalToDevice(14, GetWindowScaleFactor(hwnd_));
if (font_height != font_height_) { if (font_height != font_height_) {
font_height_ = font_height;
if (font_) { if (font_) {
DeleteObject(font_); DeleteObject(font_);
} }
@ -716,18 +717,11 @@ void RootWindowWin::OnDpiChanged(WPARAM wParam, LPARAM lParam) {
return; return;
} }
if (browser_window_) { if (browser_window_ && with_osr_) {
if (with_osr_) { // Scale factor for the new display.
browser_window_->SetDeviceScaleFactor(LOWORD(wParam) / DPI_1X); const float display_scale_factor =
} else { static_cast<float>(LOWORD(wParam)) / DPI_1X;
// Forward the DPI change to the browser window handle. browser_window_->SetDeviceScaleFactor(display_scale_factor);
HWND browser_hwnd = browser_window_->GetWindowHandle();
if (browser_hwnd) {
RECT child_rect = {};
SendMessage(browser_hwnd, WM_DPICHANGED, wParam,
reinterpret_cast<LPARAM>(&child_rect));
}
}
} }
// Suggested size and position of the current window scaled for the new DPI. // Suggested size and position of the current window scaled for the new DPI.
@ -928,7 +922,7 @@ void RootWindowWin::OnCreate(LPCREATESTRUCT lpCreateStruct) {
::SetMenu(hwnd_, NULL); ::SetMenu(hwnd_, NULL);
} }
const int device_scale_factor = GetWindowScaleFactor(hwnd_); const float device_scale_factor = GetWindowScaleFactor(hwnd_);
if (with_osr_) { if (with_osr_) {
browser_window_->SetDeviceScaleFactor(device_scale_factor); browser_window_->SetDeviceScaleFactor(device_scale_factor);
@ -938,7 +932,6 @@ void RootWindowWin::OnCreate(LPCREATESTRUCT lpCreateStruct) {
// Create the browser window. // Create the browser window.
CefRect cef_rect(rect.left, rect.top, rect.right - rect.left, CefRect cef_rect(rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top); rect.bottom - rect.top);
cef_rect = client::DeviceToLogical(cef_rect, device_scale_factor);
browser_window_->CreateBrowser(hwnd_, cef_rect, browser_settings_, browser_window_->CreateBrowser(hwnd_, cef_rect, browser_settings_,
delegate_->GetRequestContext(this)); delegate_->GetRequestContext(this));
} else { } else {
@ -1043,7 +1036,7 @@ void RootWindowWin::OnAutoResize(const CefSize& new_size) {
if (new_width < 200) if (new_width < 200)
new_width = 200; new_width = 200;
const int device_scale_factor = GetWindowScaleFactor(hwnd_); const float device_scale_factor = GetWindowScaleFactor(hwnd_);
RECT rect = {0, 0, LogicalToDevice(new_width, device_scale_factor), RECT rect = {0, 0, LogicalToDevice(new_width, device_scale_factor),
LogicalToDevice(new_size.height, device_scale_factor)}; LogicalToDevice(new_size.height, device_scale_factor)};
DWORD style = GetWindowLong(hwnd_, GWL_STYLE); DWORD style = GetWindowLong(hwnd_, GWL_STYLE);

View File

@ -25,13 +25,6 @@ int DeviceToLogical(int value, float device_scale_factor) {
return static_cast<int>(std::floor(scaled_val)); return static_cast<int>(std::floor(scaled_val));
} }
CefRect DeviceToLogical(const CefRect& value, float device_scale_factor) {
return CefRect(DeviceToLogical(value.x, device_scale_factor),
DeviceToLogical(value.y, device_scale_factor),
DeviceToLogical(value.width, device_scale_factor),
DeviceToLogical(value.height, device_scale_factor));
}
void DeviceToLogical(CefMouseEvent& value, float device_scale_factor) { void DeviceToLogical(CefMouseEvent& value, float device_scale_factor) {
value.x = DeviceToLogical(value.x, device_scale_factor); value.x = DeviceToLogical(value.x, device_scale_factor);
value.y = DeviceToLogical(value.y, device_scale_factor); value.y = DeviceToLogical(value.y, device_scale_factor);

View File

@ -16,7 +16,6 @@ CefRect LogicalToDevice(const CefRect& value, float device_scale_factor);
// Convert |value| from device coordinates to logical coordinates. // Convert |value| from device coordinates to logical coordinates.
int DeviceToLogical(int value, float device_scale_factor); int DeviceToLogical(int value, float device_scale_factor);
CefRect DeviceToLogical(const CefRect& value, float device_scale_factor);
void DeviceToLogical(CefMouseEvent& value, float device_scale_factor); void DeviceToLogical(CefMouseEvent& value, float device_scale_factor);
} // namespace client } // namespace client