Home | History | Annotate | Download | only in desktop_aura
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
      6 
      7 #include <X11/Xlib.h>
      8 
      9 // Get rid of X11 macros which conflict with gtest.
     10 #undef Bool
     11 #undef None
     12 
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/path_service.h"
     15 #include "ui/aura/env.h"
     16 #include "ui/aura/window.h"
     17 #include "ui/aura/window_tree_host.h"
     18 #include "ui/base/resource/resource_bundle.h"
     19 #include "ui/base/ui_base_paths.h"
     20 #include "ui/base/x/x11_util.h"
     21 #include "ui/events/event_handler.h"
     22 #include "ui/events/platform/x11/x11_event_source.h"
     23 #include "ui/gfx/rect.h"
     24 #include "ui/gfx/x/x11_atom_cache.h"
     25 #include "ui/gl/gl_surface.h"
     26 #include "ui/views/test/views_test_base.h"
     27 #include "ui/views/test/x11_property_change_waiter.h"
     28 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
     29 
     30 namespace views {
     31 
     32 namespace {
     33 
     34 // Blocks till |window| gets activated.
     35 class ActivationWaiter : public X11PropertyChangeWaiter {
     36  public:
     37   explicit ActivationWaiter(XID window)
     38       : X11PropertyChangeWaiter(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW"),
     39         window_(window) {
     40   }
     41 
     42   virtual ~ActivationWaiter() {
     43   }
     44 
     45  private:
     46   // X11PropertyChangeWaiter:
     47   virtual bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) OVERRIDE {
     48     XID xid = 0;
     49     ui::GetXIDProperty(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW", &xid);
     50     return xid != window_;
     51   }
     52 
     53   XID window_;
     54 
     55   DISALLOW_COPY_AND_ASSIGN(ActivationWaiter);
     56 };
     57 
     58 // An event handler which counts the number of mouse moves it has seen.
     59 class MouseMoveCounterHandler : public ui::EventHandler {
     60  public:
     61   MouseMoveCounterHandler() : count_(0) {
     62   }
     63   virtual ~MouseMoveCounterHandler() {
     64   }
     65 
     66   // ui::EventHandler:
     67   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
     68     if (event->type() == ui::ET_MOUSE_MOVED)
     69       ++count_;
     70   }
     71 
     72   int num_mouse_moves() const {
     73     return count_;
     74   }
     75 
     76  private:
     77   int count_;
     78 
     79   DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler);
     80 };
     81 
     82 // Creates a widget with the given bounds.
     83 scoped_ptr<Widget> CreateWidget(const gfx::Rect& bounds) {
     84   scoped_ptr<Widget> widget(new Widget);
     85   Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
     86   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     87   params.remove_standard_frame = true;
     88   params.native_widget = new DesktopNativeWidgetAura(widget.get());
     89   params.bounds = bounds;
     90   widget->Init(params);
     91   return widget.Pass();
     92 }
     93 
     94 // Dispatches an XMotionEvent targeted at |host|'s X window with location
     95 // |point_in_screen|.
     96 void DispatchMouseMotionEvent(DesktopWindowTreeHostX11* desktop_host,
     97                               const gfx::Point& point_in_screen) {
     98   aura::WindowTreeHost* host = static_cast<aura::WindowTreeHost*>(desktop_host);
     99   gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen();
    100 
    101   Display* display = gfx::GetXDisplay();
    102   XEvent xev;
    103   xev.xmotion.type = MotionNotify;
    104   xev.xmotion.display = display;
    105   xev.xmotion.window = host->GetAcceleratedWidget();
    106   xev.xmotion.root = DefaultRootWindow(display);
    107   xev.xmotion.subwindow = 0;
    108   xev.xmotion.time = CurrentTime;
    109   xev.xmotion.x = point_in_screen.x() - bounds_in_screen.x();
    110   xev.xmotion.y = point_in_screen.y() - bounds_in_screen.y();
    111   xev.xmotion.x_root = point_in_screen.x();
    112   xev.xmotion.y_root = point_in_screen.y();
    113   xev.xmotion.state = 0;
    114   xev.xmotion.is_hint = NotifyNormal;
    115   xev.xmotion.same_screen = True;
    116 
    117   static_cast<ui::PlatformEventDispatcher*>(desktop_host)->DispatchEvent(&xev);
    118 }
    119 
    120 }  // namespace
    121 
    122 class DesktopWindowTreeHostX11Test : public ViewsTestBase {
    123  public:
    124   DesktopWindowTreeHostX11Test() {
    125   }
    126   virtual ~DesktopWindowTreeHostX11Test() {
    127   }
    128 
    129   static void SetUpTestCase() {
    130     gfx::GLSurface::InitializeOneOffForTests();
    131     ui::RegisterPathProvider();
    132     base::FilePath ui_test_pak_path;
    133     ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
    134     ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
    135   }
    136 
    137   // testing::Test
    138   virtual void SetUp() OVERRIDE {
    139     ViewsTestBase::SetUp();
    140 
    141     // Make X11 synchronous for our display connection. This does not force the
    142     // window manager to behave synchronously.
    143     XSynchronize(gfx::GetXDisplay(), True);
    144   }
    145 
    146   virtual void TearDown() OVERRIDE {
    147     XSynchronize(gfx::GetXDisplay(), False);
    148     ViewsTestBase::TearDown();
    149   }
    150 
    151  private:
    152   DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
    153 };
    154 
    155 // Test that calling Widget::Deactivate() sets the widget as inactive wrt to
    156 // Chrome even if it not possible to deactivate the window wrt to the x server.
    157 // This behavior is required by several interactive_ui_tests.
    158 TEST_F(DesktopWindowTreeHostX11Test, Deactivate) {
    159   scoped_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
    160 
    161   ActivationWaiter waiter(
    162       widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
    163   widget->Show();
    164   widget->Activate();
    165   waiter.Wait();
    166 
    167   widget->Deactivate();
    168   // Regardless of whether |widget|'s X11 window eventually gets deactivated,
    169   // |widget|'s "active" state should change.
    170   EXPECT_FALSE(widget->IsActive());
    171 
    172   // |widget|'s X11 window should still be active. Reactivating |widget| should
    173   // update the widget's "active" state.
    174   // Note: Activating a widget whose X11 window is not active does not
    175   // synchronously update the widget's "active" state.
    176   widget->Activate();
    177   EXPECT_TRUE(widget->IsActive());
    178 }
    179 
    180 // Chrome attempts to make mouse capture look synchronous on Linux. Test that
    181 // Chrome synchronously switches the window that mouse events are forwarded to
    182 // when capture is changed.
    183 TEST_F(DesktopWindowTreeHostX11Test, CaptureEventForwarding) {
    184   scoped_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100)));
    185   aura::Window* window1 = widget1->GetNativeWindow();
    186   DesktopWindowTreeHostX11* host1 =
    187       static_cast<DesktopWindowTreeHostX11*>(window1->GetHost());
    188   widget1->Show();
    189 
    190   scoped_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100)));
    191   aura::Window* window2 = widget2->GetNativeWindow();
    192   DesktopWindowTreeHostX11* host2 =
    193       static_cast<DesktopWindowTreeHostX11*>(window2->GetHost());
    194   widget2->Show();
    195 
    196   MouseMoveCounterHandler recorder1;
    197   window1->AddPreTargetHandler(&recorder1);
    198   MouseMoveCounterHandler recorder2;
    199   window2->AddPreTargetHandler(&recorder2);
    200 
    201   // Move the mouse to the center of |widget2|.
    202   gfx::Point point_in_screen = widget2->GetWindowBoundsInScreen().CenterPoint();
    203   DispatchMouseMotionEvent(host2, point_in_screen);
    204   EXPECT_EQ(0, recorder1.num_mouse_moves());
    205   EXPECT_EQ(1, recorder2.num_mouse_moves());
    206   EXPECT_EQ(point_in_screen.ToString(),
    207             aura::Env::GetInstance()->last_mouse_location().ToString());
    208 
    209   // Set capture to |widget1|. Because DesktopWindowTreeHostX11 calls
    210   // XGrabPointer() with owner == False, the X server sends events to |widget2|
    211   // as long as the mouse is hovered over |widget2|. Verify that Chrome
    212   // redirects mouse events to |widget1|.
    213   widget1->SetCapture(NULL);
    214   point_in_screen += gfx::Vector2d(1, 0);
    215   DispatchMouseMotionEvent(host2, point_in_screen);
    216   EXPECT_EQ(1, recorder1.num_mouse_moves());
    217   EXPECT_EQ(1, recorder2.num_mouse_moves());
    218   // If the event's location was correctly changed to be relative to |widget1|,
    219   // Env's last mouse position will be correct.
    220   EXPECT_EQ(point_in_screen.ToString(),
    221             aura::Env::GetInstance()->last_mouse_location().ToString());
    222 
    223   // Set capture to |widget2|. Subsequent events sent to |widget2| should not be
    224   // forwarded.
    225   widget2->SetCapture(NULL);
    226   point_in_screen += gfx::Vector2d(1, 0);
    227   DispatchMouseMotionEvent(host2, point_in_screen);
    228   EXPECT_EQ(1, recorder1.num_mouse_moves());
    229   EXPECT_EQ(2, recorder2.num_mouse_moves());
    230   EXPECT_EQ(point_in_screen.ToString(),
    231             aura::Env::GetInstance()->last_mouse_location().ToString());
    232 
    233   // If the mouse is not hovered over |widget1| or |widget2|, the X server will
    234   // send events to the window which has capture. Test the mouse events sent to
    235   // |widget2| are not forwarded.
    236   DispatchMouseMotionEvent(host2, point_in_screen);
    237   EXPECT_EQ(1, recorder1.num_mouse_moves());
    238   EXPECT_EQ(3, recorder2.num_mouse_moves());
    239   EXPECT_EQ(point_in_screen.ToString(),
    240             aura::Env::GetInstance()->last_mouse_location().ToString());
    241 
    242   // Release capture. Test that when capture is released, mouse events are no
    243   // longer forwarded to other widgets.
    244   widget2->ReleaseCapture();
    245   point_in_screen = widget1->GetWindowBoundsInScreen().CenterPoint();
    246   DispatchMouseMotionEvent(host1, point_in_screen);
    247   EXPECT_EQ(2, recorder1.num_mouse_moves());
    248   EXPECT_EQ(3, recorder2.num_mouse_moves());
    249   EXPECT_EQ(point_in_screen.ToString(),
    250             aura::Env::GetInstance()->last_mouse_location().ToString());
    251 
    252   // Cleanup
    253   window1->RemovePreTargetHandler(&recorder1);
    254   window2->RemovePreTargetHandler(&recorder2);
    255 }
    256 
    257 }  // namespace views
    258