Index: ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc |
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc |
index 1c14861b13c96ea5fdf14ca8b17d28515cd95956..8a986d7bc9a98fbb032f4eb63e04ce7315235e1a 100644 |
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc |
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc |
@@ -2,7 +2,7 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include <vector> |
+#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" |
#include <X11/Xlib.h> |
@@ -12,11 +12,13 @@ |
#include "base/memory/scoped_ptr.h" |
#include "base/path_service.h" |
+#include "ui/aura/env.h" |
#include "ui/aura/window.h" |
#include "ui/aura/window_tree_host.h" |
#include "ui/base/resource/resource_bundle.h" |
#include "ui/base/ui_base_paths.h" |
#include "ui/base/x/x11_util.h" |
+#include "ui/events/event_handler.h" |
#include "ui/events/platform/x11/x11_event_source.h" |
#include "ui/gfx/rect.h" |
#include "ui/gfx/x/x11_atom_cache.h" |
@@ -53,18 +55,68 @@ class ActivationWaiter : public X11PropertyChangeWaiter { |
DISALLOW_COPY_AND_ASSIGN(ActivationWaiter); |
}; |
-// Creates a widget of size 100x100. |
-scoped_ptr<Widget> CreateWidget() { |
+// An event handler which counts the number of mouse moves it has seen. |
+class MouseMoveCounterHandler : public ui::EventHandler { |
+ public: |
+ MouseMoveCounterHandler() : count_(0) { |
+ } |
+ virtual ~MouseMoveCounterHandler() { |
+ } |
+ |
+ // ui::EventHandler: |
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { |
+ if (event->type() == ui::ET_MOUSE_MOVED) |
+ ++count_; |
+ } |
+ |
+ int num_mouse_moves() const { |
+ return count_; |
+ } |
+ |
+ private: |
+ int count_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler); |
+}; |
+ |
+// Creates a widget with the given bounds. |
+scoped_ptr<Widget> CreateWidget(const gfx::Rect& bounds) { |
scoped_ptr<Widget> widget(new Widget); |
Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
params.remove_standard_frame = true; |
params.native_widget = new DesktopNativeWidgetAura(widget.get()); |
- params.bounds = gfx::Rect(100, 100, 100, 100); |
+ params.bounds = bounds; |
widget->Init(params); |
return widget.Pass(); |
} |
+// Dispatches an XMotionEvent targeted at |host|'s X window with location |
+// |point_in_screen|. |
+void DispatchMouseMotionEvent(DesktopWindowTreeHostX11* desktop_host, |
+ const gfx::Point& point_in_screen) { |
+ aura::WindowTreeHost* host = static_cast<aura::WindowTreeHost*>(desktop_host); |
+ gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen(); |
+ |
+ Display* display = gfx::GetXDisplay(); |
+ XEvent xev; |
+ xev.xmotion.type = MotionNotify; |
+ xev.xmotion.display = display; |
+ xev.xmotion.window = host->GetAcceleratedWidget(); |
+ xev.xmotion.root = DefaultRootWindow(display); |
+ xev.xmotion.subwindow = 0; |
+ xev.xmotion.time = CurrentTime; |
+ xev.xmotion.x = point_in_screen.x() - bounds_in_screen.x(); |
+ xev.xmotion.y = point_in_screen.y() - bounds_in_screen.y(); |
+ xev.xmotion.x_root = point_in_screen.x(); |
+ xev.xmotion.y_root = point_in_screen.y(); |
+ xev.xmotion.state = 0; |
+ xev.xmotion.is_hint = NotifyNormal; |
+ xev.xmotion.same_screen = True; |
+ |
+ static_cast<ui::PlatformEventDispatcher*>(desktop_host)->DispatchEvent(&xev); |
+} |
+ |
} // namespace |
class DesktopWindowTreeHostX11Test : public ViewsTestBase { |
@@ -82,6 +134,7 @@ class DesktopWindowTreeHostX11Test : public ViewsTestBase { |
ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path); |
} |
+ // testing::Test |
virtual void SetUp() OVERRIDE { |
ViewsTestBase::SetUp(); |
@@ -103,7 +156,7 @@ class DesktopWindowTreeHostX11Test : public ViewsTestBase { |
// Chrome even if it not possible to deactivate the window wrt to the x server. |
// This behavior is required by several interactive_ui_tests. |
TEST_F(DesktopWindowTreeHostX11Test, Deactivate) { |
- scoped_ptr<Widget> widget(CreateWidget()); |
+ scoped_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100))); |
ActivationWaiter waiter( |
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); |
@@ -124,4 +177,81 @@ TEST_F(DesktopWindowTreeHostX11Test, Deactivate) { |
EXPECT_TRUE(widget->IsActive()); |
} |
+// Chrome attempts to make mouse capture look synchronous on Linux. Test that |
+// Chrome synchronously switches the window that mouse events are forwarded to |
+// when capture is changed. |
+TEST_F(DesktopWindowTreeHostX11Test, CaptureEventForwarding) { |
+ scoped_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100))); |
+ aura::Window* window1 = widget1->GetNativeWindow(); |
+ DesktopWindowTreeHostX11* host1 = |
+ static_cast<DesktopWindowTreeHostX11*>(window1->GetHost()); |
+ widget1->Show(); |
+ |
+ scoped_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100))); |
+ aura::Window* window2 = widget2->GetNativeWindow(); |
+ DesktopWindowTreeHostX11* host2 = |
+ static_cast<DesktopWindowTreeHostX11*>(window2->GetHost()); |
+ widget2->Show(); |
+ |
+ MouseMoveCounterHandler recorder1; |
+ window1->AddPreTargetHandler(&recorder1); |
+ MouseMoveCounterHandler recorder2; |
+ window2->AddPreTargetHandler(&recorder2); |
+ |
+ // Move the mouse to the center of |widget2|. |
+ gfx::Point point_in_screen = widget2->GetWindowBoundsInScreen().CenterPoint(); |
+ DispatchMouseMotionEvent(host2, point_in_screen); |
+ EXPECT_EQ(0, recorder1.num_mouse_moves()); |
+ EXPECT_EQ(1, recorder2.num_mouse_moves()); |
+ EXPECT_EQ(point_in_screen.ToString(), |
+ aura::Env::GetInstance()->last_mouse_location().ToString()); |
+ |
+ // Set capture to |widget1|. Because DesktopWindowTreeHostX11 calls |
+ // XGrabPointer() with owner == False, the X server sends events to |widget2| |
+ // as long as the mouse is hovered over |widget2|. Verify that Chrome |
+ // redirects mouse events to |widget1|. |
+ widget1->SetCapture(NULL); |
+ point_in_screen += gfx::Vector2d(1, 0); |
+ DispatchMouseMotionEvent(host2, point_in_screen); |
+ EXPECT_EQ(1, recorder1.num_mouse_moves()); |
+ EXPECT_EQ(1, recorder2.num_mouse_moves()); |
+ // If the event's location was correctly changed to be relative to |widget1|, |
+ // Env's last mouse position will be correct. |
+ EXPECT_EQ(point_in_screen.ToString(), |
+ aura::Env::GetInstance()->last_mouse_location().ToString()); |
+ |
+ // Set capture to |widget2|. Subsequent events sent to |widget2| should not be |
+ // forwarded. |
+ widget2->SetCapture(NULL); |
+ point_in_screen += gfx::Vector2d(1, 0); |
+ DispatchMouseMotionEvent(host2, point_in_screen); |
+ EXPECT_EQ(1, recorder1.num_mouse_moves()); |
+ EXPECT_EQ(2, recorder2.num_mouse_moves()); |
+ EXPECT_EQ(point_in_screen.ToString(), |
+ aura::Env::GetInstance()->last_mouse_location().ToString()); |
+ |
+ // If the mouse is not hovered over |widget1| or |widget2|, the X server will |
+ // send events to the window which has capture. Test the mouse events sent to |
+ // |widget2| are not forwarded. |
+ DispatchMouseMotionEvent(host2, point_in_screen); |
+ EXPECT_EQ(1, recorder1.num_mouse_moves()); |
+ EXPECT_EQ(3, recorder2.num_mouse_moves()); |
+ EXPECT_EQ(point_in_screen.ToString(), |
+ aura::Env::GetInstance()->last_mouse_location().ToString()); |
+ |
+ // Release capture. Test that when capture is released, mouse events are no |
+ // longer forwarded to other widgets. |
+ widget2->ReleaseCapture(); |
+ point_in_screen = widget1->GetWindowBoundsInScreen().CenterPoint(); |
+ DispatchMouseMotionEvent(host1, point_in_screen); |
+ EXPECT_EQ(2, recorder1.num_mouse_moves()); |
+ EXPECT_EQ(3, recorder2.num_mouse_moves()); |
+ EXPECT_EQ(point_in_screen.ToString(), |
+ aura::Env::GetInstance()->last_mouse_location().ToString()); |
+ |
+ // Cleanup |
+ window1->RemovePreTargetHandler(&recorder1); |
+ window2->RemovePreTargetHandler(&recorder2); |
+} |
+ |
} // namespace views |