text: // Copyright 2018 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
  
  #include "ui/aura/client/aura_constants.h"
  #include "ui/aura/window_tree_host_platform.h"
  #include "ui/base/hit_test.h"
  #include "ui/platform_window/platform_window.h"
  #include "ui/platform_window/wm/wm_move_resize_handler.h"
  #include "ui/views/test/widget_test.h"
  #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
  #include "ui/views/widget/desktop_aura/window_event_filter_linux.h"
  #include "ui/views/widget/widget_delegate.h"
  #include "ui/views/window/native_frame_view.h"
  
  #if defined(USE_X11)
  #include "ui/aura/env.h"
  #include "ui/base/ime/input_method.h"
  #include "ui/base/x/test/x11_property_change_waiter.h"
  #include "ui/base/x/x11_util.h"
  #include "ui/gfx/x/x11_atom_cache.h"
  #include "ui/gfx/x/xproto.h"
  #include "ui/gfx/x/xproto_util.h"
  #include "ui/views/controls/textfield/textfield.h"
  #endif  // defined(USE_X11)
  
  namespace views {
  
  namespace {
  
  bool IsNonClientComponent(int hittest) {
    switch (hittest) {
      case HTBOTTOM:
      case HTBOTTOMLEFT:
      case HTBOTTOMRIGHT:
      case HTCAPTION:
      case HTLEFT:
      case HTRIGHT:
      case HTTOP:
      case HTTOPLEFT:
      case HTTOPRIGHT:
        return true;
      default:
        return false;
    }
    return true;
  }
  
  #if defined(USE_X11)
  // Creates a widget with the given bounds.
  std::unique_ptr<Widget> CreateWidget(const gfx::Rect& bounds) {
    std::unique_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 = bounds;
    widget->Init(std::move(params));
    return widget;
  }
  
  // Dispatches a XMotionEvent targeted at |host|'s X window with location
  // |point_in_screen|.
  void DispatchMouseMotionEvent(DesktopWindowTreeHostLinux* desktop_host,
                                const gfx::Point& point_in_screen) {
    gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen();
  
    auto* connection = x11::Connection::Get();
    x11::MotionNotifyEvent xev{
        .detail = x11::Motion::Normal,
        .root = connection->default_root(),
        .event = static_cast<x11::Window>(desktop_host->GetAcceleratedWidget()),
        .root_x = point_in_screen.x(),
        .root_y = point_in_screen.y(),
        .event_x = point_in_screen.x() - bounds_in_screen.x(),
        .event_y = point_in_screen.y() - bounds_in_screen.y(),
        .same_screen = true,
    };
  
    connection->DispatchEvent(x11::Event{xev});
  }
  
  // Blocks till |window| gets activated.
  class ActivationWaiter : public ui::X11PropertyChangeWaiter {
   public:
    explicit ActivationWaiter(x11::Window window)
        : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(),
                                      "_NET_ACTIVE_WINDOW"),
          window_(window) {}
  
    ~ActivationWaiter() override = default;
  
    ActivationWaiter(const ActivationWaiter&) = delete;
    ActivationWaiter& operator=(ActivationWaiter&) = delete;
  
   private:
    // ui::X11PropertyChangeWaiter:
    bool ShouldKeepOnWaiting() override {
      x11::Window window = x11::Window::None;
      GetProperty(ui::GetX11RootWindow(), x11::GetAtom("_NET_ACTIVE_WINDOW"),
                  &window);
      return window != window_;
    }
  
    x11::Window window_;
  };
  #endif  // defined(USE_X11)
  
  // An event handler which counts the number of mouse moves it has seen.
  class MouseMoveCounterHandler : public ui::EventHandler {
   public:
    MouseMoveCounterHandler() = default;
    ~MouseMoveCounterHandler() override = default;
  
    // ui::EventHandler:
    void OnMouseEvent(ui::MouseEvent* event) override {
      if (event->type() == ui::ET_MOUSE_MOVED)
        ++count_;
    }
  
    int num_mouse_moves() const { return count_; }
  
   private:
    int count_ = 0;
  
    DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler);
  };
  
  // A fake handler, which just stores the hittest and pointer location values.
  class FakeWmMoveResizeHandler : public ui::WmMoveResizeHandler {
   public:
    using SetBoundsCallback = base::RepeatingCallback<void(gfx::Rect)>;
    explicit FakeWmMoveResizeHandler(ui::PlatformWindow* window)
        : platform_window_(window), hittest_(-1) {}
    ~FakeWmMoveResizeHandler() override = default;
  
    void Reset() {
      hittest_ = -1;
      pointer_location_in_px_ = gfx::Point();
    }
  
    int hittest() const { return hittest_; }
    gfx::Point pointer_location_in_px() const { return pointer_location_in_px_; }
  
    void set_bounds(const gfx::Rect& bounds) { bounds_ = bounds; }
  
    // ui::WmMoveResizeHandler
    void DispatchHostWindowDragMovement(
        int hittest,
        const gfx::Point& pointer_location_in_px) override {
      hittest_ = hittest;
      pointer_location_in_px_ = pointer_location_in_px;
  
      platform_window_->SetBounds(bounds_);
    }
  
   private:
    ui::PlatformWindow* platform_window_;
    gfx::Rect bounds_;
  
    int hittest_ = -1;
    gfx::Point pointer_location_in_px_;
  
    DISALLOW_COPY_AND_ASSIGN(FakeWmMoveResizeHandler);
  };
  
  void SetExpectationBasedOnHittestValue(
      int hittest,
      const FakeWmMoveResizeHandler& handler,
      const gfx::Point& pointer_location_in_px) {
    if (IsNonClientComponent(hittest)) {
      // Ensure both the pointer location and the hit test value are passed to the
      // fake move/resize handler.
      EXPECT_EQ(handler.pointer_location_in_px().ToString(),
                pointer_location_in_px.ToString());
      EXPECT_EQ(handler.hittest(), hittest);
      return;
    }
  
    // Ensure the handler does not receive the hittest value or the pointer
    // location.
    EXPECT_TRUE(handler.pointer_location_in_px().IsOrigin());
    EXPECT_NE(handler.hittest(), hittest);
  }
  
  // This is used to return a customized result to NonClientHitTest.
  class HitTestNonClientFrameView : public NativeFrameView {
   public:
    explicit HitTestNonClientFrameView(Widget* widget)
        : NativeFrameView(widget) {}
    ~HitTestNonClientFrameView() override = default;
  
    void set_hit_test_result(int component) { hit_test_result_ = component; }
  
    // NonClientFrameView overrides:
    int NonClientHitTest(const gfx::Point& point) override {
      return hit_test_result_;
    }
  
   private:
    int hit_test_result_ = HTNOWHERE;
  
    DISALLOW_COPY_AND_ASSIGN(HitTestNonClientFrameView);
  };
  
  // This is used to return HitTestNonClientFrameView on create call.
  class HitTestWidgetDelegate : public WidgetDelegate {
   public:
    HitTestWidgetDelegate() {
      SetCanResize(true);
      SetOwnedByWidget(true);
    }
    ~HitTestWidgetDelegate() override = default;
  
    HitTestNonClientFrameView* frame_view() { return frame_view_; }
  
    // WidgetDelegate:
    std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
        Widget* widget) override {
      DCHECK(!frame_view_);
      auto frame_view = std::make_unique<HitTestNonClientFrameView>(widget);
      frame_view_ = frame_view.get();
      return frame_view;
    }
  
   private:
    HitTestNonClientFrameView* frame_view_ = nullptr;
  
    DISALLOW_COPY_AND_ASSIGN(HitTestWidgetDelegate);
  };
  
  // Test host that can intercept calls to the real host.
  class TestDesktopWindowTreeHostLinux : public DesktopWindowTreeHostLinux {
   public:
    TestDesktopWindowTreeHostLinux(
        internal::NativeWidgetDelegate* native_widget_delegate,
        DesktopNativeWidgetAura* desktop_native_widget_aura)
        : DesktopWindowTreeHostLinux(native_widget_delegate,
                                     desktop_native_widget_aura) {}
    ~TestDesktopWindowTreeHostLinux() override = default;
  
    // PlatformWindowDelegate:
    // Instead of making these tests friends of the host, override the dispatch
    // method to make it public and nothing else.
    void DispatchEvent(ui::Event* event) override {
      DesktopWindowTreeHostLinux::DispatchEvent(event);
    }
  
    void ResetCalledMaximize() { called_maximize_ = false; }
    bool called_maximize() const { return called_maximize_; }
    // DesktopWindowTreeHost
    void Maximize() override {
      called_maximize_ = true;
      DesktopWindowTreeHostLinux::Maximize();
    }
  
   private:
    bool called_maximize_ = false;
  
    DISALLOW_COPY_AND_ASSIGN(TestDesktopWindowTreeHostLinux);
  };
  
  }  // namespace
  
  class DesktopWindowTreeHostLinuxTest
      : public test::DesktopWidgetTestInteractive {
   public:
    DesktopWindowTreeHostLinuxTest() = default;
    ~DesktopWindowTreeHostLinuxTest() override = default;
  
   protected:
    Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds) {
      Widget* toplevel = new Widget;
      delegate_ = new HitTestWidgetDelegate();
      Widget::InitParams toplevel_params =
          CreateParams(Widget::InitParams::TYPE_WINDOW);
      auto* native_widget = new DesktopNativeWidgetAura(toplevel);
      toplevel_params.native_widget = native_widget;
      host_ = new TestDesktopWindowTreeHostLinux(toplevel, native_widget);
      toplevel_params.desktop_window_tree_host = host_;
      toplevel_params.delegate = delegate_;
      toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
      toplevel_params.bounds = bounds;
      toplevel_params.remove_standard_frame = true;
      toplevel->Init(std::move(toplevel_params));
      return toplevel;
    }
  
    void GenerateAndDispatchMouseEvent(ui::EventType event_type,
                                       const gfx::Point& click_location,
                                       int flags) {
      DCHECK(host_);
      std::unique_ptr<ui::MouseEvent> mouse_event(
          GenerateMouseEvent(event_type, click_location, flags));
      host_->DispatchEvent(mouse_event.get());
    }
  
    void GenerateAndDispatchClickMouseEvent(const gfx::Point& click_location,
                                            int flags) {
      DCHECK(host_);
      GenerateAndDispatchMouseEvent(ui::ET_MOUSE_PRESSED, click_location, flags);
      GenerateAndDispatchMouseEvent(ui::ET_MOUSE_RELEASED, click_location, flags);
    }
  
    ui::MouseEvent* GenerateMouseEvent(ui::EventType event_type,
                                       const gfx::Point& click_location,
                                       int flags) {
      int flag = 0;
      if (flags & ui::EF_LEFT_MOUSE_BUTTON)
        flag = ui::EF_LEFT_MOUSE_BUTTON;
      else if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
        flag = ui::EF_RIGHT_MOUSE_BUTTON;
  
      if (!flag) {
        NOTREACHED()
            << "Other mouse clicks are not supported yet. Add the new one.";
      }
      return new ui::MouseEvent(event_type, click_location, click_location,
                                base::TimeTicks::Now(), flags, flag);
    }
  
    HitTestWidgetDelegate* delegate_ = nullptr;
    TestDesktopWindowTreeHostLinux* host_ = nullptr;
  
   private:
    DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostLinuxTest);
  };
  
  TEST_F(DesktopWindowTreeHostLinuxTest, HitTest) {
    gfx::Rect bounds(0, 0, 100, 100);
    std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds));
    widget->Show();
  
    // Install a fake move/resize handler to intercept the move/resize call.
    auto handler =
        std::make_unique<FakeWmMoveResizeHandler>(host_->platform_window());
    host_->DestroyNonClientEventFilter();
    host_->non_client_window_event_filter_ =
        std::make_unique<WindowEventFilterLinux>(host_, handler.get());
  
    // It is not important to use pointer locations corresponding to the hittests
    // values used in the browser itself, because we fake the hit test results,
    // which non client frame view sends back. Thus, just make sure the content
    // window is able to receive these events.
    gfx::Point pointer_location_in_px(10, 10);
  
    constexpr int hittest_values[] = {
        HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION,     HTLEFT,
        HTRIGHT,  HTTOP,        HTTOPLEFT,     HTTOPRIGHT,    HTNOWHERE,
        HTBORDER, HTCLIENT,     HTCLOSE,       HTERROR,       HTGROWBOX,
        HTHELP,   HTHSCROLL,    HTMENU,        HTMAXBUTTON,   HTMINBUTTON,
        HTREDUCE, HTSIZE,       HTSYSMENU,     HTTRANSPARENT, HTVSCROLL,
        HTZOOM,
    };
  
    aura::Window* window = widget->GetNativeWindow();
    auto* frame_view = delegate_->frame_view();
    for (int hittest : hittest_values) {
      handler->Reset();
  
      // Set the desired hit test result value, which will be returned, when
      // WindowEventFilter starts to perform hit testing.
      frame_view->set_hit_test_result(hittest);
  
      gfx::Rect bounds = window->GetBoundsInScreen();
  
      // The wm move/resize handler receives pointer location in the global screen
      // coordinate, whereas event dispatcher receives event locations on a local
      // system coordinate. Thus, add an offset of a new possible origin value of
      // a window to the expected pointer location.
      gfx::Point expected_pointer_location_in_px(pointer_location_in_px);
      expected_pointer_location_in_px.Offset(bounds.x(), bounds.y());
  
      if (hittest == HTCAPTION) {
        // Move the window on HTCAPTION hit test value.
        bounds =
            gfx::Rect(gfx::Point(bounds.x() + 2, bounds.y() + 4), bounds.size());
        handler->set_bounds(bounds);
      } else if (IsNonClientComponent(hittest)) {
        // Resize the window on other than HTCAPTION non client hit test values.
        bounds = gfx::Rect(
            gfx::Point(bounds.origin()),
            gfx::Size(bounds.size().width() + 5, bounds.size().height() + 10));
        handler->set_bounds(bounds);
      }
  
      // Send mouse down event and make sure the WindowEventFilter calls the
      // move/resize handler to start interactive move/resize with the |hittest|
      // value we specified.
      GenerateAndDispatchMouseEvent(ui::ET_MOUSE_PRESSED, pointer_location_in_px,
                                    ui::EF_LEFT_MOUSE_BUTTON);
  
      // The test expectation is based on the hit test component. If it is a
      // non-client component, which results in a call to move/resize, the
      // handler must receive the hittest value and the pointer location in
      // global screen coordinate system. In other cases, it must not.
      SetExpectationBasedOnHittestValue(hittest, *handler.get(),
                                        expected_pointer_location_in_px);
      // Make sure the bounds of the content window are correct.
      EXPECT_EQ(window->GetBoundsInScreen().ToString(), bounds.ToString());
  
      // Dispatch mouse release event to release a mouse pressed handler and be
      // able to consume future events.
      GenerateAndDispatchMouseEvent(ui::ET_MOUSE_RELEASED, pointer_location_in_px,
                                    ui::EF_LEFT_MOUSE_BUTTON);
    }
  }
  
  // Tests that the window is maximized in response to a double click event.
  TEST_F(DesktopWindowTreeHostLinuxTest, DoubleClickHeaderMaximizes) {
    gfx::Rect bounds(0, 0, 100, 100);
    std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds));
    widget->Show();
  
    aura::Window* window = widget->GetNativeWindow();
    window->SetProperty(aura::client::kResizeBehaviorKey,
                        aura::client::kResizeBehaviorCanMaximize);
  
    RunPendingMessages();
  
    host_->ResetCalledMaximize();
  
    auto* frame_view = delegate_->frame_view();
    // Set the desired hit test result value, which will be returned, when
    // WindowEventFilter starts to perform hit testing.
    frame_view->set_hit_test_result(HTCAPTION);
  
    host_->ResetCalledMaximize();
  
    int flags = ui::EF_LEFT_MOUSE_BUTTON;
    GenerateAndDispatchClickMouseEvent(gfx::Point(), flags);
    flags |= ui::EF_IS_DOUBLE_CLICK;
    GenerateAndDispatchClickMouseEvent(gfx::Point(), flags);
  
    EXPECT_TRUE(host_->called_maximize());
  
    widget->CloseNow();
  }
  
  // Tests that the window does not maximize in response to a double click event,
  // if the first click was to a different target component than that of the
  // second click.
  TEST_F(DesktopWindowTreeHostLinuxTest,
         DoubleClickTwoDifferentTargetsDoesntMaximizes) {
    gfx::Rect bounds(0, 0, 100, 100);
    std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds));
    widget->Show();
  
    aura::Window* window = widget->GetNativeWindow();
    window->SetProperty(aura::client::kResizeBehaviorKey,
                        aura::client::kResizeBehaviorCanMaximize);
  
    RunPendingMessages();
  
    host_->ResetCalledMaximize();
  
    auto* frame_view = delegate_->frame_view();
  
    frame_view->set_hit_test_result(HTCLIENT);
    int flags = ui::EF_LEFT_MOUSE_BUTTON;
    GenerateAndDispatchClickMouseEvent(gfx::Point(), flags);
  
    frame_view->set_hit_test_result(HTCLIENT);
    flags |= ui::EF_IS_DOUBLE_CLICK;
    GenerateAndDispatchClickMouseEvent(gfx::Point(), flags);
  
    EXPECT_FALSE(host_->called_maximize());
  
    widget->CloseNow();
  }
  
  // Tests that the window does not maximize in response to a double click event,
  // if the double click was interrupted by a right click.
  TEST_F(DesktopWindowTreeHostLinuxTest,
         RightClickDuringDoubleClickDoesntMaximize) {
    gfx::Rect bounds(0, 0, 100, 100);
    std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds));
    widget->Show();
  
    aura::Window* window = widget->GetNativeWindow();
    window->SetProperty(aura::client::kResizeBehaviorKey,
                        aura::client::kResizeBehaviorCanMaximize);
  
    RunPendingMessages();
  
    host_->ResetCalledMaximize();
  
    auto* frame_view = delegate_->frame_view();
  
    frame_view->set_hit_test_result(HTCLIENT);
    int flags_left_button = ui::EF_LEFT_MOUSE_BUTTON;
    GenerateAndDispatchClickMouseEvent(gfx::Point(), flags_left_button);
  
    frame_view->set_hit_test_result(HTCAPTION);
    GenerateAndDispatchClickMouseEvent(gfx::Point(), ui::EF_RIGHT_MOUSE_BUTTON);
    EXPECT_FALSE(host_->called_maximize());
  
    flags_left_button |= ui::EF_IS_DOUBLE_CLICK;
    GenerateAndDispatchClickMouseEvent(gfx::Point(), flags_left_button);
    EXPECT_FALSE(host_->called_maximize());
  
    widget->CloseNow();
  }
  
  #if defined(USE_X11)
  // Test that calling Widget::Deactivate() sets the widget as inactive wrt to
  // 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(DesktopWindowTreeHostLinuxTest, Deactivate) {
    std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
  
    ActivationWaiter waiter(static_cast<x11::Window>(
        widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()));
    widget->Show();
    widget->Activate();
    waiter.Wait();
  
    widget->Deactivate();
    // Regardless of whether |widget|'s X11 window eventually gets deactivated,
    // |widget|'s "active" state should change.
    EXPECT_FALSE(widget->IsActive());
  
    // |widget|'s X11 window should still be active. Reactivating |widget| should
    // update the widget's "active" state.
    // Note: Activating a widget whose X11 window is not active does not
    // synchronously update the widget's "active" state.
    widget->Activate();
    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(DesktopWindowTreeHostLinuxTest, CaptureEventForwarding) {
    std::unique_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100)));
    aura::Window* window1 = widget1->GetNativeWindow();
    DesktopWindowTreeHostLinux* host1 =
        static_cast<DesktopWindowTreeHostLinux*>(window1->GetHost());
    widget1->Show();
  
    std::unique_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100)));
    aura::Window* window2 = widget2->GetNativeWindow();
    DesktopWindowTreeHostLinux* host2 =
        static_cast<DesktopWindowTreeHostLinux*>(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(nullptr);
    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(nullptr);
    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);
  }
  
  TEST_F(DesktopWindowTreeHostLinuxTest, InputMethodFocus) {
    std::unique_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
  
    // Waiter should be created as early as possible so that PropertyNotify has
    // time to be set before widget is activated.
    ActivationWaiter waiter(static_cast<x11::Window>(
        widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()));
  
    std::unique_ptr<Textfield> textfield(new Textfield);
    textfield->SetBounds(0, 0, 200, 20);
    widget->GetRootView()->AddChildView(textfield.get());
    widget->ShowInactive();
    textfield->RequestFocus();
  
    EXPECT_FALSE(widget->IsActive());
    // TODO(shuchen): uncomment the below check once the
    // "default-focused-input-method" logic is removed in aura::WindowTreeHost.
    // EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
    //           widget->GetInputMethod()->GetTextInputType());
  
    widget->Activate();
    waiter.Wait();
  
    EXPECT_TRUE(widget->IsActive());
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
              widget->GetInputMethod()->GetTextInputType());
  
    widget->Deactivate();
  
    EXPECT_FALSE(widget->IsActive());
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
              widget->GetInputMethod()->GetTextInputType());
  }
  
  #endif  // defined(USE_X11)
  
  }  // namespace views
  