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