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().
browser_->AddRef();
CefWindowDelegateView* delegate_view =
new CefWindowDelegateView(GetBackgroundColor());
CefWindowDelegateView* delegate_view = new CefWindowDelegateView(
GetBackgroundColor(), window_x11_->TopLevelAlwaysOnTop());
delegate_view->Init(window_info_.window, browser_->web_contents(),
gfx::Rect(gfx::Point(), rect.size()));

View File

@ -194,8 +194,15 @@ bool CefBrowserPlatformDelegateNativeWin::CreateHostWindow() {
point =
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 =
new CefWindowDelegateView(GetBackgroundColor());
new CefWindowDelegateView(GetBackgroundColor(), always_on_top);
delegate_view->Init(window_info_.window, browser_->web_contents(),
gfx::Rect(0, 0, point.x(), point.y()));

View File

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

View File

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

View File

@ -396,3 +396,34 @@ void CefWindowX11::ContinueFocus() {
browser_->SetFocus(true);
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_; }
gfx::Rect bounds() const { return bounds_; }
bool TopLevelAlwaysOnTop() const;
private:
void ContinueFocus();

View File

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

View File

@ -51,6 +51,7 @@ void MaximizeWindow(GtkWindow* window) {
RootWindowGtk::RootWindowGtk()
: with_controls_(false),
always_on_top_(false),
with_osr_(false),
with_extension_(false),
is_popup_(false),
@ -84,6 +85,7 @@ void RootWindowGtk::Init(RootWindow::Delegate* delegate,
delegate_ = delegate;
with_controls_ = config.with_controls;
always_on_top_ = config.always_on_top;
with_osr_ = config.with_osr;
with_extension_ = config.with_extension;
start_rect_ = config.bounds;
@ -275,6 +277,10 @@ void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings,
window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
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);
g_signal_connect(G_OBJECT(window_), "focus-in-event",
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.
// Members set during initialization.
bool with_controls_;
bool always_on_top_;
bool with_osr_;
bool with_extension_;
bool is_popup_;

View File

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

View File

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

View File

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

View File

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

View File

@ -130,6 +130,7 @@ int RunMain(int argc, char* argv[]) {
test_runner::RegisterSchemeHandlers();
RootWindowConfig window_config;
window_config.always_on_top = command_line->HasSwitch(switches::kAlwaysOnTop);
window_config.with_controls =
!command_line->HasSwitch(switches::kHideControls);
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();
RootWindowConfig window_config;
window_config.always_on_top = command_line->HasSwitch(switches::kAlwaysOnTop);
window_config.with_controls =
!command_line->HasSwitch(switches::kHideControls);
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 kHideFrame[] = "hide-frame";
const char kHideControls[] = "hide-controls";
const char kAlwaysOnTop[] = "always-on-top";
const char kHideTopMenu[] = "hide-top-menu";
const char kWidevineCdmPath[] = "widevine-cdm-path";
const char kSslClientCertificate[] = "ssl-client-certificate";

View File

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