| // Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "libcef/browser/native/browser_platform_delegate_native_win.h" |
| |
| #include <shellapi.h> |
| #include <wininet.h> |
| #include <winspool.h> |
| |
| #include "libcef/browser/browser_host_impl.h" |
| #include "libcef/browser/context.h" |
| #include "libcef/browser/native/file_dialog_runner_win.h" |
| #include "libcef/browser/native/javascript_dialog_runner_win.h" |
| #include "libcef/browser/native/menu_runner_win.h" |
| #include "libcef/browser/native/window_delegate_view.h" |
| #include "libcef/browser/thread_util.h" |
| |
| #include "base/files/file_util.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/registry.h" |
| #include "base/win/win_util.h" |
| #include "content/public/browser/native_web_keyboard_event.h" |
| #include "third_party/blink/public/common/input/web_mouse_event.h" |
| #include "third_party/blink/public/common/input/web_mouse_wheel_event.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/win/shell.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/events/keycodes/dom/dom_key.h" |
| #include "ui/events/keycodes/dom/keycode_converter.h" |
| #include "ui/events/keycodes/keyboard_code_conversion_win.h" |
| #include "ui/events/keycodes/platform_key_map_win.h" |
| #include "ui/gfx/geometry/vector2d.h" |
| #include "ui/gfx/win/hwnd_util.h" |
| #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/win/hwnd_util.h" |
| |
| #pragma comment(lib, "dwmapi.lib") |
| |
| namespace { |
| |
| void WriteTempFileAndView(scoped_refptr<base::RefCountedString> str) { |
| CEF_REQUIRE_BLOCKING(); |
| |
| base::FilePath tmp_file; |
| if (!base::CreateTemporaryFile(&tmp_file)) |
| return; |
| |
| // The shell command will look at the file extension to identify the correct |
| // program to open. |
| tmp_file = tmp_file.AddExtension(L"txt"); |
| |
| const std::string& data = str->data(); |
| int write_ct = base::WriteFile(tmp_file, data.c_str(), data.size()); |
| DCHECK_EQ(static_cast<int>(data.size()), write_ct); |
| |
| ui::win::OpenFileViaShell(tmp_file); |
| } |
| |
| // According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp: |
| // "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in |
| // ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6 |
| // support URLS of 2083 chars in length, 2K is safe." |
| const int kMaxAddressLengthChars = 2048; |
| |
| bool HasExternalHandler(const std::string& scheme) { |
| base::win::RegKey key; |
| const std::wstring registry_path = |
| base::ASCIIToUTF16(scheme + "\\shell\\open\\command"); |
| key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ); |
| if (key.Valid()) { |
| DWORD size = 0; |
| key.ReadValue(NULL, NULL, &size, NULL); |
| if (size > 2) { |
| // ShellExecute crashes the process when the command is empty. |
| // We check for "2" because it always returns the trailing NULL. |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void ExecuteExternalProtocol(const GURL& url) { |
| CEF_REQUIRE_BLOCKING(); |
| |
| if (!HasExternalHandler(url.scheme())) |
| return; |
| |
| const std::string& address = url.spec(); |
| if (address.length() > kMaxAddressLengthChars) |
| return; |
| |
| 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 |
| |
| CefBrowserPlatformDelegateNativeWin::CefBrowserPlatformDelegateNativeWin( |
| const CefWindowInfo& window_info, |
| SkColor background_color, |
| bool use_shared_texture, |
| bool use_external_begin_frame) |
| : CefBrowserPlatformDelegateNativeAura(window_info, |
| background_color, |
| use_shared_texture, |
| use_external_begin_frame), |
| host_window_created_(false), |
| window_widget_(nullptr) {} |
| |
| void CefBrowserPlatformDelegateNativeWin::BrowserDestroyed( |
| CefBrowserHostImpl* browser) { |
| CefBrowserPlatformDelegate::BrowserDestroyed(browser); |
| |
| if (host_window_created_) { |
| // Release the reference added in CreateHostWindow(). |
| browser->Release(); |
| } |
| } |
| |
| bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() { |
| RegisterWindowClass(); |
| |
| has_frame_ = !(window_info_.style & WS_CHILD); |
| |
| std::wstring windowName(CefString(&window_info_.window_name)); |
| |
| // Create the new browser window. |
| CreateWindowEx(window_info_.ex_style, GetWndClass(), windowName.c_str(), |
| window_info_.style, window_info_.x, window_info_.y, |
| window_info_.width, window_info_.height, |
| window_info_.parent_window, window_info_.menu, |
| ::GetModuleHandle(NULL), this); |
| |
| // It's possible for CreateWindowEx to fail if the parent window was |
| // destroyed between the call to CreateBrowser and the above one. |
| DCHECK(window_info_.window); |
| if (!window_info_.window) |
| return false; |
| |
| host_window_created_ = true; |
| |
| // Add a reference that will later be released in DestroyBrowser(). |
| browser_->AddRef(); |
| |
| if (!called_enable_non_client_dpi_scaling_ && 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. |
| // 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_); |
| |
| // Convert from device coordinates to logical coordinates. |
| RECT cr; |
| GetClientRect(window_info_.window, &cr); |
| gfx::Point point = gfx::Point(cr.right, cr.bottom); |
| const float scale = GetWindowScaleFactor(window_info_.window); |
| point = |
| gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(point), 1.0f / scale)); |
| |
| // Stay on top if top-most window hosting the web view is topmost. |
| HWND top_level_window = GetAncestor(window_info_.window, GA_ROOT); |
| DWORD top_level_window_ex_styles = |
| GetWindowLongPtr(top_level_window, GWL_EXSTYLE); |
| bool always_on_top = |
| (top_level_window_ex_styles & WS_EX_TOPMOST) == WS_EX_TOPMOST; |
| |
| CefWindowDelegateView* delegate_view = new CefWindowDelegateView( |
| GetBackgroundColor(), always_on_top, GetBoundsChangedCallback()); |
| delegate_view->Init(window_info_.window, browser_->web_contents(), |
| gfx::Rect(0, 0, point.x(), point.y())); |
| |
| window_widget_ = delegate_view->GetWidget(); |
| |
| const HWND widget_hwnd = HWNDForWidget(window_widget_); |
| DCHECK(widget_hwnd); |
| const DWORD widget_ex_styles = GetWindowLongPtr(widget_hwnd, GWL_EXSTYLE); |
| |
| if (window_info_.ex_style & WS_EX_NOACTIVATE) { |
| // Add the WS_EX_NOACTIVATE style on the DesktopWindowTreeHostWin HWND |
| // so that HWNDMessageHandler::Show() called via Widget::Show() does not |
| // activate the window. |
| SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE, |
| widget_ex_styles | WS_EX_NOACTIVATE); |
| } |
| |
| window_widget_->Show(); |
| |
| if (window_info_.ex_style & WS_EX_NOACTIVATE) { |
| // Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the |
| // browser correctly activate and focus the window. |
| SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE, widget_ex_styles); |
| } |
| |
| return true; |
| } |
| |
| void CefBrowserPlatformDelegateNativeWin::CloseHostWindow() { |
| if (window_info_.window != NULL) { |
| HWND frameWnd = GetAncestor(window_info_.window, GA_ROOT); |
| PostMessage(frameWnd, WM_CLOSE, 0, 0); |
| } |
| } |
| |
| CefWindowHandle CefBrowserPlatformDelegateNativeWin::GetHostWindowHandle() |
| const { |
| if (windowless_handler_) |
| return windowless_handler_->GetParentWindowHandle(); |
| return window_info_.window; |
| } |
| |
| views::Widget* CefBrowserPlatformDelegateNativeWin::GetWindowWidget() const { |
| return window_widget_; |
| } |
| |
| void CefBrowserPlatformDelegateNativeWin::SendFocusEvent(bool setFocus) { |
| if (!setFocus) |
| return; |
| |
| if (browser_->web_contents()) { |
| // Give logical focus to the RenderWidgetHostViewAura in the views |
| // hierarchy. This does not change the native keyboard focus. |
| browser_->web_contents()->Focus(); |
| } |
| |
| if (window_widget_) { |
| // Give native focus to the DesktopWindowTreeHostWin associated with the |
| // root window. |
| // |
| // The DesktopWindowTreeHostWin HandleNativeFocus/HandleNativeBlur methods |
| // are called in response to WM_SETFOCUS/WM_KILLFOCUS respectively. The |
| // implementation has been patched to call HandleActivationChanged which |
| // results in the following behaviors: |
| // 1. Update focus/activation state of the aura::Window indirectly via |
| // wm::FocusController. This allows focus-related behaviors (e.g. focus |
| // rings, flashing caret, onFocus/onBlur JS events, etc.) to work as |
| // expected (see issue #1677). |
| // 2. Update focus state of the ui::InputMethod. If this does not occur |
| // then InputMethodBase::GetTextInputClient will return NULL and |
| // InputMethodWin::OnChar will fail to sent character events to the |
| // renderer (see issue #1700). |
| // |
| // This differs from activation in Chrome which is handled via |
| // HWNDMessageHandler::PostProcessActivateMessage (Widget::Show indirectly |
| // calls HWNDMessageHandler::Activate which calls ::SetForegroundWindow |
| // resulting in a WM_ACTIVATE message being sent to the window). The Chrome |
| // code path doesn't work for CEF because IsTopLevelWindow in |
| // hwnd_message_handler.cc will return false and consequently |
| // HWNDMessageHandler::PostProcessActivateMessage will not be called. |
| // |
| // Activation events are usually reserved for the top-level window so |
| // triggering activation based on focus events may be incorrect in some |
| // circumstances. Revisit this implementation if additional problems are |
| // discovered. |
| ::SetFocus(HWNDForWidget(window_widget_)); |
| } |
| } |
| |
| void CefBrowserPlatformDelegateNativeWin::NotifyMoveOrResizeStarted() { |
| // Call the parent method to dismiss any existing popups. |
| CefBrowserPlatformDelegate::NotifyMoveOrResizeStarted(); |
| |
| if (!window_widget_) |
| return; |
| |
| // Notify DesktopWindowTreeHostWin of move events so that screen rectangle |
| // information is communicated to the renderer process and popups are |
| // displayed in the correct location. |
| views::DesktopWindowTreeHostWin* tree_host = |
| static_cast<views::DesktopWindowTreeHostWin*>( |
| aura::WindowTreeHost::GetForAcceleratedWidget( |
| HWNDForWidget(window_widget_))); |
| DCHECK(tree_host); |
| if (tree_host) { |
| // Cast to HWNDMessageHandlerDelegate so we can access HandleMove(). |
| static_cast<views::HWNDMessageHandlerDelegate*>(tree_host)->HandleMove(); |
| } |
| } |
| |
| void CefBrowserPlatformDelegateNativeWin::SizeTo(int width, int height) { |
| HWND window = window_info_.window; |
| |
| RECT rect = {0, 0, width, height}; |
| DWORD style = GetWindowLong(window, GWL_STYLE); |
| DWORD ex_style = GetWindowLong(window, GWL_EXSTYLE); |
| bool has_menu = !(style & WS_CHILD) && (GetMenu(window) != NULL); |
| |
| // The size value is for the client area. Calculate the whole window size |
| // based on the current style. |
| AdjustWindowRectEx(&rect, style, has_menu, ex_style); |
| |
| // Size the window. The left/top values may be negative. |
| SetWindowPos(window, NULL, 0, 0, rect.right - rect.left, |
| rect.bottom - rect.top, |
| SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); |
| } |
| |
| gfx::Point CefBrowserPlatformDelegateNativeWin::GetScreenPoint( |
| const gfx::Point& view) const { |
| if (windowless_handler_) |
| return windowless_handler_->GetParentScreenPoint(view); |
| |
| if (!window_info_.window) |
| return view; |
| |
| // Convert from logical coordinates to device coordinates. |
| const float scale = GetWindowScaleFactor(window_info_.window); |
| const gfx::Point& device_pt = |
| gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(view), scale)); |
| |
| // Convert from client coordinates to screen coordinates. |
| POINT screen_pt = {device_pt.x(), device_pt.y()}; |
| ClientToScreen(window_info_.window, &screen_pt); |
| |
| return gfx::Point(screen_pt.x, screen_pt.y); |
| } |
| |
| void CefBrowserPlatformDelegateNativeWin::ViewText(const std::string& text) { |
| std::string str = text; |
| scoped_refptr<base::RefCountedString> str_ref = |
| base::RefCountedString::TakeString(&str); |
| CEF_POST_USER_VISIBLE_TASK(base::Bind(WriteTempFileAndView, str_ref)); |
| } |
| |
| bool CefBrowserPlatformDelegateNativeWin::HandleKeyboardEvent( |
| const content::NativeWebKeyboardEvent& event) { |
| // Any unhandled keyboard/character messages are sent to DefWindowProc so that |
| // shortcut keys work correctly. |
| if (event.os_event) { |
| const MSG& msg = event.os_event->native_event(); |
| return !DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); |
| } else { |
| MSG msg = {}; |
| |
| msg.hwnd = GetHostWindowHandle(); |
| if (!msg.hwnd) |
| return false; |
| |
| switch (event.GetType()) { |
| case blink::WebInputEvent::kRawKeyDown: |
| msg.message = event.is_system_key ? WM_SYSKEYDOWN : WM_KEYDOWN; |
| break; |
| case blink::WebInputEvent::kKeyUp: |
| msg.message = event.is_system_key ? WM_SYSKEYUP : WM_KEYUP; |
| break; |
| case blink::WebInputEvent::kChar: |
| msg.message = event.is_system_key ? WM_SYSCHAR : WM_CHAR; |
| break; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| |
| msg.wParam = event.windows_key_code; |
| |
| UINT scan_code = ::MapVirtualKeyW(event.windows_key_code, MAPVK_VK_TO_VSC); |
| msg.lParam = (scan_code << 16) | // key scan code |
| 1; // key repeat count |
| if (event.GetModifiers() & content::NativeWebKeyboardEvent::kAltKey) |
| msg.lParam |= (1 << 29); |
| |
| return !DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); |
| } |
| } |
| |
| // static |
| void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) { |
| CEF_POST_USER_VISIBLE_TASK(base::Bind(ExecuteExternalProtocol, url)); |
| } |
| |
| CefEventHandle CefBrowserPlatformDelegateNativeWin::GetEventHandle( |
| const content::NativeWebKeyboardEvent& event) const { |
| if (!event.os_event) |
| return NULL; |
| return const_cast<CefEventHandle>(&event.os_event->native_event()); |
| } |
| |
| std::unique_ptr<CefFileDialogRunner> |
| CefBrowserPlatformDelegateNativeWin::CreateFileDialogRunner() { |
| return base::WrapUnique(new CefFileDialogRunnerWin); |
| } |
| |
| std::unique_ptr<CefJavaScriptDialogRunner> |
| CefBrowserPlatformDelegateNativeWin::CreateJavaScriptDialogRunner() { |
| return base::WrapUnique(new CefJavaScriptDialogRunnerWin); |
| } |
| |
| std::unique_ptr<CefMenuRunner> |
| CefBrowserPlatformDelegateNativeWin::CreateMenuRunner() { |
| return base::WrapUnique(new CefMenuRunnerWin); |
| } |
| |
| gfx::Point CefBrowserPlatformDelegateNativeWin::GetDialogPosition( |
| const gfx::Size& size) { |
| const gfx::Size& max_size = GetMaximumDialogSize(); |
| return gfx::Point((max_size.width() - size.width()) / 2, |
| (max_size.height() - size.height()) / 2); |
| } |
| |
| gfx::Size CefBrowserPlatformDelegateNativeWin::GetMaximumDialogSize() { |
| return GetWindowWidget()->GetWindowBoundsInScreen().size(); |
| } |
| |
| ui::KeyEvent CefBrowserPlatformDelegateNativeWin::TranslateUiKeyEvent( |
| const CefKeyEvent& key_event) const { |
| int flags = TranslateUiEventModifiers(key_event.modifiers); |
| ui::KeyboardCode key_code = |
| ui::KeyboardCodeForWindowsKeyCode(key_event.windows_key_code); |
| ui::DomCode dom_code = |
| ui::KeycodeConverter::NativeKeycodeToDomCode(key_event.native_key_code); |
| base::TimeTicks time_stamp = GetEventTimeStamp(); |
| |
| if (key_event.type == KEYEVENT_CHAR) { |
| return ui::KeyEvent(key_event.windows_key_code /* character */, key_code, |
| dom_code, flags, time_stamp); |
| } |
| |
| ui::EventType type = ui::ET_UNKNOWN; |
| switch (key_event.type) { |
| case KEYEVENT_RAWKEYDOWN: |
| case KEYEVENT_KEYDOWN: |
| type = ui::ET_KEY_PRESSED; |
| break; |
| case KEYEVENT_KEYUP: |
| type = ui::ET_KEY_RELEASED; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| ui::DomKey dom_key = |
| ui::PlatformKeyMap::DomKeyFromKeyboardCode(key_code, &flags); |
| return ui::KeyEvent(type, key_code, dom_code, flags, dom_key, time_stamp); |
| } |
| |
| gfx::Vector2d CefBrowserPlatformDelegateNativeWin::GetUiWheelEventOffset( |
| int deltaX, |
| int deltaY) const { |
| static const ULONG defaultScrollCharsPerWheelDelta = 1; |
| static const FLOAT scrollbarPixelsPerLine = 100.0f / 3.0f; |
| static const ULONG defaultScrollLinesPerWheelDelta = 3; |
| |
| float wheelDeltaX = float(deltaX) / WHEEL_DELTA; |
| float wheelDeltaY = float(deltaY) / WHEEL_DELTA; |
| float scrollDeltaX = wheelDeltaX; |
| float scrollDeltaY = wheelDeltaY; |
| |
| ULONG scrollChars = defaultScrollCharsPerWheelDelta; |
| SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0); |
| scrollDeltaX *= static_cast<FLOAT>(scrollChars) * scrollbarPixelsPerLine; |
| |
| ULONG scrollLines = defaultScrollLinesPerWheelDelta; |
| SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0); |
| scrollDeltaY *= static_cast<FLOAT>(scrollLines) * scrollbarPixelsPerLine; |
| |
| return gfx::Vector2d(scrollDeltaX, scrollDeltaY); |
| } |
| |
| base::TimeTicks CefBrowserPlatformDelegateNativeWin::GetEventTimeStamp() const { |
| return base::TimeTicks() + |
| base::TimeDelta::FromMilliseconds(GetMessageTime()); |
| } |
| |
| // static |
| void CefBrowserPlatformDelegateNativeWin::RegisterWindowClass() { |
| static bool registered = false; |
| if (registered) |
| return; |
| |
| // Register the window class |
| WNDCLASSEX wcex = { |
| /* cbSize = */ sizeof(WNDCLASSEX), |
| /* style = */ CS_HREDRAW | CS_VREDRAW, |
| /* lpfnWndProc = */ CefBrowserPlatformDelegateNativeWin::WndProc, |
| /* cbClsExtra = */ 0, |
| /* cbWndExtra = */ 0, |
| /* hInstance = */ ::GetModuleHandle(NULL), |
| /* hIcon = */ NULL, |
| /* hCursor = */ LoadCursor(NULL, IDC_ARROW), |
| /* hbrBackground = */ 0, |
| /* lpszMenuName = */ NULL, |
| /* lpszClassName = */ CefBrowserPlatformDelegateNativeWin::GetWndClass(), |
| /* hIconSm = */ NULL, |
| }; |
| RegisterClassEx(&wcex); |
| |
| registered = true; |
| } |
| |
| // static |
| LPCTSTR CefBrowserPlatformDelegateNativeWin::GetWndClass() { |
| return L"CefBrowserWindow"; |
| } |
| |
| // static |
| LRESULT CALLBACK CefBrowserPlatformDelegateNativeWin::WndProc(HWND hwnd, |
| UINT message, |
| WPARAM wParam, |
| LPARAM lParam) { |
| CefBrowserPlatformDelegateNativeWin* platform_delegate = nullptr; |
| CefBrowserHostImpl* browser = nullptr; |
| |
| if (message != WM_NCCREATE) { |
| platform_delegate = static_cast<CefBrowserPlatformDelegateNativeWin*>( |
| gfx::GetWindowUserData(hwnd)); |
| if (platform_delegate) |
| browser = platform_delegate->browser_; |
| } |
| |
| switch (message) { |
| case WM_CLOSE: |
| if (browser && !browser->TryCloseBrowser()) { |
| // Cancel the close. |
| return 0; |
| } |
| |
| // Allow the close. |
| 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: |
| if (platform_delegate) { |
| // Clear the user data pointer. |
| gfx::SetWindowUserData(hwnd, NULL); |
| |
| // Force the browser to be destroyed. This will result in a call to |
| // BrowserDestroyed() that will release the reference added in |
| // CreateHostWindow(). |
| browser->WindowDestroyed(); |
| } |
| break; |
| |
| case WM_SIZE: |
| if (platform_delegate && platform_delegate->window_widget_) { |
| // Pass window resize events to the HWND for the DesktopNativeWidgetAura |
| // root window. Passing size 0x0 (wParam == SIZE_MINIMIZED, for example) |
| // will cause the widget to be hidden which reduces resource usage. |
| RECT rc; |
| GetClientRect(hwnd, &rc); |
| SetWindowPos(HWNDForWidget(platform_delegate->window_widget_), NULL, |
| rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, |
| SWP_NOZORDER); |
| } |
| return 0; |
| |
| case WM_MOVING: |
| case WM_MOVE: |
| if (browser) |
| browser->NotifyMoveOrResizeStarted(); |
| return 0; |
| |
| case WM_SETFOCUS: |
| // Selecting "Close window" from the task bar menu may send a focus |
| // notification even though the window is currently disabled (e.g. while |
| // a modal JS dialog is displayed). |
| if (browser && ::IsWindowEnabled(hwnd)) |
| browser->SetFocus(true); |
| return 0; |
| |
| case WM_ERASEBKGND: |
| 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); |
| } |