1 // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/root_window_win.h"
6
7 #include <shellscalingapi.h>
8
9 #include "include/base/cef_build.h"
10 #include "include/base/cef_callback.h"
11 #include "include/cef_app.h"
12 #include "tests/cefclient/browser/browser_window_osr_win.h"
13 #include "tests/cefclient/browser/browser_window_std_win.h"
14 #include "tests/cefclient/browser/main_context.h"
15 #include "tests/cefclient/browser/resource.h"
16 #include "tests/cefclient/browser/temp_window.h"
17 #include "tests/cefclient/browser/window_test_runner_win.h"
18 #include "tests/shared/browser/geometry_util.h"
19 #include "tests/shared/browser/main_message_loop.h"
20 #include "tests/shared/browser/util_win.h"
21 #include "tests/shared/common/client_switches.h"
22
23 #define MAX_URL_LENGTH 255
24 #define BUTTON_WIDTH 72
25 #define URLBAR_HEIGHT 24
26
27 namespace client {
28
29 namespace {
30
31 // Message handler for the About box.
AboutWndProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)32 INT_PTR CALLBACK AboutWndProc(HWND hDlg,
33 UINT message,
34 WPARAM wParam,
35 LPARAM lParam) {
36 UNREFERENCED_PARAMETER(lParam);
37 switch (message) {
38 case WM_INITDIALOG:
39 return TRUE;
40
41 case WM_COMMAND:
42 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
43 EndDialog(hDlg, LOWORD(wParam));
44 return TRUE;
45 }
46 break;
47 }
48 return FALSE;
49 }
50
51 // Returns true if the process is per monitor DPI aware.
IsProcessPerMonitorDpiAware()52 bool IsProcessPerMonitorDpiAware() {
53 enum class PerMonitorDpiAware {
54 UNKNOWN = 0,
55 PER_MONITOR_DPI_UNAWARE,
56 PER_MONITOR_DPI_AWARE,
57 };
58 static PerMonitorDpiAware per_monitor_dpi_aware = PerMonitorDpiAware::UNKNOWN;
59 if (per_monitor_dpi_aware == PerMonitorDpiAware::UNKNOWN) {
60 per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_UNAWARE;
61 HMODULE shcore_dll = ::LoadLibrary(L"shcore.dll");
62 if (shcore_dll) {
63 typedef HRESULT(WINAPI * GetProcessDpiAwarenessPtr)(
64 HANDLE, PROCESS_DPI_AWARENESS*);
65 GetProcessDpiAwarenessPtr func_ptr =
66 reinterpret_cast<GetProcessDpiAwarenessPtr>(
67 ::GetProcAddress(shcore_dll, "GetProcessDpiAwareness"));
68 if (func_ptr) {
69 PROCESS_DPI_AWARENESS awareness;
70 if (SUCCEEDED(func_ptr(nullptr, &awareness)) &&
71 awareness == PROCESS_PER_MONITOR_DPI_AWARE)
72 per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
73 }
74 }
75 }
76 return per_monitor_dpi_aware == PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
77 }
78
79 // DPI value for 1x scale factor.
80 #define DPI_1X 96.0f
81
GetWindowScaleFactor(HWND hwnd)82 float GetWindowScaleFactor(HWND hwnd) {
83 if (hwnd && IsProcessPerMonitorDpiAware()) {
84 typedef UINT(WINAPI * GetDpiForWindowPtr)(HWND);
85 static GetDpiForWindowPtr func_ptr = reinterpret_cast<GetDpiForWindowPtr>(
86 GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow"));
87 if (func_ptr)
88 return static_cast<float>(func_ptr(hwnd)) / DPI_1X;
89 }
90
91 return client::GetDeviceScaleFactor();
92 }
93
GetButtonWidth(HWND hwnd)94 int GetButtonWidth(HWND hwnd) {
95 return LogicalToDevice(BUTTON_WIDTH, GetWindowScaleFactor(hwnd));
96 }
97
GetURLBarHeight(HWND hwnd)98 int GetURLBarHeight(HWND hwnd) {
99 return LogicalToDevice(URLBAR_HEIGHT, GetWindowScaleFactor(hwnd));
100 }
101
102 } // namespace
103
RootWindowWin()104 RootWindowWin::RootWindowWin()
105 : with_controls_(false),
106 always_on_top_(false),
107 with_osr_(false),
108 with_extension_(false),
109 is_popup_(false),
110 start_rect_(),
111 initialized_(false),
112 hwnd_(nullptr),
113 draggable_region_(nullptr),
114 font_(nullptr),
115 font_height_(0),
116 back_hwnd_(nullptr),
117 forward_hwnd_(nullptr),
118 reload_hwnd_(nullptr),
119 stop_hwnd_(nullptr),
120 edit_hwnd_(nullptr),
121 edit_wndproc_old_(nullptr),
122 find_hwnd_(nullptr),
123 find_message_id_(0),
124 find_wndproc_old_(nullptr),
125 find_state_(),
126 find_next_(false),
127 find_match_case_last_(false),
128 window_destroyed_(false),
129 browser_destroyed_(false),
130 called_enable_non_client_dpi_scaling_(false) {
131 find_buff_[0] = 0;
132
133 // Create a HRGN representing the draggable window area.
134 draggable_region_ = ::CreateRectRgn(0, 0, 0, 0);
135 }
136
~RootWindowWin()137 RootWindowWin::~RootWindowWin() {
138 REQUIRE_MAIN_THREAD();
139
140 ::DeleteObject(draggable_region_);
141 ::DeleteObject(font_);
142
143 // The window and browser should already have been destroyed.
144 DCHECK(window_destroyed_);
145 DCHECK(browser_destroyed_);
146 }
147
Init(RootWindow::Delegate * delegate,std::unique_ptr<RootWindowConfig> config,const CefBrowserSettings & settings)148 void RootWindowWin::Init(RootWindow::Delegate* delegate,
149 std::unique_ptr<RootWindowConfig> config,
150 const CefBrowserSettings& settings) {
151 DCHECK(delegate);
152 DCHECK(!initialized_);
153
154 delegate_ = delegate;
155 with_controls_ = config->with_controls;
156 always_on_top_ = config->always_on_top;
157 with_osr_ = config->with_osr;
158 with_extension_ = config->with_extension;
159
160 start_rect_.left = config->bounds.x;
161 start_rect_.top = config->bounds.y;
162 start_rect_.right = config->bounds.x + config->bounds.width;
163 start_rect_.bottom = config->bounds.y + config->bounds.height;
164
165 CreateBrowserWindow(config->url);
166
167 initialized_ = true;
168
169 // Create the native root window on the main thread.
170 if (CURRENTLY_ON_MAIN_THREAD()) {
171 CreateRootWindow(settings, config->initially_hidden);
172 } else {
173 MAIN_POST_CLOSURE(base::BindOnce(&RootWindowWin::CreateRootWindow, this,
174 settings, config->initially_hidden));
175 }
176 }
177
InitAsPopup(RootWindow::Delegate * delegate,bool with_controls,bool with_osr,const CefPopupFeatures & popupFeatures,CefWindowInfo & windowInfo,CefRefPtr<CefClient> & client,CefBrowserSettings & settings)178 void RootWindowWin::InitAsPopup(RootWindow::Delegate* delegate,
179 bool with_controls,
180 bool with_osr,
181 const CefPopupFeatures& popupFeatures,
182 CefWindowInfo& windowInfo,
183 CefRefPtr<CefClient>& client,
184 CefBrowserSettings& settings) {
185 CEF_REQUIRE_UI_THREAD();
186
187 DCHECK(delegate);
188 DCHECK(!initialized_);
189
190 delegate_ = delegate;
191 with_controls_ = with_controls;
192 with_osr_ = with_osr;
193 is_popup_ = true;
194
195 if (popupFeatures.xSet)
196 start_rect_.left = popupFeatures.x;
197 if (popupFeatures.ySet)
198 start_rect_.top = popupFeatures.y;
199 if (popupFeatures.widthSet)
200 start_rect_.right = start_rect_.left + popupFeatures.width;
201 if (popupFeatures.heightSet)
202 start_rect_.bottom = start_rect_.top + popupFeatures.height;
203
204 CreateBrowserWindow(std::string());
205
206 initialized_ = true;
207
208 // The new popup is initially parented to a temporary window. The native root
209 // window will be created after the browser is created and the popup window
210 // will be re-parented to it at that time.
211 browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo,
212 client, settings);
213 }
214
Show(ShowMode mode)215 void RootWindowWin::Show(ShowMode mode) {
216 REQUIRE_MAIN_THREAD();
217
218 if (!hwnd_)
219 return;
220
221 int nCmdShow = SW_SHOWNORMAL;
222 switch (mode) {
223 case ShowMinimized:
224 nCmdShow = SW_SHOWMINIMIZED;
225 break;
226 case ShowMaximized:
227 nCmdShow = SW_SHOWMAXIMIZED;
228 break;
229 case ShowNoActivate:
230 nCmdShow = SW_SHOWNOACTIVATE;
231 break;
232 default:
233 break;
234 }
235
236 ShowWindow(hwnd_, nCmdShow);
237 UpdateWindow(hwnd_);
238 }
239
Hide()240 void RootWindowWin::Hide() {
241 REQUIRE_MAIN_THREAD();
242
243 if (hwnd_)
244 ShowWindow(hwnd_, SW_HIDE);
245 }
246
SetBounds(int x,int y,size_t width,size_t height)247 void RootWindowWin::SetBounds(int x, int y, size_t width, size_t height) {
248 REQUIRE_MAIN_THREAD();
249
250 if (hwnd_) {
251 SetWindowPos(hwnd_, nullptr, x, y, static_cast<int>(width),
252 static_cast<int>(height), SWP_NOZORDER);
253 }
254 }
255
Close(bool force)256 void RootWindowWin::Close(bool force) {
257 REQUIRE_MAIN_THREAD();
258
259 if (hwnd_) {
260 if (force)
261 DestroyWindow(hwnd_);
262 else
263 PostMessage(hwnd_, WM_CLOSE, 0, 0);
264 }
265 }
266
SetDeviceScaleFactor(float device_scale_factor)267 void RootWindowWin::SetDeviceScaleFactor(float device_scale_factor) {
268 REQUIRE_MAIN_THREAD();
269
270 if (browser_window_ && with_osr_)
271 browser_window_->SetDeviceScaleFactor(device_scale_factor);
272 }
273
GetDeviceScaleFactor() const274 float RootWindowWin::GetDeviceScaleFactor() const {
275 REQUIRE_MAIN_THREAD();
276
277 if (browser_window_ && with_osr_)
278 return browser_window_->GetDeviceScaleFactor();
279
280 NOTREACHED();
281 return 0.0f;
282 }
283
GetBrowser() const284 CefRefPtr<CefBrowser> RootWindowWin::GetBrowser() const {
285 REQUIRE_MAIN_THREAD();
286
287 if (browser_window_)
288 return browser_window_->GetBrowser();
289 return nullptr;
290 }
291
GetWindowHandle() const292 ClientWindowHandle RootWindowWin::GetWindowHandle() const {
293 REQUIRE_MAIN_THREAD();
294 return hwnd_;
295 }
296
WithWindowlessRendering() const297 bool RootWindowWin::WithWindowlessRendering() const {
298 REQUIRE_MAIN_THREAD();
299 return with_osr_;
300 }
301
WithExtension() const302 bool RootWindowWin::WithExtension() const {
303 REQUIRE_MAIN_THREAD();
304 return with_extension_;
305 }
306
CreateBrowserWindow(const std::string & startup_url)307 void RootWindowWin::CreateBrowserWindow(const std::string& startup_url) {
308 if (with_osr_) {
309 OsrRendererSettings settings = {};
310 MainContext::Get()->PopulateOsrSettings(&settings);
311 browser_window_.reset(new BrowserWindowOsrWin(this, startup_url, settings));
312 } else {
313 browser_window_.reset(new BrowserWindowStdWin(this, startup_url));
314 }
315 }
316
CreateRootWindow(const CefBrowserSettings & settings,bool initially_hidden)317 void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
318 bool initially_hidden) {
319 REQUIRE_MAIN_THREAD();
320 DCHECK(!hwnd_);
321
322 HINSTANCE hInstance = GetModuleHandle(nullptr);
323
324 // Load strings from the resource file.
325 const std::wstring& window_title = GetResourceString(IDS_APP_TITLE);
326 const std::wstring& window_class = GetResourceString(IDC_CEFCLIENT);
327
328 const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
329 const HBRUSH background_brush = CreateSolidBrush(
330 RGB(CefColorGetR(background_color), CefColorGetG(background_color),
331 CefColorGetB(background_color)));
332
333 // Register the window class.
334 RegisterRootClass(hInstance, window_class, background_brush);
335
336 // Register the message used with the find dialog.
337 find_message_id_ = RegisterWindowMessage(FINDMSGSTRING);
338 CHECK(find_message_id_);
339
340 CefRefPtr<CefCommandLine> command_line =
341 CefCommandLine::GetGlobalCommandLine();
342 const bool no_activate = command_line->HasSwitch(switches::kNoActivate);
343
344 const DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
345 DWORD dwExStyle = always_on_top_ ? WS_EX_TOPMOST : 0;
346 if (no_activate) {
347 // Don't activate the browser window on creation.
348 dwExStyle |= WS_EX_NOACTIVATE;
349 }
350
351 int x, y, width, height;
352 if (::IsRectEmpty(&start_rect_)) {
353 // Use the default window position/size.
354 x = y = width = height = CW_USEDEFAULT;
355 } else {
356 // Adjust the window size to account for window frame and controls.
357 RECT window_rect = start_rect_;
358 ::AdjustWindowRectEx(&window_rect, dwStyle, with_controls_, dwExStyle);
359
360 x = start_rect_.left;
361 y = start_rect_.top;
362 width = window_rect.right - window_rect.left;
363 height = window_rect.bottom - window_rect.top;
364 }
365
366 browser_settings_ = settings;
367
368 // Create the main window initially hidden.
369 CreateWindowEx(dwExStyle, window_class.c_str(), window_title.c_str(), dwStyle,
370 x, y, width, height, nullptr, nullptr, hInstance, this);
371 CHECK(hwnd_);
372
373 if (!called_enable_non_client_dpi_scaling_ && IsProcessPerMonitorDpiAware()) {
374 // This call gets Windows to scale the non-client area when WM_DPICHANGED
375 // is fired on Windows versions < 10.0.14393.0.
376 // Derived signature; not available in headers.
377 typedef LRESULT(WINAPI * EnableChildWindowDpiMessagePtr)(HWND, BOOL);
378 static EnableChildWindowDpiMessagePtr func_ptr =
379 reinterpret_cast<EnableChildWindowDpiMessagePtr>(GetProcAddress(
380 GetModuleHandle(L"user32.dll"), "EnableChildWindowDpiMessage"));
381 if (func_ptr)
382 func_ptr(hwnd_, TRUE);
383 }
384
385 if (!initially_hidden) {
386 // Show this window.
387 Show(no_activate ? ShowNoActivate : ShowNormal);
388 }
389 }
390
391 // static
RegisterRootClass(HINSTANCE hInstance,const std::wstring & window_class,HBRUSH background_brush)392 void RootWindowWin::RegisterRootClass(HINSTANCE hInstance,
393 const std::wstring& window_class,
394 HBRUSH background_brush) {
395 // Only register the class one time.
396 static bool class_registered = false;
397 if (class_registered)
398 return;
399 class_registered = true;
400
401 WNDCLASSEX wcex;
402
403 wcex.cbSize = sizeof(WNDCLASSEX);
404
405 wcex.style = CS_HREDRAW | CS_VREDRAW;
406 wcex.lpfnWndProc = RootWndProc;
407 wcex.cbClsExtra = 0;
408 wcex.cbWndExtra = 0;
409 wcex.hInstance = hInstance;
410 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CEFCLIENT));
411 wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
412 wcex.hbrBackground = background_brush;
413 wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CEFCLIENT);
414 wcex.lpszClassName = window_class.c_str();
415 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
416
417 RegisterClassEx(&wcex);
418 }
419
420 // static
EditWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)421 LRESULT CALLBACK RootWindowWin::EditWndProc(HWND hWnd,
422 UINT message,
423 WPARAM wParam,
424 LPARAM lParam) {
425 REQUIRE_MAIN_THREAD();
426
427 RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
428 DCHECK(self);
429 DCHECK(hWnd == self->edit_hwnd_);
430
431 switch (message) {
432 case WM_CHAR:
433 if (wParam == VK_RETURN) {
434 // When the user hits the enter key load the URL.
435 CefRefPtr<CefBrowser> browser = self->GetBrowser();
436 if (browser) {
437 wchar_t strPtr[MAX_URL_LENGTH + 1] = {0};
438 *((LPWORD)strPtr) = MAX_URL_LENGTH;
439 LRESULT strLen = SendMessage(hWnd, EM_GETLINE, 0, (LPARAM)strPtr);
440 if (strLen > 0) {
441 strPtr[strLen] = 0;
442 browser->GetMainFrame()->LoadURL(strPtr);
443 }
444 }
445 return 0;
446 }
447 break;
448 case WM_NCDESTROY:
449 // Clear the reference to |self|.
450 SetUserDataPtr(hWnd, nullptr);
451 self->edit_hwnd_ = nullptr;
452 break;
453 }
454
455 return CallWindowProc(self->edit_wndproc_old_, hWnd, message, wParam, lParam);
456 }
457
458 // static
FindWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)459 LRESULT CALLBACK RootWindowWin::FindWndProc(HWND hWnd,
460 UINT message,
461 WPARAM wParam,
462 LPARAM lParam) {
463 REQUIRE_MAIN_THREAD();
464
465 RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
466 DCHECK(self);
467 DCHECK(hWnd == self->find_hwnd_);
468
469 switch (message) {
470 case WM_ACTIVATE:
471 // Set this dialog as current when activated.
472 MainMessageLoop::Get()->SetCurrentModelessDialog(wParam == 0 ? nullptr
473 : hWnd);
474 return FALSE;
475 case WM_NCDESTROY:
476 // Clear the reference to |self|.
477 SetUserDataPtr(hWnd, nullptr);
478 self->find_hwnd_ = nullptr;
479 break;
480 }
481
482 return CallWindowProc(self->find_wndproc_old_, hWnd, message, wParam, lParam);
483 }
484
485 // static
RootWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)486 LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd,
487 UINT message,
488 WPARAM wParam,
489 LPARAM lParam) {
490 REQUIRE_MAIN_THREAD();
491
492 RootWindowWin* self = nullptr;
493 if (message != WM_NCCREATE) {
494 self = GetUserDataPtr<RootWindowWin*>(hWnd);
495 if (!self)
496 return DefWindowProc(hWnd, message, wParam, lParam);
497 DCHECK_EQ(hWnd, self->hwnd_);
498 }
499
500 if (self && message == self->find_message_id_) {
501 // Message targeting the find dialog.
502 LPFINDREPLACE lpfr = reinterpret_cast<LPFINDREPLACE>(lParam);
503 CHECK(lpfr == &self->find_state_);
504 self->OnFindEvent();
505 return 0;
506 }
507
508 // Callback for the main window
509 switch (message) {
510 case WM_COMMAND:
511 if (self->OnCommand(LOWORD(wParam)))
512 return 0;
513 break;
514
515 case WM_GETOBJECT: {
516 // Only the lower 32 bits of lParam are valid when checking the object id
517 // because it sometimes gets sign-extended incorrectly (but not always).
518 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
519
520 // Accessibility readers will send an OBJID_CLIENT message.
521 if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
522 if (self->GetBrowser() && self->GetBrowser()->GetHost())
523 self->GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
524 }
525 } break;
526
527 case WM_PAINT:
528 self->OnPaint();
529 return 0;
530
531 case WM_ACTIVATE:
532 self->OnActivate(LOWORD(wParam) != WA_INACTIVE);
533 // Allow DefWindowProc to set keyboard focus.
534 break;
535
536 case WM_SETFOCUS:
537 self->OnFocus();
538 return 0;
539
540 case WM_SIZE:
541 self->OnSize(wParam == SIZE_MINIMIZED);
542 break;
543
544 case WM_MOVING:
545 case WM_MOVE:
546 self->OnMove();
547 return 0;
548
549 case WM_DPICHANGED:
550 self->OnDpiChanged(wParam, lParam);
551 break;
552
553 case WM_ERASEBKGND:
554 if (self->OnEraseBkgnd())
555 break;
556 // Don't erase the background.
557 return 0;
558
559 case WM_ENTERMENULOOP:
560 if (!wParam) {
561 // Entering the menu loop for the application menu.
562 CefSetOSModalLoop(true);
563 }
564 break;
565
566 case WM_EXITMENULOOP:
567 if (!wParam) {
568 // Exiting the menu loop for the application menu.
569 CefSetOSModalLoop(false);
570 }
571 break;
572
573 case WM_CLOSE:
574 if (self->OnClose())
575 return 0; // Cancel the close.
576 break;
577
578 case WM_NCHITTEST: {
579 LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
580 if (hit == HTCLIENT) {
581 POINTS points = MAKEPOINTS(lParam);
582 POINT point = {points.x, points.y};
583 ::ScreenToClient(hWnd, &point);
584 if (::PtInRegion(self->draggable_region_, point.x, point.y)) {
585 // If cursor is inside a draggable region return HTCAPTION to allow
586 // dragging.
587 return HTCAPTION;
588 }
589 }
590 return hit;
591 }
592
593 case WM_NCCREATE: {
594 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
595 self = reinterpret_cast<RootWindowWin*>(cs->lpCreateParams);
596 DCHECK(self);
597 // Associate |self| with the main window.
598 SetUserDataPtr(hWnd, self);
599 self->hwnd_ = hWnd;
600
601 self->OnNCCreate(cs);
602 } break;
603
604 case WM_CREATE:
605 self->OnCreate(reinterpret_cast<CREATESTRUCT*>(lParam));
606 break;
607
608 case WM_NCDESTROY:
609 // Clear the reference to |self|.
610 SetUserDataPtr(hWnd, nullptr);
611 self->hwnd_ = nullptr;
612 self->OnDestroyed();
613 break;
614 }
615
616 return DefWindowProc(hWnd, message, wParam, lParam);
617 }
618
OnPaint()619 void RootWindowWin::OnPaint() {
620 PAINTSTRUCT ps;
621 BeginPaint(hwnd_, &ps);
622 EndPaint(hwnd_, &ps);
623 }
624
OnFocus()625 void RootWindowWin::OnFocus() {
626 // Selecting "Close window" from the task bar menu may send a focus
627 // notification even though the window is currently disabled (e.g. while a
628 // modal JS dialog is displayed).
629 if (browser_window_ && ::IsWindowEnabled(hwnd_))
630 browser_window_->SetFocus(true);
631 }
632
OnActivate(bool active)633 void RootWindowWin::OnActivate(bool active) {
634 if (active)
635 delegate_->OnRootWindowActivated(this);
636 }
637
OnSize(bool minimized)638 void RootWindowWin::OnSize(bool minimized) {
639 if (minimized) {
640 // Notify the browser window that it was hidden and do nothing further.
641 if (browser_window_)
642 browser_window_->Hide();
643 return;
644 }
645
646 if (browser_window_)
647 browser_window_->Show();
648
649 RECT rect;
650 GetClientRect(hwnd_, &rect);
651
652 if (with_controls_ && edit_hwnd_) {
653 const int button_width = GetButtonWidth(hwnd_);
654 const int urlbar_height = GetURLBarHeight(hwnd_);
655 const int font_height = LogicalToDevice(14, GetWindowScaleFactor(hwnd_));
656
657 if (font_height != font_height_) {
658 font_height_ = font_height;
659 if (font_) {
660 DeleteObject(font_);
661 }
662
663 // Create a scaled font.
664 font_ =
665 ::CreateFont(-font_height, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
666 DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
667 DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
668
669 SendMessage(back_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
670 TRUE);
671 SendMessage(forward_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
672 TRUE);
673 SendMessage(reload_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
674 TRUE);
675 SendMessage(stop_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
676 TRUE);
677 SendMessage(edit_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
678 TRUE);
679 }
680
681 // Resize the window and address bar to match the new frame size.
682 rect.top += urlbar_height;
683
684 int x_offset = rect.left;
685
686 // |browser_hwnd| may be nullptr if the browser has not yet been created.
687 HWND browser_hwnd = nullptr;
688 if (browser_window_)
689 browser_hwnd = browser_window_->GetWindowHandle();
690
691 // Resize all controls.
692 HDWP hdwp = BeginDeferWindowPos(browser_hwnd ? 6 : 5);
693 hdwp = DeferWindowPos(hdwp, back_hwnd_, nullptr, x_offset, 0, button_width,
694 urlbar_height, SWP_NOZORDER);
695 x_offset += button_width;
696 hdwp = DeferWindowPos(hdwp, forward_hwnd_, nullptr, x_offset, 0,
697 button_width, urlbar_height, SWP_NOZORDER);
698 x_offset += button_width;
699 hdwp = DeferWindowPos(hdwp, reload_hwnd_, nullptr, x_offset, 0,
700 button_width, urlbar_height, SWP_NOZORDER);
701 x_offset += button_width;
702 hdwp = DeferWindowPos(hdwp, stop_hwnd_, nullptr, x_offset, 0, button_width,
703 urlbar_height, SWP_NOZORDER);
704 x_offset += button_width;
705 hdwp = DeferWindowPos(hdwp, edit_hwnd_, nullptr, x_offset, 0,
706 rect.right - x_offset, urlbar_height, SWP_NOZORDER);
707
708 if (browser_hwnd) {
709 hdwp = DeferWindowPos(hdwp, browser_hwnd, nullptr, rect.left, rect.top,
710 rect.right - rect.left, rect.bottom - rect.top,
711 SWP_NOZORDER);
712 }
713
714 BOOL result = EndDeferWindowPos(hdwp);
715 ALLOW_UNUSED_LOCAL(result);
716 DCHECK(result);
717 } else if (browser_window_) {
718 // Size the browser window to the whole client area.
719 browser_window_->SetBounds(0, 0, rect.right, rect.bottom);
720 }
721 }
722
OnMove()723 void RootWindowWin::OnMove() {
724 // Notify the browser of move events so that popup windows are displayed
725 // in the correct location and dismissed when the window moves.
726 CefRefPtr<CefBrowser> browser = GetBrowser();
727 if (browser)
728 browser->GetHost()->NotifyMoveOrResizeStarted();
729 }
730
OnDpiChanged(WPARAM wParam,LPARAM lParam)731 void RootWindowWin::OnDpiChanged(WPARAM wParam, LPARAM lParam) {
732 if (LOWORD(wParam) != HIWORD(wParam)) {
733 NOTIMPLEMENTED() << "Received non-square scaling factors";
734 return;
735 }
736
737 if (browser_window_ && with_osr_) {
738 // Scale factor for the new display.
739 const float display_scale_factor =
740 static_cast<float>(LOWORD(wParam)) / DPI_1X;
741 browser_window_->SetDeviceScaleFactor(display_scale_factor);
742 }
743
744 // Suggested size and position of the current window scaled for the new DPI.
745 const RECT* rect = reinterpret_cast<RECT*>(lParam);
746 SetBounds(rect->left, rect->top, rect->right - rect->left,
747 rect->bottom - rect->top);
748 }
749
OnEraseBkgnd()750 bool RootWindowWin::OnEraseBkgnd() {
751 // Erase the background when the browser does not exist.
752 return (GetBrowser() == nullptr);
753 }
754
OnCommand(UINT id)755 bool RootWindowWin::OnCommand(UINT id) {
756 if (id >= ID_TESTS_FIRST && id <= ID_TESTS_LAST) {
757 delegate_->OnTest(this, id);
758 return true;
759 }
760
761 switch (id) {
762 case IDM_ABOUT:
763 OnAbout();
764 return true;
765 case IDM_EXIT:
766 delegate_->OnExit(this);
767 return true;
768 case ID_FIND:
769 OnFind();
770 return true;
771 case IDC_NAV_BACK: // Back button
772 if (CefRefPtr<CefBrowser> browser = GetBrowser())
773 browser->GoBack();
774 return true;
775 case IDC_NAV_FORWARD: // Forward button
776 if (CefRefPtr<CefBrowser> browser = GetBrowser())
777 browser->GoForward();
778 return true;
779 case IDC_NAV_RELOAD: // Reload button
780 if (CefRefPtr<CefBrowser> browser = GetBrowser())
781 browser->Reload();
782 return true;
783 case IDC_NAV_STOP: // Stop button
784 if (CefRefPtr<CefBrowser> browser = GetBrowser())
785 browser->StopLoad();
786 return true;
787 }
788
789 return false;
790 }
791
OnFind()792 void RootWindowWin::OnFind() {
793 if (find_hwnd_) {
794 // Give focus to the existing find dialog.
795 ::SetFocus(find_hwnd_);
796 return;
797 }
798
799 // Configure dialog state.
800 ZeroMemory(&find_state_, sizeof(find_state_));
801 find_state_.lStructSize = sizeof(find_state_);
802 find_state_.hwndOwner = hwnd_;
803 find_state_.lpstrFindWhat = find_buff_;
804 find_state_.wFindWhatLen = sizeof(find_buff_);
805 find_state_.Flags = FR_HIDEWHOLEWORD | FR_DOWN;
806
807 // Create the dialog.
808 find_hwnd_ = FindText(&find_state_);
809
810 // Override the dialog's window procedure.
811 find_wndproc_old_ = SetWndProcPtr(find_hwnd_, FindWndProc);
812
813 // Associate |self| with the dialog.
814 SetUserDataPtr(find_hwnd_, this);
815 }
816
OnFindEvent()817 void RootWindowWin::OnFindEvent() {
818 CefRefPtr<CefBrowser> browser = GetBrowser();
819
820 if (find_state_.Flags & FR_DIALOGTERM) {
821 // The find dialog box has been dismissed so invalidate the handle and
822 // reset the search results.
823 if (browser) {
824 browser->GetHost()->StopFinding(true);
825 find_what_last_.clear();
826 find_next_ = false;
827 }
828 } else if ((find_state_.Flags & FR_FINDNEXT) && browser) {
829 // Search for the requested string.
830 bool match_case = ((find_state_.Flags & FR_MATCHCASE) ? true : false);
831 const std::wstring& find_what = find_buff_;
832 if (match_case != find_match_case_last_ || find_what != find_what_last_) {
833 // The search string has changed, so reset the search results.
834 if (!find_what.empty()) {
835 browser->GetHost()->StopFinding(true);
836 find_next_ = false;
837 }
838 find_match_case_last_ = match_case;
839 find_what_last_ = find_buff_;
840 }
841
842 browser->GetHost()->Find(find_what,
843 (find_state_.Flags & FR_DOWN) ? true : false,
844 match_case, find_next_);
845 if (!find_next_)
846 find_next_ = true;
847 }
848 }
849
OnAbout()850 void RootWindowWin::OnAbout() {
851 // Show the about box.
852 DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd_,
853 AboutWndProc);
854 }
855
OnNCCreate(LPCREATESTRUCT lpCreateStruct)856 void RootWindowWin::OnNCCreate(LPCREATESTRUCT lpCreateStruct) {
857 if (IsProcessPerMonitorDpiAware()) {
858 // This call gets Windows to scale the non-client area when WM_DPICHANGED
859 // is fired on Windows versions >= 10.0.14393.0.
860 typedef BOOL(WINAPI * EnableNonClientDpiScalingPtr)(HWND);
861 static EnableNonClientDpiScalingPtr func_ptr =
862 reinterpret_cast<EnableNonClientDpiScalingPtr>(GetProcAddress(
863 GetModuleHandle(L"user32.dll"), "EnableNonClientDpiScaling"));
864 called_enable_non_client_dpi_scaling_ = !!(func_ptr && func_ptr(hwnd_));
865 }
866 }
867
OnCreate(LPCREATESTRUCT lpCreateStruct)868 void RootWindowWin::OnCreate(LPCREATESTRUCT lpCreateStruct) {
869 const HINSTANCE hInstance = lpCreateStruct->hInstance;
870
871 RECT rect;
872 GetClientRect(hwnd_, &rect);
873
874 if (with_controls_) {
875 // Create the child controls.
876 int x_offset = 0;
877
878 const int button_width = GetButtonWidth(hwnd_);
879 const int urlbar_height = GetURLBarHeight(hwnd_);
880
881 back_hwnd_ = CreateWindow(
882 L"BUTTON", L"Back", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
883 x_offset, 0, button_width, urlbar_height, hwnd_,
884 reinterpret_cast<HMENU>(IDC_NAV_BACK), hInstance, 0);
885 CHECK(back_hwnd_);
886 x_offset += button_width;
887
888 forward_hwnd_ =
889 CreateWindow(L"BUTTON", L"Forward",
890 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
891 x_offset, 0, button_width, urlbar_height, hwnd_,
892 reinterpret_cast<HMENU>(IDC_NAV_FORWARD), hInstance, 0);
893 CHECK(forward_hwnd_);
894 x_offset += button_width;
895
896 reload_hwnd_ =
897 CreateWindow(L"BUTTON", L"Reload",
898 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
899 x_offset, 0, button_width, urlbar_height, hwnd_,
900 reinterpret_cast<HMENU>(IDC_NAV_RELOAD), hInstance, 0);
901 CHECK(reload_hwnd_);
902 x_offset += button_width;
903
904 stop_hwnd_ = CreateWindow(
905 L"BUTTON", L"Stop", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
906 x_offset, 0, button_width, urlbar_height, hwnd_,
907 reinterpret_cast<HMENU>(IDC_NAV_STOP), hInstance, 0);
908 CHECK(stop_hwnd_);
909 x_offset += button_width;
910
911 edit_hwnd_ = CreateWindow(L"EDIT", 0,
912 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
913 ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_DISABLED,
914 x_offset, 0, rect.right - button_width * 4,
915 urlbar_height, hwnd_, 0, hInstance, 0);
916 CHECK(edit_hwnd_);
917
918 // Override the edit control's window procedure.
919 edit_wndproc_old_ = SetWndProcPtr(edit_hwnd_, EditWndProc);
920
921 // Associate |this| with the edit window.
922 SetUserDataPtr(edit_hwnd_, this);
923
924 rect.top += urlbar_height;
925
926 if (!with_osr_) {
927 // Remove the menu items that are only used with OSR.
928 HMENU hMenu = ::GetMenu(hwnd_);
929 if (hMenu) {
930 HMENU hTestMenu = ::GetSubMenu(hMenu, 2);
931 if (hTestMenu) {
932 ::RemoveMenu(hTestMenu, ID_TESTS_OSR_FPS, MF_BYCOMMAND);
933 ::RemoveMenu(hTestMenu, ID_TESTS_OSR_DSF, MF_BYCOMMAND);
934 }
935 }
936 }
937 } else {
938 // No controls so also remove the default menu.
939 ::SetMenu(hwnd_, nullptr);
940 }
941
942 const float device_scale_factor = GetWindowScaleFactor(hwnd_);
943
944 if (with_osr_) {
945 browser_window_->SetDeviceScaleFactor(device_scale_factor);
946 }
947
948 if (!is_popup_) {
949 // Create the browser window.
950 CefRect cef_rect(rect.left, rect.top, rect.right - rect.left,
951 rect.bottom - rect.top);
952 browser_window_->CreateBrowser(hwnd_, cef_rect, browser_settings_, nullptr,
953 delegate_->GetRequestContext(this));
954 } else {
955 // With popups we already have a browser window. Parent the browser window
956 // to the root window and show it in the correct location.
957 browser_window_->ShowPopup(hwnd_, rect.left, rect.top,
958 rect.right - rect.left, rect.bottom - rect.top);
959 }
960 }
961
OnClose()962 bool RootWindowWin::OnClose() {
963 if (browser_window_ && !browser_window_->IsClosing()) {
964 CefRefPtr<CefBrowser> browser = GetBrowser();
965 if (browser) {
966 // Notify the browser window that we would like to close it. This
967 // will result in a call to ClientHandler::DoClose() if the
968 // JavaScript 'onbeforeunload' event handler allows it.
969 browser->GetHost()->CloseBrowser(false);
970
971 // Cancel the close.
972 return true;
973 }
974 }
975
976 // Allow the close.
977 return false;
978 }
979
OnDestroyed()980 void RootWindowWin::OnDestroyed() {
981 window_destroyed_ = true;
982 NotifyDestroyedIfDone();
983 }
984
OnBrowserCreated(CefRefPtr<CefBrowser> browser)985 void RootWindowWin::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
986 REQUIRE_MAIN_THREAD();
987
988 if (is_popup_) {
989 // For popup browsers create the root window once the browser has been
990 // created.
991 CreateRootWindow(CefBrowserSettings(), false);
992 } else {
993 // Make sure the browser is sized correctly.
994 OnSize(false);
995 }
996
997 delegate_->OnBrowserCreated(this, browser);
998 }
999
OnBrowserWindowDestroyed()1000 void RootWindowWin::OnBrowserWindowDestroyed() {
1001 REQUIRE_MAIN_THREAD();
1002
1003 browser_window_.reset();
1004
1005 if (!window_destroyed_) {
1006 // The browser was destroyed first. This could be due to the use of
1007 // off-screen rendering or execution of JavaScript window.close().
1008 // Close the RootWindow.
1009 Close(true);
1010 }
1011
1012 browser_destroyed_ = true;
1013 NotifyDestroyedIfDone();
1014 }
1015
OnSetAddress(const std::string & url)1016 void RootWindowWin::OnSetAddress(const std::string& url) {
1017 REQUIRE_MAIN_THREAD();
1018
1019 if (edit_hwnd_)
1020 SetWindowText(edit_hwnd_, CefString(url).ToWString().c_str());
1021 }
1022
OnSetTitle(const std::string & title)1023 void RootWindowWin::OnSetTitle(const std::string& title) {
1024 REQUIRE_MAIN_THREAD();
1025
1026 if (hwnd_)
1027 SetWindowText(hwnd_, CefString(title).ToWString().c_str());
1028 }
1029
OnSetFullscreen(bool fullscreen)1030 void RootWindowWin::OnSetFullscreen(bool fullscreen) {
1031 REQUIRE_MAIN_THREAD();
1032
1033 CefRefPtr<CefBrowser> browser = GetBrowser();
1034 if (browser) {
1035 std::unique_ptr<window_test::WindowTestRunnerWin> test_runner(
1036 new window_test::WindowTestRunnerWin());
1037 if (fullscreen)
1038 test_runner->Maximize(browser);
1039 else
1040 test_runner->Restore(browser);
1041 }
1042 }
1043
OnAutoResize(const CefSize & new_size)1044 void RootWindowWin::OnAutoResize(const CefSize& new_size) {
1045 REQUIRE_MAIN_THREAD();
1046
1047 if (!hwnd_)
1048 return;
1049
1050 int new_width = new_size.width;
1051
1052 // Make the window wide enough to drag by the top menu bar.
1053 if (new_width < 200)
1054 new_width = 200;
1055
1056 const float device_scale_factor = GetWindowScaleFactor(hwnd_);
1057 RECT rect = {0, 0, LogicalToDevice(new_width, device_scale_factor),
1058 LogicalToDevice(new_size.height, device_scale_factor)};
1059 DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
1060 DWORD ex_style = GetWindowLong(hwnd_, GWL_EXSTYLE);
1061 bool has_menu = !(style & WS_CHILD) && (GetMenu(hwnd_) != nullptr);
1062
1063 // The size value is for the client area. Calculate the whole window size
1064 // based on the current style.
1065 AdjustWindowRectEx(&rect, style, has_menu, ex_style);
1066
1067 // Size the window. The left/top values may be negative.
1068 // Also show the window if it's not currently visible.
1069 SetWindowPos(hwnd_, nullptr, 0, 0, rect.right - rect.left,
1070 rect.bottom - rect.top,
1071 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1072 }
1073
OnSetLoadingState(bool isLoading,bool canGoBack,bool canGoForward)1074 void RootWindowWin::OnSetLoadingState(bool isLoading,
1075 bool canGoBack,
1076 bool canGoForward) {
1077 REQUIRE_MAIN_THREAD();
1078
1079 if (with_controls_) {
1080 EnableWindow(back_hwnd_, canGoBack);
1081 EnableWindow(forward_hwnd_, canGoForward);
1082 EnableWindow(reload_hwnd_, !isLoading);
1083 EnableWindow(stop_hwnd_, isLoading);
1084 EnableWindow(edit_hwnd_, TRUE);
1085 }
1086
1087 if (!isLoading && GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
1088 // Done with the initial navigation. Remove the WS_EX_NOACTIVATE style so
1089 // that future mouse clicks inside the browser correctly activate and focus
1090 // the window. For the top-level window removing this style causes Windows
1091 // to display the task bar button.
1092 SetWindowLongPtr(hwnd_, GWL_EXSTYLE,
1093 GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
1094
1095 if (browser_window_) {
1096 HWND browser_hwnd = browser_window_->GetWindowHandle();
1097 SetWindowLongPtr(
1098 browser_hwnd, GWL_EXSTYLE,
1099 GetWindowLongPtr(browser_hwnd, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
1100 }
1101 }
1102 }
1103
1104 namespace {
1105
1106 LPCWSTR kParentWndProc = L"CefParentWndProc";
1107 LPCWSTR kDraggableRegion = L"CefDraggableRegion";
1108
SubclassedWindowProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)1109 LRESULT CALLBACK SubclassedWindowProc(HWND hWnd,
1110 UINT message,
1111 WPARAM wParam,
1112 LPARAM lParam) {
1113 WNDPROC hParentWndProc =
1114 reinterpret_cast<WNDPROC>(::GetPropW(hWnd, kParentWndProc));
1115 HRGN hRegion = reinterpret_cast<HRGN>(::GetPropW(hWnd, kDraggableRegion));
1116
1117 if (message == WM_NCHITTEST) {
1118 LRESULT hit = CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
1119 if (hit == HTCLIENT) {
1120 POINTS points = MAKEPOINTS(lParam);
1121 POINT point = {points.x, points.y};
1122 ::ScreenToClient(hWnd, &point);
1123 if (::PtInRegion(hRegion, point.x, point.y)) {
1124 // Let the parent window handle WM_NCHITTEST by returning HTTRANSPARENT
1125 // in child windows.
1126 return HTTRANSPARENT;
1127 }
1128 }
1129 return hit;
1130 }
1131
1132 return CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
1133 }
1134
SubclassWindow(HWND hWnd,HRGN hRegion)1135 void SubclassWindow(HWND hWnd, HRGN hRegion) {
1136 HANDLE hParentWndProc = ::GetPropW(hWnd, kParentWndProc);
1137 if (hParentWndProc) {
1138 return;
1139 }
1140
1141 SetLastError(0);
1142 LONG_PTR hOldWndProc = SetWindowLongPtr(
1143 hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
1144 if (hOldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
1145 return;
1146 }
1147
1148 ::SetPropW(hWnd, kParentWndProc, reinterpret_cast<HANDLE>(hOldWndProc));
1149 ::SetPropW(hWnd, kDraggableRegion, reinterpret_cast<HANDLE>(hRegion));
1150 }
1151
UnSubclassWindow(HWND hWnd)1152 void UnSubclassWindow(HWND hWnd) {
1153 LONG_PTR hParentWndProc =
1154 reinterpret_cast<LONG_PTR>(::GetPropW(hWnd, kParentWndProc));
1155 if (hParentWndProc) {
1156 LONG_PTR hPreviousWndProc =
1157 SetWindowLongPtr(hWnd, GWLP_WNDPROC, hParentWndProc);
1158 ALLOW_UNUSED_LOCAL(hPreviousWndProc);
1159 DCHECK_EQ(hPreviousWndProc,
1160 reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
1161 }
1162
1163 ::RemovePropW(hWnd, kParentWndProc);
1164 ::RemovePropW(hWnd, kDraggableRegion);
1165 }
1166
SubclassWindowsProc(HWND hwnd,LPARAM lParam)1167 BOOL CALLBACK SubclassWindowsProc(HWND hwnd, LPARAM lParam) {
1168 SubclassWindow(hwnd, reinterpret_cast<HRGN>(lParam));
1169 return TRUE;
1170 }
1171
UnSubclassWindowsProc(HWND hwnd,LPARAM lParam)1172 BOOL CALLBACK UnSubclassWindowsProc(HWND hwnd, LPARAM lParam) {
1173 UnSubclassWindow(hwnd);
1174 return TRUE;
1175 }
1176
1177 } // namespace
1178
OnSetDraggableRegions(const std::vector<CefDraggableRegion> & regions)1179 void RootWindowWin::OnSetDraggableRegions(
1180 const std::vector<CefDraggableRegion>& regions) {
1181 REQUIRE_MAIN_THREAD();
1182
1183 // Reset draggable region.
1184 ::SetRectRgn(draggable_region_, 0, 0, 0, 0);
1185
1186 // Determine new draggable region.
1187 std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
1188 for (; it != regions.end(); ++it) {
1189 HRGN region = ::CreateRectRgn(it->bounds.x, it->bounds.y,
1190 it->bounds.x + it->bounds.width,
1191 it->bounds.y + it->bounds.height);
1192 ::CombineRgn(draggable_region_, draggable_region_, region,
1193 it->draggable ? RGN_OR : RGN_DIFF);
1194 ::DeleteObject(region);
1195 }
1196
1197 // Subclass child window procedures in order to do hit-testing.
1198 // This will be a no-op, if it is already subclassed.
1199 if (hwnd_) {
1200 WNDENUMPROC proc =
1201 !regions.empty() ? SubclassWindowsProc : UnSubclassWindowsProc;
1202 ::EnumChildWindows(hwnd_, proc,
1203 reinterpret_cast<LPARAM>(draggable_region_));
1204 }
1205 }
1206
NotifyDestroyedIfDone()1207 void RootWindowWin::NotifyDestroyedIfDone() {
1208 // Notify once both the window and the browser have been destroyed.
1209 if (window_destroyed_ && browser_destroyed_)
1210 delegate_->OnRootWindowDestroyed(this);
1211 }
1212
1213 } // namespace client
1214