• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Embedded Framework Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "libcef/browser/native/browser_platform_delegate_native_win.h"
6 
7 #include <shellapi.h>
8 #include <wininet.h>
9 #include <winspool.h>
10 
11 #include "libcef/browser/alloy/alloy_browser_host_impl.h"
12 #include "libcef/browser/context.h"
13 #include "libcef/browser/native/file_dialog_runner_win.h"
14 #include "libcef/browser/native/javascript_dialog_runner_win.h"
15 #include "libcef/browser/native/menu_runner_win.h"
16 #include "libcef/browser/native/window_delegate_view.h"
17 #include "libcef/browser/thread_util.h"
18 
19 #include "base/files/file_util.h"
20 #include "base/memory/ref_counted_memory.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/win/registry.h"
23 #include "base/win/win_util.h"
24 #include "content/public/browser/native_web_keyboard_event.h"
25 #include "third_party/blink/public/common/input/web_mouse_event.h"
26 #include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
27 #include "ui/aura/window.h"
28 #include "ui/base/win/shell.h"
29 #include "ui/display/display.h"
30 #include "ui/display/screen.h"
31 #include "ui/events/keycodes/dom/dom_key.h"
32 #include "ui/events/keycodes/dom/keycode_converter.h"
33 #include "ui/events/keycodes/keyboard_code_conversion_win.h"
34 #include "ui/events/keycodes/platform_key_map_win.h"
35 #include "ui/gfx/geometry/vector2d.h"
36 #include "ui/gfx/win/hwnd_util.h"
37 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
38 #include "ui/views/widget/widget.h"
39 #include "ui/views/win/hwnd_util.h"
40 
41 #pragma comment(lib, "dwmapi.lib")
42 
43 namespace {
44 
WriteTempFileAndView(scoped_refptr<base::RefCountedString> str)45 void WriteTempFileAndView(scoped_refptr<base::RefCountedString> str) {
46   CEF_REQUIRE_BLOCKING();
47 
48   base::FilePath tmp_file;
49   if (!base::CreateTemporaryFile(&tmp_file))
50     return;
51 
52   // The shell command will look at the file extension to identify the correct
53   // program to open.
54   tmp_file = tmp_file.AddExtension(L"txt");
55 
56   const std::string& data = str->data();
57   int write_ct = base::WriteFile(tmp_file, data.c_str(), data.size());
58   DCHECK_EQ(static_cast<int>(data.size()), write_ct);
59 
60   ui::win::OpenFileViaShell(tmp_file);
61 }
62 
63 // According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp:
64 // "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in
65 // ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6
66 // support URLS of 2083 chars in length, 2K is safe."
67 const int kMaxAddressLengthChars = 2048;
68 
HasExternalHandler(const std::string & scheme)69 bool HasExternalHandler(const std::string& scheme) {
70   base::win::RegKey key;
71   const std::wstring registry_path =
72       base::ASCIIToWide(scheme + "\\shell\\open\\command");
73   key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ);
74   if (key.Valid()) {
75     DWORD size = 0;
76     key.ReadValue(NULL, NULL, &size, NULL);
77     if (size > 2) {
78       // ShellExecute crashes the process when the command is empty.
79       // We check for "2" because it always returns the trailing NULL.
80       return true;
81     }
82   }
83 
84   return false;
85 }
86 
ExecuteExternalProtocol(const GURL & url)87 void ExecuteExternalProtocol(const GURL& url) {
88   CEF_REQUIRE_BLOCKING();
89 
90   if (!HasExternalHandler(url.scheme()))
91     return;
92 
93   const std::string& address = url.spec();
94   if (address.length() > kMaxAddressLengthChars)
95     return;
96 
97   ShellExecuteA(NULL, "open", address.c_str(), NULL, NULL, SW_SHOWNORMAL);
98 }
99 
100 // DPI value for 1x scale factor.
101 #define DPI_1X 96.0f
102 
GetWindowScaleFactor(HWND hwnd)103 float GetWindowScaleFactor(HWND hwnd) {
104   DCHECK(hwnd);
105 
106   if (base::win::IsProcessPerMonitorDpiAware()) {
107     // Let Windows tell us the correct DPI.
108     static auto get_dpi_for_window_func = []() {
109       return reinterpret_cast<decltype(::GetDpiForWindow)*>(
110           GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow"));
111     }();
112     if (get_dpi_for_window_func)
113       return static_cast<float>(get_dpi_for_window_func(hwnd)) / DPI_1X;
114   }
115 
116   // Fallback to the monitor that contains the window center point.
117   RECT cr;
118   GetWindowRect(hwnd, &cr);
119   return display::Screen::GetScreen()
120       ->GetDisplayNearestPoint(
121           gfx::Point((cr.right - cr.left) / 2, (cr.bottom - cr.top) / 2))
122       .device_scale_factor();
123 }
124 
125 }  // namespace
126 
CefBrowserPlatformDelegateNativeWin(const CefWindowInfo & window_info,SkColor background_color)127 CefBrowserPlatformDelegateNativeWin::CefBrowserPlatformDelegateNativeWin(
128     const CefWindowInfo& window_info,
129     SkColor background_color)
130     : CefBrowserPlatformDelegateNativeAura(window_info, background_color) {}
131 
BrowserDestroyed(CefBrowserHostBase * browser)132 void CefBrowserPlatformDelegateNativeWin::BrowserDestroyed(
133     CefBrowserHostBase* browser) {
134   CefBrowserPlatformDelegateNative::BrowserDestroyed(browser);
135 
136   if (host_window_created_) {
137     // Release the reference added in CreateHostWindow().
138     browser->Release();
139   }
140 }
141 
CreateHostWindow()142 bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() {
143   RegisterWindowClass();
144 
145   has_frame_ = !(window_info_.style & WS_CHILD);
146 
147   std::wstring windowName(CefString(&window_info_.window_name));
148 
149   // Create the new browser window.
150   CreateWindowEx(window_info_.ex_style, GetWndClass(), windowName.c_str(),
151                  window_info_.style, window_info_.x, window_info_.y,
152                  window_info_.width, window_info_.height,
153                  window_info_.parent_window, window_info_.menu,
154                  ::GetModuleHandle(NULL), this);
155 
156   // It's possible for CreateWindowEx to fail if the parent window was
157   // destroyed between the call to CreateBrowser and the above one.
158   DCHECK(window_info_.window);
159   if (!window_info_.window)
160     return false;
161 
162   host_window_created_ = true;
163 
164   // Add a reference that will later be released in DestroyBrowser().
165   browser_->AddRef();
166 
167   if (!called_enable_non_client_dpi_scaling_ && has_frame_ &&
168       base::win::IsProcessPerMonitorDpiAware()) {
169     // This call gets Windows to scale the non-client area when WM_DPICHANGED
170     // is fired on Windows versions < 10.0.14393.0.
171     // Derived signature; not available in headers.
172     static auto enable_child_window_dpi_message_func = []() {
173       using EnableChildWindowDpiMessagePtr = LRESULT(WINAPI*)(HWND, BOOL);
174       return reinterpret_cast<EnableChildWindowDpiMessagePtr>(GetProcAddress(
175           GetModuleHandle(L"user32.dll"), "EnableChildWindowDpiMessage"));
176     }();
177     if (enable_child_window_dpi_message_func)
178       enable_child_window_dpi_message_func(window_info_.window, TRUE);
179   }
180 
181   DCHECK(!window_widget_);
182 
183   // Convert from device coordinates to logical coordinates.
184   RECT cr;
185   GetClientRect(window_info_.window, &cr);
186   gfx::Point point = gfx::Point(cr.right, cr.bottom);
187   const float scale = GetWindowScaleFactor(window_info_.window);
188   point =
189       gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(point), 1.0f / scale));
190 
191   // Stay on top if top-most window hosting the web view is topmost.
192   HWND top_level_window = GetAncestor(window_info_.window, GA_ROOT);
193   DWORD top_level_window_ex_styles =
194       GetWindowLongPtr(top_level_window, GWL_EXSTYLE);
195   bool always_on_top =
196       (top_level_window_ex_styles & WS_EX_TOPMOST) == WS_EX_TOPMOST;
197 
198   CefWindowDelegateView* delegate_view = new CefWindowDelegateView(
199       GetBackgroundColor(), always_on_top, GetBoundsChangedCallback());
200   delegate_view->Init(window_info_.window, web_contents_,
201                       gfx::Rect(0, 0, point.x(), point.y()));
202 
203   window_widget_ = delegate_view->GetWidget();
204 
205   const HWND widget_hwnd = HWNDForWidget(window_widget_);
206   DCHECK(widget_hwnd);
207   const DWORD widget_ex_styles = GetWindowLongPtr(widget_hwnd, GWL_EXSTYLE);
208 
209   if (window_info_.ex_style & WS_EX_NOACTIVATE) {
210     // Add the WS_EX_NOACTIVATE style on the DesktopWindowTreeHostWin HWND
211     // so that HWNDMessageHandler::Show() called via Widget::Show() does not
212     // activate the window.
213     SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE,
214                      widget_ex_styles | WS_EX_NOACTIVATE);
215   }
216 
217   window_widget_->Show();
218 
219   if (window_info_.ex_style & WS_EX_NOACTIVATE) {
220     // Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the
221     // browser correctly activate and focus the window.
222     SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE, widget_ex_styles);
223   }
224 
225   return true;
226 }
227 
CloseHostWindow()228 void CefBrowserPlatformDelegateNativeWin::CloseHostWindow() {
229   if (window_info_.window != NULL) {
230     HWND frameWnd = GetAncestor(window_info_.window, GA_ROOT);
231     PostMessage(frameWnd, WM_CLOSE, 0, 0);
232   }
233 }
234 
GetHostWindowHandle() const235 CefWindowHandle CefBrowserPlatformDelegateNativeWin::GetHostWindowHandle()
236     const {
237   if (windowless_handler_)
238     return windowless_handler_->GetParentWindowHandle();
239   return window_info_.window;
240 }
241 
GetWindowWidget() const242 views::Widget* CefBrowserPlatformDelegateNativeWin::GetWindowWidget() const {
243   return window_widget_;
244 }
245 
SendFocusEvent(bool setFocus)246 void CefBrowserPlatformDelegateNativeWin::SendFocusEvent(bool setFocus) {
247   if (!setFocus)
248     return;
249 
250   if (web_contents_) {
251     // Give logical focus to the RenderWidgetHostViewAura in the views
252     // hierarchy. This does not change the native keyboard focus.
253     web_contents_->Focus();
254   }
255 
256   if (window_widget_) {
257     // Give native focus to the DesktopWindowTreeHostWin associated with the
258     // root window.
259     //
260     // The DesktopWindowTreeHostWin HandleNativeFocus/HandleNativeBlur methods
261     // are called in response to WM_SETFOCUS/WM_KILLFOCUS respectively. The
262     // implementation has been patched to call HandleActivationChanged which
263     // results in the following behaviors:
264     // 1. Update focus/activation state of the aura::Window indirectly via
265     //    wm::FocusController. This allows focus-related behaviors (e.g. focus
266     //    rings, flashing caret, onFocus/onBlur JS events, etc.) to work as
267     //    expected (see issue #1677).
268     // 2. Update focus state of the ui::InputMethod. If this does not occur
269     //    then InputMethodBase::GetTextInputClient will return NULL and
270     //    InputMethodWin::OnChar will fail to sent character events to the
271     //    renderer (see issue #1700).
272     //
273     // This differs from activation in Chrome which is handled via
274     // HWNDMessageHandler::PostProcessActivateMessage (Widget::Show indirectly
275     // calls HWNDMessageHandler::Activate which calls ::SetForegroundWindow
276     // resulting in a WM_ACTIVATE message being sent to the window). The Chrome
277     // code path doesn't work for CEF because IsTopLevelWindow in
278     // hwnd_message_handler.cc will return false and consequently
279     // HWNDMessageHandler::PostProcessActivateMessage will not be called.
280     //
281     // Activation events are usually reserved for the top-level window so
282     // triggering activation based on focus events may be incorrect in some
283     // circumstances. Revisit this implementation if additional problems are
284     // discovered.
285     ::SetFocus(HWNDForWidget(window_widget_));
286   }
287 }
288 
NotifyMoveOrResizeStarted()289 void CefBrowserPlatformDelegateNativeWin::NotifyMoveOrResizeStarted() {
290   // Call the parent method to dismiss any existing popups.
291   CefBrowserPlatformDelegateNative::NotifyMoveOrResizeStarted();
292 
293   if (!window_widget_)
294     return;
295 
296   // Notify DesktopWindowTreeHostWin of move events so that screen rectangle
297   // information is communicated to the renderer process and popups are
298   // displayed in the correct location.
299   views::DesktopWindowTreeHostWin* tree_host =
300       static_cast<views::DesktopWindowTreeHostWin*>(
301           aura::WindowTreeHost::GetForAcceleratedWidget(
302               HWNDForWidget(window_widget_)));
303   DCHECK(tree_host);
304   if (tree_host) {
305     // Cast to HWNDMessageHandlerDelegate so we can access HandleMove().
306     static_cast<views::HWNDMessageHandlerDelegate*>(tree_host)->HandleMove();
307   }
308 }
309 
SizeTo(int width,int height)310 void CefBrowserPlatformDelegateNativeWin::SizeTo(int width, int height) {
311   HWND window = window_info_.window;
312 
313   RECT rect = {0, 0, width, height};
314   DWORD style = GetWindowLong(window, GWL_STYLE);
315   DWORD ex_style = GetWindowLong(window, GWL_EXSTYLE);
316   bool has_menu = !(style & WS_CHILD) && (GetMenu(window) != NULL);
317 
318   // The size value is for the client area. Calculate the whole window size
319   // based on the current style.
320   AdjustWindowRectEx(&rect, style, has_menu, ex_style);
321 
322   // Size the window. The left/top values may be negative.
323   SetWindowPos(window, NULL, 0, 0, rect.right - rect.left,
324                rect.bottom - rect.top,
325                SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
326 }
327 
GetScreenPoint(const gfx::Point & view) const328 gfx::Point CefBrowserPlatformDelegateNativeWin::GetScreenPoint(
329     const gfx::Point& view) const {
330   if (windowless_handler_)
331     return windowless_handler_->GetParentScreenPoint(view);
332 
333   if (!window_info_.window)
334     return view;
335 
336   // Convert from logical coordinates to device coordinates.
337   const float scale = GetWindowScaleFactor(window_info_.window);
338   const gfx::Point& device_pt =
339       gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(view), scale));
340 
341   // Convert from client coordinates to screen coordinates.
342   POINT screen_pt = {device_pt.x(), device_pt.y()};
343   ClientToScreen(window_info_.window, &screen_pt);
344 
345   return gfx::Point(screen_pt.x, screen_pt.y);
346 }
347 
ViewText(const std::string & text)348 void CefBrowserPlatformDelegateNativeWin::ViewText(const std::string& text) {
349   std::string str = text;
350   scoped_refptr<base::RefCountedString> str_ref =
351       base::RefCountedString::TakeString(&str);
352   CEF_POST_USER_VISIBLE_TASK(base::Bind(WriteTempFileAndView, str_ref));
353 }
354 
HandleKeyboardEvent(const content::NativeWebKeyboardEvent & event)355 bool CefBrowserPlatformDelegateNativeWin::HandleKeyboardEvent(
356     const content::NativeWebKeyboardEvent& event) {
357   // Any unhandled keyboard/character messages are sent to DefWindowProc so that
358   // shortcut keys work correctly.
359   if (event.os_event) {
360     const MSG& msg = event.os_event->native_event();
361     return !DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
362   } else {
363     MSG msg = {};
364 
365     msg.hwnd = GetHostWindowHandle();
366     if (!msg.hwnd)
367       return false;
368 
369     switch (event.GetType()) {
370       case blink::WebInputEvent::Type::kRawKeyDown:
371         msg.message = event.is_system_key ? WM_SYSKEYDOWN : WM_KEYDOWN;
372         break;
373       case blink::WebInputEvent::Type::kKeyUp:
374         msg.message = event.is_system_key ? WM_SYSKEYUP : WM_KEYUP;
375         break;
376       case blink::WebInputEvent::Type::kChar:
377         msg.message = event.is_system_key ? WM_SYSCHAR : WM_CHAR;
378         break;
379       default:
380         NOTREACHED();
381         return false;
382     }
383 
384     msg.wParam = event.windows_key_code;
385 
386     UINT scan_code = ::MapVirtualKeyW(event.windows_key_code, MAPVK_VK_TO_VSC);
387     msg.lParam = (scan_code << 16) |  // key scan code
388                  1;                   // key repeat count
389     if (event.GetModifiers() & content::NativeWebKeyboardEvent::kAltKey)
390       msg.lParam |= (1 << 29);
391 
392     return !DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
393   }
394 }
395 
396 // static
HandleExternalProtocol(const GURL & url)397 void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {
398   CEF_POST_USER_VISIBLE_TASK(base::Bind(ExecuteExternalProtocol, url));
399 }
400 
GetEventHandle(const content::NativeWebKeyboardEvent & event) const401 CefEventHandle CefBrowserPlatformDelegateNativeWin::GetEventHandle(
402     const content::NativeWebKeyboardEvent& event) const {
403   if (!event.os_event)
404     return NULL;
405   return const_cast<CefEventHandle>(&event.os_event->native_event());
406 }
407 
408 std::unique_ptr<CefFileDialogRunner>
CreateFileDialogRunner()409 CefBrowserPlatformDelegateNativeWin::CreateFileDialogRunner() {
410   return base::WrapUnique(new CefFileDialogRunnerWin);
411 }
412 
413 std::unique_ptr<CefJavaScriptDialogRunner>
CreateJavaScriptDialogRunner()414 CefBrowserPlatformDelegateNativeWin::CreateJavaScriptDialogRunner() {
415   return base::WrapUnique(new CefJavaScriptDialogRunnerWin);
416 }
417 
418 std::unique_ptr<CefMenuRunner>
CreateMenuRunner()419 CefBrowserPlatformDelegateNativeWin::CreateMenuRunner() {
420   return base::WrapUnique(new CefMenuRunnerWin);
421 }
422 
GetDialogPosition(const gfx::Size & size)423 gfx::Point CefBrowserPlatformDelegateNativeWin::GetDialogPosition(
424     const gfx::Size& size) {
425   const gfx::Size& max_size = GetMaximumDialogSize();
426   return gfx::Point((max_size.width() - size.width()) / 2,
427                     (max_size.height() - size.height()) / 2);
428 }
429 
GetMaximumDialogSize()430 gfx::Size CefBrowserPlatformDelegateNativeWin::GetMaximumDialogSize() {
431   return GetWindowWidget()->GetWindowBoundsInScreen().size();
432 }
433 
TranslateUiKeyEvent(const CefKeyEvent & key_event) const434 ui::KeyEvent CefBrowserPlatformDelegateNativeWin::TranslateUiKeyEvent(
435     const CefKeyEvent& key_event) const {
436   int flags = TranslateUiEventModifiers(key_event.modifiers);
437   ui::KeyboardCode key_code =
438       ui::KeyboardCodeForWindowsKeyCode(key_event.windows_key_code);
439   ui::DomCode dom_code =
440       ui::KeycodeConverter::NativeKeycodeToDomCode(key_event.native_key_code);
441   base::TimeTicks time_stamp = GetEventTimeStamp();
442 
443   if (key_event.type == KEYEVENT_CHAR) {
444     return ui::KeyEvent(key_event.windows_key_code /* character */, key_code,
445                         dom_code, flags, time_stamp);
446   }
447 
448   ui::EventType type = ui::ET_UNKNOWN;
449   switch (key_event.type) {
450     case KEYEVENT_RAWKEYDOWN:
451     case KEYEVENT_KEYDOWN:
452       type = ui::ET_KEY_PRESSED;
453       break;
454     case KEYEVENT_KEYUP:
455       type = ui::ET_KEY_RELEASED;
456       break;
457     default:
458       NOTREACHED();
459   }
460 
461   ui::DomKey dom_key =
462       ui::PlatformKeyMap::DomKeyFromKeyboardCode(key_code, &flags);
463   return ui::KeyEvent(type, key_code, dom_code, flags, dom_key, time_stamp);
464 }
465 
GetUiWheelEventOffset(int deltaX,int deltaY) const466 gfx::Vector2d CefBrowserPlatformDelegateNativeWin::GetUiWheelEventOffset(
467     int deltaX,
468     int deltaY) const {
469   static const ULONG defaultScrollCharsPerWheelDelta = 1;
470   static const FLOAT scrollbarPixelsPerLine = 100.0f / 3.0f;
471   static const ULONG defaultScrollLinesPerWheelDelta = 3;
472 
473   float wheelDeltaX = float(deltaX) / WHEEL_DELTA;
474   float wheelDeltaY = float(deltaY) / WHEEL_DELTA;
475   float scrollDeltaX = wheelDeltaX;
476   float scrollDeltaY = wheelDeltaY;
477 
478   ULONG scrollChars = defaultScrollCharsPerWheelDelta;
479   SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
480   scrollDeltaX *= static_cast<FLOAT>(scrollChars) * scrollbarPixelsPerLine;
481 
482   ULONG scrollLines = defaultScrollLinesPerWheelDelta;
483   SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
484   scrollDeltaY *= static_cast<FLOAT>(scrollLines) * scrollbarPixelsPerLine;
485 
486   return gfx::Vector2d(scrollDeltaX, scrollDeltaY);
487 }
488 
489 // static
RegisterWindowClass()490 void CefBrowserPlatformDelegateNativeWin::RegisterWindowClass() {
491   static bool registered = false;
492   if (registered)
493     return;
494 
495   // Register the window class
496   WNDCLASSEX wcex = {
497       /* cbSize = */ sizeof(WNDCLASSEX),
498       /* style = */ CS_HREDRAW | CS_VREDRAW,
499       /* lpfnWndProc = */ CefBrowserPlatformDelegateNativeWin::WndProc,
500       /* cbClsExtra = */ 0,
501       /* cbWndExtra = */ 0,
502       /* hInstance = */ ::GetModuleHandle(NULL),
503       /* hIcon = */ NULL,
504       /* hCursor = */ LoadCursor(NULL, IDC_ARROW),
505       /* hbrBackground = */ 0,
506       /* lpszMenuName = */ NULL,
507       /* lpszClassName = */ CefBrowserPlatformDelegateNativeWin::GetWndClass(),
508       /* hIconSm = */ NULL,
509   };
510   RegisterClassEx(&wcex);
511 
512   registered = true;
513 }
514 
515 // static
GetWndClass()516 LPCTSTR CefBrowserPlatformDelegateNativeWin::GetWndClass() {
517   return L"CefBrowserWindow";
518 }
519 
520 // static
WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)521 LRESULT CALLBACK CefBrowserPlatformDelegateNativeWin::WndProc(HWND hwnd,
522                                                               UINT message,
523                                                               WPARAM wParam,
524                                                               LPARAM lParam) {
525   CefBrowserPlatformDelegateNativeWin* platform_delegate = nullptr;
526   CefBrowserHostBase* browser = nullptr;
527 
528   if (message != WM_NCCREATE) {
529     platform_delegate = static_cast<CefBrowserPlatformDelegateNativeWin*>(
530         gfx::GetWindowUserData(hwnd));
531     if (platform_delegate) {
532       browser = platform_delegate->browser_;
533     }
534   }
535 
536   switch (message) {
537     case WM_CLOSE:
538       if (browser && !browser->TryCloseBrowser()) {
539         // Cancel the close.
540         return 0;
541       }
542 
543       // Allow the close.
544       break;
545 
546     case WM_NCCREATE: {
547       CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
548       platform_delegate =
549           reinterpret_cast<CefBrowserPlatformDelegateNativeWin*>(
550               cs->lpCreateParams);
551       DCHECK(platform_delegate);
552       // Associate |platform_delegate| with the window handle.
553       gfx::SetWindowUserData(hwnd, platform_delegate);
554       platform_delegate->window_info_.window = hwnd;
555 
556       if (platform_delegate->has_frame_ &&
557           base::win::IsProcessPerMonitorDpiAware()) {
558         // This call gets Windows to scale the non-client area when
559         // WM_DPICHANGED is fired on Windows versions >= 10.0.14393.0.
560         static auto enable_non_client_dpi_scaling_func = []() {
561           return reinterpret_cast<decltype(::EnableNonClientDpiScaling)*>(
562               GetProcAddress(GetModuleHandle(L"user32.dll"),
563                              "EnableNonClientDpiScaling"));
564         }();
565         platform_delegate->called_enable_non_client_dpi_scaling_ =
566             !!(enable_non_client_dpi_scaling_func &&
567                enable_non_client_dpi_scaling_func(hwnd));
568       }
569     } break;
570 
571     case WM_NCDESTROY:
572       if (platform_delegate) {
573         // Clear the user data pointer.
574         gfx::SetWindowUserData(hwnd, NULL);
575 
576         // Force the browser to be destroyed. This will result in a call to
577         // BrowserDestroyed() that will release the reference added in
578         // CreateHostWindow().
579         static_cast<AlloyBrowserHostImpl*>(browser)->WindowDestroyed();
580       }
581       break;
582 
583     case WM_SIZE:
584       if (platform_delegate && platform_delegate->window_widget_) {
585         // Pass window resize events to the HWND for the DesktopNativeWidgetAura
586         // root window. Passing size 0x0 (wParam == SIZE_MINIMIZED, for example)
587         // will cause the widget to be hidden which reduces resource usage.
588         RECT rc;
589         GetClientRect(hwnd, &rc);
590         SetWindowPos(HWNDForWidget(platform_delegate->window_widget_), NULL,
591                      rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
592                      SWP_NOZORDER);
593       }
594       return 0;
595 
596     case WM_MOVING:
597     case WM_MOVE:
598       if (browser)
599         browser->NotifyMoveOrResizeStarted();
600       return 0;
601 
602     case WM_SETFOCUS:
603       // Selecting "Close window" from the task bar menu may send a focus
604       // notification even though the window is currently disabled (e.g. while
605       // a modal JS dialog is displayed).
606       if (browser && ::IsWindowEnabled(hwnd))
607         browser->SetFocus(true);
608       return 0;
609 
610     case WM_ERASEBKGND:
611       return 0;
612 
613     case WM_DPICHANGED:
614       if (platform_delegate && platform_delegate->has_frame_) {
615         // Suggested size and position of the current window scaled for the
616         // new DPI.
617         const RECT* rect = reinterpret_cast<RECT*>(lParam);
618         SetWindowPos(platform_delegate->GetHostWindowHandle(), NULL, rect->left,
619                      rect->top, rect->right - rect->left,
620                      rect->bottom - rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
621       }
622       break;
623   }
624 
625   return DefWindowProc(hwnd, message, wParam, lParam);
626 }
627