Skip to content

Commit

Permalink
Fix focus issues under X11
Browse files Browse the repository at this point in the history
  * Prevent focus stealing when mouse enter a web view.
  * Return focus to the toplevel window or a special "focus window"
    on BrowserHost->SendFocusEvent(false).
  * Fix FindToplevelParent for nested x window hiearchy

Signed-off-by: Jiří Janoušek <[email protected]>
  • Loading branch information
jiri-janousek committed Mar 12, 2018
1 parent 96f907e commit 2c8185d
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,11 @@ views::Widget* CefBrowserPlatformDelegateNativeLinux::GetWindowWidget() const {
}

void CefBrowserPlatformDelegateNativeLinux::SendFocusEvent(bool setFocus) {
if (!setFocus)
if (!setFocus) {
if (window_x11_)
window_x11_->Unfocus();
return;
}

if (browser_->web_contents()) {
// Give logical focus to the RenderWidgetHostViewAura in the views
Expand Down
73 changes: 67 additions & 6 deletions libcef/browser/native/window_x11.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,42 @@ ::Window FindToplevelParent(::Display* display, ::Window window) {
::Window* children = NULL;
unsigned int nchildren = 0;
// Enumerate all parents of "window" to find the highest level window
// that either:
// - has a parent that does not contain the _NET_WM_PID property
// - has a parent that is the root window.
// that has a parent that is the root window.
while (XQueryTree(display, window, &root, &parent, &children, &nchildren)) {
if (children) {
XFree(children);
}

top_level_window = window;
if (!ui::PropertyExists(parent, kNetWMPid) || parent == root) {
if (parent == root) {
break;
}
window = parent;
}
return top_level_window;
}

bool IsParentOfChildWindow(::Display* display, ::Window parent, ::Window child) {
if (parent == child)
return false;
::Window root = None;
::Window real_parent = None;
::Window* children = NULL;
unsigned int nchildren = 0;
while (XQueryTree(display, child, &root, &real_parent, &children, &nchildren)) {
if (children) {
XFree(children);
}
if (real_parent == root) {
return real_parent == parent;
}
if (real_parent == parent)
return true;
child = real_parent;
}
return false;
}

} // namespace

CEF_EXPORT XDisplay* cef_get_xdisplay() {
Expand All @@ -87,6 +106,7 @@ CefWindowX11::CefWindowX11(CefRefPtr<CefBrowserHostImpl> browser,
xdisplay_(gfx::GetXDisplay()),
parent_xwindow_(parent_xwindow),
xwindow_(0),
previously_focused_(0),
window_mapped_(false),
bounds_(bounds),
focus_pending_(false),
Expand Down Expand Up @@ -218,14 +238,55 @@ void CefWindowX11::Focus() {
if (xwindow_ == x11::None || !window_mapped_)
return;

::Window focused = x11::None;
int revert_to = 0;
XGetInputFocus(xdisplay_, &focused, &revert_to);

if (browser_.get()) {
::Window child = FindChild(xdisplay_, xwindow_);
if (child && ui::IsWindowVisible(child)) {
if (child && focused != child && ui::IsWindowVisible(child)) {
// Give focus to the child DesktopWindowTreeHostX11.
XSetInputFocus(xdisplay_, child, RevertToParent, x11::CurrentTime);
if (focused != xwindow_) {
// Store the focused window to restore the original state precisely.
previously_focused_ = focused;
}
}
} else {
} else if (focused != xwindow_) {
XSetInputFocus(xdisplay_, xwindow_, RevertToParent, x11::CurrentTime);
// Store the focused window to restore the original state precisely.
previously_focused_ = focused;
}
}

void CefWindowX11::Unfocus() {
if (xwindow_ == x11::None || !window_mapped_)
return;

::Window focused = x11::None;
int revert_to = 0;
XGetInputFocus(xdisplay_, &focused, &revert_to);
if (!focused)
return;

::Window toplevel = FindToplevelParent(xdisplay_, xwindow_);
if (toplevel == xwindow_)
return;

::Window child = x11::None;
if (browser_.get()) {
child = FindChild(xdisplay_, xwindow_);
}
if (focused == xwindow_ || focused == child) {
// Our window or child window still has keyboard focus. Return it back to
// the original window so that GUI toolkits can receive keyboard events again.
if (previously_focused_ && IsParentOfChildWindow(xdisplay_, toplevel, previously_focused_)) {
// GTK+ may have a special "focus window" for keyboard events. It must be a child of the toplevel though.
XSetInputFocus(xdisplay_, previously_focused_, RevertToParent, x11::CurrentTime);
} else {
// Otherwise, the tolevel window is the best focus candidate we have.
XSetInputFocus(xdisplay_, toplevel, RevertToParent, x11::CurrentTime);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions libcef/browser/native/window_x11.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class CefWindowX11 : public ui::PlatformEventDispatcher {
void Hide();

void Focus();
void Unfocus();

void SetBounds(const gfx::Rect& bounds);

Expand All @@ -61,6 +62,7 @@ class CefWindowX11 : public ui::PlatformEventDispatcher {
::Display* xdisplay_;
::Window parent_xwindow_;
::Window xwindow_;
::Window previously_focused_;

// Is the window mapped to the screen?
bool window_mapped_;
Expand Down
2 changes: 1 addition & 1 deletion patch/patches/extensions_1947.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git content/browser/frame_host/render_frame_host_manager.cc content/browser/frame_host/render_frame_host_manager.cc
index 1971727c0a8f..2babb3aca3d0 100644
index 1971727c0a8f..4306530c069e 100644
--- content/browser/frame_host/render_frame_host_manager.cc
+++ content/browser/frame_host/render_frame_host_manager.cc
@@ -1111,10 +1111,11 @@ bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
Expand Down
34 changes: 27 additions & 7 deletions patch/patches/views_widget_180_1481_1565_1677_1749.patch
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ index db66147f0e9c..2b9bdfa2ec53 100644
// a reference.
corewm::TooltipWin* tooltip_;
diff --git ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index b802ad497cf2..46e7f0678c64 100644
index b802ad497cf2..1dcfba0ef53d 100644
--- ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -147,6 +147,7 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11(
Expand Down Expand Up @@ -222,7 +222,18 @@ index b802ad497cf2..46e7f0678c64 100644
return bounds_in_pixels_;
}

@@ -506,7 +510,8 @@ void DesktopWindowTreeHostX11::CloseNow() {
@@ -262,7 +266,9 @@ void DesktopWindowTreeHostX11::OnCrossingEvent(bool enter,
// window or the PointerRoot is focused) && |has_pointer_|. Therefore, we
// can just use |has_pointer_| in the assignment. The transitions for when
// the focus changes are handled in OnFocusEvent().
- has_pointer_focus_ = has_pointer_;
+ // On the other hand, if has_external_parent_ is true, we don't want to steal
+ // focus from the toolkits when mouse enters web view. It must click.
+ has_pointer_focus_ = has_pointer_ && !has_external_parent_;
}

AfterActivationStateChanged();
@@ -506,7 +512,8 @@ void DesktopWindowTreeHostX11::CloseNow() {
// Actually free our native resources.
if (ui::PlatformEventSource::GetInstance())
ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
Expand All @@ -232,7 +243,7 @@ index b802ad497cf2..46e7f0678c64 100644
xwindow_ = x11::None;

desktop_native_widget_aura_->OnHostClosed();
@@ -647,6 +652,8 @@ void DesktopWindowTreeHostX11::GetWindowPlacement(
@@ -647,6 +654,8 @@ void DesktopWindowTreeHostX11::GetWindowPlacement(
}

gfx::Rect DesktopWindowTreeHostX11::GetWindowBoundsInScreen() const {
Expand All @@ -241,7 +252,16 @@ index b802ad497cf2..46e7f0678c64 100644
return ToDIPRect(bounds_in_pixels_);
}

@@ -1243,6 +1250,8 @@ void DesktopWindowTreeHostX11::SetBoundsInPixels(
@@ -723,7 +732,7 @@ void DesktopWindowTreeHostX11::SetShape(
}

void DesktopWindowTreeHostX11::Activate() {
- if (!IsVisible() || !activatable_)
+ if (!IsVisible() || !activatable_ || has_external_parent_)
return;

BeforeActivationStateChanged();
@@ -1243,6 +1252,8 @@ void DesktopWindowTreeHostX11::SetBoundsInPixels(
}

gfx::Point DesktopWindowTreeHostX11::GetLocationOnScreenInPixels() const {
Expand All @@ -250,15 +270,15 @@ index b802ad497cf2..46e7f0678c64 100644
return bounds_in_pixels_.origin();
}

@@ -1335,7 +1344,6 @@ void DesktopWindowTreeHostX11::InitX11Window(
@@ -1335,7 +1346,6 @@ void DesktopWindowTreeHostX11::InitX11Window(
::Atom window_type;
switch (params.type) {
case Widget::InitParams::TYPE_MENU:
- swa.override_redirect = x11::True;
window_type = gfx::GetAtom("_NET_WM_WINDOW_TYPE_MENU");
break;
case Widget::InitParams::TYPE_TOOLTIP:
@@ -1391,9 +1399,15 @@ void DesktopWindowTreeHostX11::InitX11Window(
@@ -1391,9 +1401,15 @@ void DesktopWindowTreeHostX11::InitX11Window(
attribute_mask |= CWBorderPixel;
swa.border_pixel = 0;

Expand All @@ -275,7 +295,7 @@ index b802ad497cf2..46e7f0678c64 100644
bounds_in_pixels_.y(), bounds_in_pixels_.width(),
bounds_in_pixels_.height(),
0, // border width
@@ -2010,6 +2024,10 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent(
@@ -2010,6 +2026,10 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent(
}
break;
}
Expand Down

0 comments on commit 2c8185d

Please sign in to comment.