From b8eaec0db2497939b3eca3e186d259cfa9a77ed7 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Thu, 7 Feb 2019 16:36:31 -0500 Subject: [PATCH] Windows: Use WS_EX_NOACTIVATE to control initial window activation (issue #1856) --- .../browser_platform_delegate_native_win.cc | 19 ++++++++++++ .../browser/browser_window_osr_win.cc | 3 ++ .../browser/browser_window_std_win.cc | 16 ++++++++-- tests/cefclient/browser/client_handler.cc | 23 +++++++++++++- tests/cefclient/browser/client_handler.h | 4 +++ tests/cefclient/browser/osr_window_win.cc | 15 +++++++-- tests/cefclient/browser/root_window.h | 1 + tests/cefclient/browser/root_window_win.cc | 31 +++++++++++++++++-- tests/shared/common/client_switches.cc | 1 + tests/shared/common/client_switches.h | 1 + 10 files changed, 107 insertions(+), 7 deletions(-) diff --git a/libcef/browser/native/browser_platform_delegate_native_win.cc b/libcef/browser/native/browser_platform_delegate_native_win.cc index c79fd9705..d9a322c13 100644 --- a/libcef/browser/native/browser_platform_delegate_native_win.cc +++ b/libcef/browser/native/browser_platform_delegate_native_win.cc @@ -207,8 +207,27 @@ bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() { gfx::Rect(0, 0, point.x(), point.y())); window_widget_ = delegate_view->GetWidget(); + + const HWND widget_hwnd = HWNDForWidget(window_widget_); + DCHECK(widget_hwnd); + const DWORD widget_ex_styles = GetWindowLongPtr(widget_hwnd, GWL_EXSTYLE); + + if (window_info_.ex_style & WS_EX_NOACTIVATE) { + // Add the WS_EX_NOACTIVATE style on the DesktopWindowTreeHostWin HWND + // so that HWNDMessageHandler::Show() called via Widget::Show() does not + // activate the window. + SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE, + widget_ex_styles | WS_EX_NOACTIVATE); + } + window_widget_->Show(); + if (window_info_.ex_style & WS_EX_NOACTIVATE) { + // Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside the + // browser correctly activate and focus the window. + SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE, widget_ex_styles); + } + return true; } diff --git a/tests/cefclient/browser/browser_window_osr_win.cc b/tests/cefclient/browser/browser_window_osr_win.cc index 445732913..796d932ef 100644 --- a/tests/cefclient/browser/browser_window_osr_win.cc +++ b/tests/cefclient/browser/browser_window_osr_win.cc @@ -41,6 +41,9 @@ void BrowserWindowOsrWin::GetPopupConfig(CefWindowHandle temp_handle, windowInfo.external_begin_frame_enabled = osr_window_->settings().external_begin_frame_enabled; + // Don't activate the hidden browser on creation. + windowInfo.ex_style |= WS_EX_NOACTIVATE; + client = client_handler_; } diff --git a/tests/cefclient/browser/browser_window_std_win.cc b/tests/cefclient/browser/browser_window_std_win.cc index 9956eacd9..fa7ee028c 100644 --- a/tests/cefclient/browser/browser_window_std_win.cc +++ b/tests/cefclient/browser/browser_window_std_win.cc @@ -26,6 +26,11 @@ void BrowserWindowStdWin::CreateBrowser( RECT wnd_rect = {rect.x, rect.y, rect.x + rect.width, rect.y + rect.height}; window_info.SetAsChild(parent_handle, wnd_rect); + if (GetWindowLongPtr(parent_handle, GWL_EXSTYLE) & WS_EX_NOACTIVATE) { + // Don't activate the browser window on creation. + window_info.ex_style |= WS_EX_NOACTIVATE; + } + CefBrowserHost::CreateBrowser(window_info, client_handler_, client_handler_->startup_url(), settings, request_context); @@ -39,6 +44,10 @@ void BrowserWindowStdWin::GetPopupConfig(CefWindowHandle temp_handle, // The window will be properly sized after the browser is created. windowInfo.SetAsChild(temp_handle, RECT()); + + // Don't activate the hidden browser window on creation. + windowInfo.ex_style |= WS_EX_NOACTIVATE; + client = client_handler_; } @@ -53,8 +62,11 @@ void BrowserWindowStdWin::ShowPopup(ClientWindowHandle parent_handle, if (hwnd) { SetParent(hwnd, parent_handle); SetWindowPos(hwnd, NULL, x, y, static_cast(width), - static_cast(height), SWP_NOZORDER); - ShowWindow(hwnd, SW_SHOW); + static_cast(height), SWP_NOZORDER | SWP_NOACTIVATE); + + const bool no_activate = + GetWindowLongPtr(parent_handle, GWL_EXSTYLE) & WS_EX_NOACTIVATE; + ShowWindow(hwnd, no_activate ? SW_SHOWNOACTIVATE : SW_SHOW); } } diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index 39e40882b..224953abb 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -249,7 +249,8 @@ ClientHandler::ClientHandler(Delegate* delegate, browser_count_(0), console_log_file_(MainContext::Get()->GetConsoleLogPath()), first_console_message_(true), - focus_on_editable_field_(false) { + focus_on_editable_field_(false), + initial_navigation_(true) { DCHECK(!console_log_file_.empty()); #if defined(OS_LINUX) @@ -497,6 +498,22 @@ void ClientHandler::OnTakeFocus(CefRefPtr browser, bool next) { NotifyTakeFocus(next); } +bool ClientHandler::OnSetFocus(CefRefPtr browser, + FocusSource source) { + CEF_REQUIRE_UI_THREAD(); + + if (initial_navigation_) { + CefRefPtr command_line = + CefCommandLine::GetGlobalCommandLine(); + if (command_line->HasSwitch(switches::kNoActivate)) { + // Don't give focus to the browser on creation. + return true; + } + } + + return false; +} + bool ClientHandler::OnPreKeyEvent(CefRefPtr browser, const CefKeyEvent& event, CefEventHandle os_event, @@ -606,6 +623,10 @@ void ClientHandler::OnLoadingStateChange(CefRefPtr browser, bool canGoForward) { CEF_REQUIRE_UI_THREAD(); + if (!isLoading && initial_navigation_) { + initial_navigation_ = false; + } + NotifyLoadingState(isLoading, canGoBack, canGoForward); } diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index d00973f2d..1042faefb 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -169,6 +169,7 @@ class ClientHandler : public CefClient, // CefFocusHandler methods void OnTakeFocus(CefRefPtr browser, bool next) OVERRIDE; + bool OnSetFocus(CefRefPtr browser, FocusSource source) OVERRIDE; // CefKeyboardHandler methods bool OnPreKeyEvent(CefRefPtr browser, @@ -370,6 +371,9 @@ class ClientHandler : public CefClient, // True if an editable field currently has focus. bool focus_on_editable_field_; + // True for the initial navigation after browser creation. + bool initial_navigation_; + // Set of Handlers registered with the message router. MessageHandlerSet message_handler_set_; diff --git a/tests/cefclient/browser/osr_window_win.cc b/tests/cefclient/browser/osr_window_win.cc index 7947828dc..7ed4e3b74 100644 --- a/tests/cefclient/browser/osr_window_win.cc +++ b/tests/cefclient/browser/osr_window_win.cc @@ -76,6 +76,11 @@ void OsrWindowWin::CreateBrowser(HWND parent_hwnd, CefWindowInfo window_info; window_info.SetAsWindowless(hwnd_); + if (GetWindowLongPtr(parent_hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE) { + // Don't activate the browser window on creation. + window_info.ex_style |= WS_EX_NOACTIVATE; + } + window_info.shared_texture_enabled = settings_.shared_texture_enabled; window_info.external_begin_frame_enabled = settings_.external_begin_frame_enabled; @@ -220,10 +225,16 @@ void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) { RegisterOsrClass(hInst, background_brush); + DWORD ex_style = 0; + if (GetWindowLongPtr(parent_hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE) { + // Don't activate the browser window on creation. + ex_style |= WS_EX_NOACTIVATE; + } + // Create the native window with a border so it's easier to visually identify // OSR windows. - hwnd_ = ::CreateWindow( - kWndClass, 0, + hwnd_ = ::CreateWindowEx( + ex_style, kWndClass, 0, WS_BORDER | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, parent_hwnd, 0, hInst, 0); diff --git a/tests/cefclient/browser/root_window.h b/tests/cefclient/browser/root_window.h index 7d1dfff6a..584449a46 100644 --- a/tests/cefclient/browser/root_window.h +++ b/tests/cefclient/browser/root_window.h @@ -150,6 +150,7 @@ class RootWindow ShowNormal, ShowMinimized, ShowMaximized, + ShowNoActivate, }; // Show the window. diff --git a/tests/cefclient/browser/root_window_win.cc b/tests/cefclient/browser/root_window_win.cc index 667a789ff..f6821b0ca 100644 --- a/tests/cefclient/browser/root_window_win.cc +++ b/tests/cefclient/browser/root_window_win.cc @@ -226,6 +226,9 @@ void RootWindowWin::Show(ShowMode mode) { case ShowMaximized: nCmdShow = SW_SHOWMAXIMIZED; break; + case ShowNoActivate: + nCmdShow = SW_SHOWNOACTIVATE; + break; default: break; } @@ -334,8 +337,16 @@ void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings, find_message_id_ = RegisterWindowMessage(FINDMSGSTRING); CHECK(find_message_id_); + CefRefPtr command_line = + CefCommandLine::GetGlobalCommandLine(); + const bool no_activate = command_line->HasSwitch(switches::kNoActivate); + const DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; - const DWORD dwExStyle = always_on_top_ ? WS_EX_TOPMOST : 0; + DWORD dwExStyle = always_on_top_ ? WS_EX_TOPMOST : 0; + if (no_activate) { + // Don't activate the browser window on creation. + dwExStyle |= WS_EX_NOACTIVATE; + } int x, y, width, height; if (::IsRectEmpty(&start_rect_)) { @@ -373,7 +384,7 @@ void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings, if (!initially_hidden) { // Show this window. - Show(ShowNormal); + Show(no_activate ? ShowNoActivate : ShowNormal); } } @@ -1069,6 +1080,22 @@ void RootWindowWin::OnSetLoadingState(bool isLoading, EnableWindow(stop_hwnd_, isLoading); EnableWindow(edit_hwnd_, TRUE); } + + if (!isLoading && GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & WS_EX_NOACTIVATE) { + // Done with the initial navigation. Remove the WS_EX_NOACTIVATE style so + // that future mouse clicks inside the browser correctly activate and focus + // the window. For the top-level window removing this style causes Windows + // to display the task bar button. + SetWindowLongPtr(hwnd_, GWL_EXSTYLE, + GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE); + + if (browser_window_) { + HWND browser_hwnd = browser_window_->GetWindowHandle(); + SetWindowLongPtr( + browser_hwnd, GWL_EXSTYLE, + GetWindowLongPtr(browser_hwnd, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE); + } + } } namespace { diff --git a/tests/shared/common/client_switches.cc b/tests/shared/common/client_switches.cc index 545d046ba..bf6e1ae0b 100644 --- a/tests/shared/common/client_switches.cc +++ b/tests/shared/common/client_switches.cc @@ -43,6 +43,7 @@ const char kWidevineCdmPath[] = "widevine-cdm-path"; const char kSslClientCertificate[] = "ssl-client-certificate"; const char kCRLSetsPath[] = "crl-sets-path"; const char kLoadExtension[] = "load-extension"; +const char kNoActivate[] = "no-activate"; } // namespace switches } // namespace client diff --git a/tests/shared/common/client_switches.h b/tests/shared/common/client_switches.h index d653f4445..ef6bb4aa8 100644 --- a/tests/shared/common/client_switches.h +++ b/tests/shared/common/client_switches.h @@ -37,6 +37,7 @@ extern const char kWidevineCdmPath[]; extern const char kSslClientCertificate[]; extern const char kCRLSetsPath[]; extern const char kLoadExtension[]; +extern const char kNoActivate[]; } // namespace switches } // namespace client