Windows: Fix hidden dropdowns when the window is topmost (issue #1468)

Also add a --always-on-top flag to cefclient to allow easier testing of this
behavior on Windows and Linux.
This commit is contained in:
Isaac Devine 2018-02-28 17:47:36 +13:00 committed by Marshall Greenblatt
parent 1928572b52
commit c64898f9fc
19 changed files with 85 additions and 9 deletions

View File

@ -81,8 +81,8 @@ bool CefBrowserPlatformDelegateNativeLinux::CreateHostWindow() {
// Add a reference that will be released in BrowserDestroyed(). // Add a reference that will be released in BrowserDestroyed().
browser_->AddRef(); browser_->AddRef();
CefWindowDelegateView* delegate_view = CefWindowDelegateView* delegate_view = new CefWindowDelegateView(
new CefWindowDelegateView(GetBackgroundColor()); GetBackgroundColor(), window_x11_->TopLevelAlwaysOnTop());
delegate_view->Init(window_info_.window, browser_->web_contents(), delegate_view->Init(window_info_.window, browser_->web_contents(),
gfx::Rect(gfx::Point(), rect.size())); gfx::Rect(gfx::Point(), rect.size()));

View File

@ -194,8 +194,15 @@ bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() {
point = point =
gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(point), 1.0f / scale)); gfx::ToFlooredPoint(gfx::ScalePoint(gfx::PointF(point), 1.0f / scale));
// Stay on top if top-most window hosting the web view is topmost.
HWND top_level_window = GetAncestor(window_info_.window, GA_ROOT);
DWORD top_level_window_ex_styles =
GetWindowLongPtr(top_level_window, GWL_EXSTYLE);
bool always_on_top =
(top_level_window_ex_styles & WS_EX_TOPMOST) == WS_EX_TOPMOST;
CefWindowDelegateView* delegate_view = CefWindowDelegateView* delegate_view =
new CefWindowDelegateView(GetBackgroundColor()); new CefWindowDelegateView(GetBackgroundColor(), always_on_top);
delegate_view->Init(window_info_.window, browser_->web_contents(), delegate_view->Init(window_info_.window, browser_->web_contents(),
gfx::Rect(0, 0, point.x(), point.y())); gfx::Rect(0, 0, point.x(), point.y()));

View File

@ -12,8 +12,11 @@
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
CefWindowDelegateView::CefWindowDelegateView(SkColor background_color) CefWindowDelegateView::CefWindowDelegateView(SkColor background_color,
: background_color_(background_color), web_view_(NULL) {} bool always_on_top)
: background_color_(background_color),
web_view_(NULL),
always_on_top_(always_on_top) {}
void CefWindowDelegateView::Init(gfx::AcceleratedWidget parent_widget, void CefWindowDelegateView::Init(gfx::AcceleratedWidget parent_widget,
content::WebContents* web_contents, content::WebContents* web_contents,
@ -44,6 +47,8 @@ void CefWindowDelegateView::Init(gfx::AcceleratedWidget parent_widget,
// CefBrowserHostImpl::PlatformSetFocus. // CefBrowserHostImpl::PlatformSetFocus.
params.activatable = views::Widget::InitParams::ACTIVATABLE_YES; params.activatable = views::Widget::InitParams::ACTIVATABLE_YES;
params.keep_on_top = always_on_top_;
// Results in a call to InitContent(). // Results in a call to InitContent().
widget->Init(params); widget->Init(params);

View File

@ -20,7 +20,7 @@ class WebView;
// will be deleted automatically when the associated root window is destroyed. // will be deleted automatically when the associated root window is destroyed.
class CefWindowDelegateView : public views::WidgetDelegateView { class CefWindowDelegateView : public views::WidgetDelegateView {
public: public:
explicit CefWindowDelegateView(SkColor background_color); CefWindowDelegateView(SkColor background_color, bool always_on_top);
// Create the Widget and associated root window. // Create the Widget and associated root window.
void Init(gfx::AcceleratedWidget parent_widget, void Init(gfx::AcceleratedWidget parent_widget,
@ -43,6 +43,7 @@ class CefWindowDelegateView : public views::WidgetDelegateView {
private: private:
SkColor background_color_; SkColor background_color_;
views::WebView* web_view_; views::WebView* web_view_;
bool always_on_top_;
DISALLOW_COPY_AND_ASSIGN(CefWindowDelegateView); DISALLOW_COPY_AND_ASSIGN(CefWindowDelegateView);
}; };

View File

@ -396,3 +396,34 @@ void CefWindowX11::ContinueFocus() {
browser_->SetFocus(true); browser_->SetFocus(true);
focus_pending_ = false; focus_pending_ = false;
} }
bool CefWindowX11::TopLevelAlwaysOnTop() const {
::Window toplevel_window = FindToplevelParent(xdisplay_, xwindow_);
Atom state_atom = gfx::GetAtom("_NET_WM_STATE");
Atom state_keep_above = gfx::GetAtom("_NET_WM_STATE_KEEP_ABOVE");
Atom* states;
Atom actual_type;
int actual_format;
unsigned long num_items;
unsigned long bytes_after;
XGetWindowProperty(xdisplay_, toplevel_window, state_atom, 0, 1024,
x11::False, XA_ATOM, &actual_type, &actual_format,
&num_items, &bytes_after,
reinterpret_cast<unsigned char**>(&states));
bool always_on_top = false;
for (unsigned long i = 0; i < num_items; ++i) {
if (states[i] == state_keep_above) {
always_on_top = true;
break;
}
}
XFree(states);
return always_on_top;
}

View File

@ -52,6 +52,8 @@ class CefWindowX11 : public ui::PlatformEventDispatcher {
::Window xwindow() const { return xwindow_; } ::Window xwindow() const { return xwindow_; }
gfx::Rect bounds() const { return bounds_; } gfx::Rect bounds() const { return bounds_; }
bool TopLevelAlwaysOnTop() const;
private: private:
void ContinueFocus(); void ContinueFocus();

View File

@ -23,6 +23,9 @@ namespace client {
struct RootWindowConfig { struct RootWindowConfig {
RootWindowConfig(); RootWindowConfig();
// If true the window will always display above other windows.
bool always_on_top;
// If true the window will show controls. // If true the window will show controls.
bool with_controls; bool with_controls;

View File

@ -51,6 +51,7 @@ void MaximizeWindow(GtkWindow* window) {
RootWindowGtk::RootWindowGtk() RootWindowGtk::RootWindowGtk()
: with_controls_(false), : with_controls_(false),
always_on_top_(false),
with_osr_(false), with_osr_(false),
with_extension_(false), with_extension_(false),
is_popup_(false), is_popup_(false),
@ -84,6 +85,7 @@ void RootWindowGtk::Init(RootWindow::Delegate* delegate,
delegate_ = delegate; delegate_ = delegate;
with_controls_ = config.with_controls; with_controls_ = config.with_controls;
always_on_top_ = config.always_on_top;
with_osr_ = config.with_osr; with_osr_ = config.with_osr;
with_extension_ = config.with_extension; with_extension_ = config.with_extension;
start_rect_ = config.bounds; start_rect_ = config.bounds;
@ -275,6 +277,10 @@ void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
CHECK(window_); CHECK(window_);
if (always_on_top_) {
gtk_window_set_keep_above(GTK_WINDOW(window_), TRUE);
}
gtk_window_set_default_size(GTK_WINDOW(window_), width, height); gtk_window_set_default_size(GTK_WINDOW(window_), width, height);
g_signal_connect(G_OBJECT(window_), "focus-in-event", g_signal_connect(G_OBJECT(window_), "focus-in-event",
G_CALLBACK(&RootWindowGtk::WindowFocusIn), this); G_CALLBACK(&RootWindowGtk::WindowFocusIn), this);

View File

@ -125,6 +125,7 @@ class RootWindowGtk : public RootWindow, public BrowserWindow::Delegate {
// After initialization all members are only accessed on the main thread. // After initialization all members are only accessed on the main thread.
// Members set during initialization. // Members set during initialization.
bool with_controls_; bool with_controls_;
bool always_on_top_;
bool with_osr_; bool with_osr_;
bool with_extension_; bool with_extension_;
bool is_popup_; bool is_popup_;

View File

@ -21,6 +21,7 @@ static const char* kDefaultImageCache[] = {"menu_icon", "window_icon"};
RootWindowViews::RootWindowViews() RootWindowViews::RootWindowViews()
: with_controls_(false), : with_controls_(false),
always_on_top_(false),
with_extension_(false), with_extension_(false),
initially_hidden_(false), initially_hidden_(false),
is_popup_(false), is_popup_(false),
@ -42,6 +43,7 @@ void RootWindowViews::Init(RootWindow::Delegate* delegate,
delegate_ = delegate; delegate_ = delegate;
with_controls_ = config.with_controls; with_controls_ = config.with_controls;
always_on_top_ = config.always_on_top;
with_extension_ = config.with_extension; with_extension_ = config.with_extension;
initially_hidden_ = config.initially_hidden; initially_hidden_ = config.initially_hidden;
if (initially_hidden_ && !config.source_bounds.IsEmpty()) { if (initially_hidden_ && !config.source_bounds.IsEmpty()) {
@ -239,6 +241,7 @@ void RootWindowViews::OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) {
CEF_REQUIRE_UI_THREAD(); CEF_REQUIRE_UI_THREAD();
DCHECK(!window_); DCHECK(!window_);
window_ = window; window_ = window;
window_->SetAlwaysOnTop(always_on_top_);
if (!pending_extensions_.empty()) { if (!pending_extensions_.empty()) {
window_->OnExtensionsChanged(pending_extensions_); window_->OnExtensionsChanged(pending_extensions_);

View File

@ -104,6 +104,7 @@ class RootWindowViews : public RootWindow,
// unless otherwise indicated. // unless otherwise indicated.
// Members set during initialization. // Members set during initialization.
bool with_controls_; bool with_controls_;
bool always_on_top_;
bool with_extension_; bool with_extension_;
bool initially_hidden_; bool initially_hidden_;
CefRefPtr<CefWindow> parent_window_; CefRefPtr<CefWindow> parent_window_;

View File

@ -103,6 +103,7 @@ int GetURLBarHeight(HWND hwnd) {
RootWindowWin::RootWindowWin() RootWindowWin::RootWindowWin()
: with_controls_(false), : with_controls_(false),
always_on_top_(false),
with_osr_(false), with_osr_(false),
with_extension_(false), with_extension_(false),
is_popup_(false), is_popup_(false),
@ -152,6 +153,7 @@ void RootWindowWin::Init(RootWindow::Delegate* delegate,
delegate_ = delegate; delegate_ = delegate;
with_controls_ = config.with_controls; with_controls_ = config.with_controls;
always_on_top_ = config.always_on_top;
with_osr_ = config.with_osr; with_osr_ = config.with_osr;
with_extension_ = config.with_extension; with_extension_ = config.with_extension;
@ -333,6 +335,7 @@ void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
CHECK(find_message_id_); CHECK(find_message_id_);
const DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; const DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
const DWORD dwExStyle = always_on_top_ ? WS_EX_TOPMOST : 0;
int x, y, width, height; int x, y, width, height;
if (::IsRectEmpty(&start_rect_)) { if (::IsRectEmpty(&start_rect_)) {
@ -341,7 +344,7 @@ void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
} else { } else {
// Adjust the window size to account for window frame and controls. // Adjust the window size to account for window frame and controls.
RECT window_rect = start_rect_; RECT window_rect = start_rect_;
::AdjustWindowRectEx(&window_rect, dwStyle, with_controls_, 0); ::AdjustWindowRectEx(&window_rect, dwStyle, with_controls_, dwExStyle);
x = start_rect_.left; x = start_rect_.left;
y = start_rect_.top; y = start_rect_.top;
@ -352,8 +355,8 @@ void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
browser_settings_ = settings; browser_settings_ = settings;
// Create the main window initially hidden. // Create the main window initially hidden.
CreateWindow(window_class.c_str(), window_title.c_str(), dwStyle, x, y, width, CreateWindowEx(dwExStyle, window_class.c_str(), window_title.c_str(), dwStyle,
height, NULL, NULL, hInstance, this); x, y, width, height, NULL, NULL, hInstance, this);
CHECK(hwnd_); CHECK(hwnd_);
if (!called_enable_non_client_dpi_scaling_ && IsProcessPerMonitorDpiAware()) { if (!called_enable_non_client_dpi_scaling_ && IsProcessPerMonitorDpiAware()) {

View File

@ -112,6 +112,7 @@ class RootWindowWin : public RootWindow, public BrowserWindow::Delegate {
// After initialization all members are only accessed on the main thread. // After initialization all members are only accessed on the main thread.
// Members set during initialization. // Members set during initialization.
bool with_controls_; bool with_controls_;
bool always_on_top_;
bool with_osr_; bool with_osr_;
bool with_extension_; bool with_extension_;
bool is_popup_; bool is_popup_;

View File

@ -222,6 +222,13 @@ void ViewsWindow::SetFullscreen(bool fullscreen) {
} }
} }
void ViewsWindow::SetAlwaysOnTop(bool on_top) {
CEF_REQUIRE_UI_THREAD();
if (window_) {
window_->SetAlwaysOnTop(on_top);
}
}
void ViewsWindow::SetLoadingState(bool isLoading, void ViewsWindow::SetLoadingState(bool isLoading,
bool canGoBack, bool canGoBack,
bool canGoForward) { bool canGoForward) {

View File

@ -112,6 +112,7 @@ class ViewsWindow : public CefBrowserViewDelegate,
void SetTitle(const std::string& title); void SetTitle(const std::string& title);
void SetFavicon(CefRefPtr<CefImage> image); void SetFavicon(CefRefPtr<CefImage> image);
void SetFullscreen(bool fullscreen); void SetFullscreen(bool fullscreen);
void SetAlwaysOnTop(bool on_top);
void SetLoadingState(bool isLoading, bool canGoBack, bool canGoForward); void SetLoadingState(bool isLoading, bool canGoBack, bool canGoForward);
void SetDraggableRegions(const std::vector<CefDraggableRegion>& regions); void SetDraggableRegions(const std::vector<CefDraggableRegion>& regions);
void TakeFocus(bool next); void TakeFocus(bool next);

View File

@ -130,6 +130,7 @@ int RunMain(int argc, char* argv[]) {
test_runner::RegisterSchemeHandlers(); test_runner::RegisterSchemeHandlers();
RootWindowConfig window_config; RootWindowConfig window_config;
window_config.always_on_top = command_line->HasSwitch(switches::kAlwaysOnTop);
window_config.with_controls = window_config.with_controls =
!command_line->HasSwitch(switches::kHideControls); !command_line->HasSwitch(switches::kHideControls);
window_config.with_osr = settings.windowless_rendering_enabled ? true : false; window_config.with_osr = settings.windowless_rendering_enabled ? true : false;

View File

@ -95,6 +95,7 @@ int RunMain(HINSTANCE hInstance, int nCmdShow) {
test_runner::RegisterSchemeHandlers(); test_runner::RegisterSchemeHandlers();
RootWindowConfig window_config; RootWindowConfig window_config;
window_config.always_on_top = command_line->HasSwitch(switches::kAlwaysOnTop);
window_config.with_controls = window_config.with_controls =
!command_line->HasSwitch(switches::kHideControls); !command_line->HasSwitch(switches::kHideControls);
window_config.with_osr = settings.windowless_rendering_enabled ? true : false; window_config.with_osr = settings.windowless_rendering_enabled ? true : false;

View File

@ -37,6 +37,7 @@ const char kFilterURL[] = "filter-url";
const char kUseViews[] = "use-views"; const char kUseViews[] = "use-views";
const char kHideFrame[] = "hide-frame"; const char kHideFrame[] = "hide-frame";
const char kHideControls[] = "hide-controls"; const char kHideControls[] = "hide-controls";
const char kAlwaysOnTop[] = "always-on-top";
const char kHideTopMenu[] = "hide-top-menu"; const char kHideTopMenu[] = "hide-top-menu";
const char kWidevineCdmPath[] = "widevine-cdm-path"; const char kWidevineCdmPath[] = "widevine-cdm-path";
const char kSslClientCertificate[] = "ssl-client-certificate"; const char kSslClientCertificate[] = "ssl-client-certificate";

View File

@ -31,6 +31,7 @@ extern const char kFilterURL[];
extern const char kUseViews[]; extern const char kUseViews[];
extern const char kHideFrame[]; extern const char kHideFrame[];
extern const char kHideControls[]; extern const char kHideControls[];
extern const char kAlwaysOnTop[];
extern const char kHideTopMenu[]; extern const char kHideTopMenu[];
extern const char kWidevineCdmPath[]; extern const char kWidevineCdmPath[];
extern const char kSslClientCertificate[]; extern const char kSslClientCertificate[];