1 // Copyright 2013 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/test/ui_controls_factory_desktop_aurax11.h"
6 
7 #include <utility>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/check_op.h"
12 #include "base/location.h"
13 #include "base/macros.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "ui/aura/client/screen_position_client.h"
17 #include "ui/aura/env.h"
18 #include "ui/aura/test/aura_test_utils.h"
19 #include "ui/aura/test/ui_controls_factory_aura.h"
20 #include "ui/aura/window_event_dispatcher.h"
21 #include "ui/base/test/ui_controls_aura.h"
22 #include "ui/base/x/test/x11_ui_controls_test_helper.h"
23 #include "ui/views/test/test_desktop_screen_x11.h"
24 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
25 
26 namespace views {
27 namespace test {
28 namespace {
29 
30 using ui_controls::DOWN;
31 using ui_controls::LEFT;
32 using ui_controls::MIDDLE;
33 using ui_controls::MouseButton;
34 using ui_controls::RIGHT;
35 using ui_controls::UIControlsAura;
36 using ui_controls::UP;
37 
38 class UIControlsDesktopX11 : public UIControlsAura {
39  public:
40   UIControlsDesktopX11() = default;
41   ~UIControlsDesktopX11() override = default;
42 
SendKeyPress(gfx::NativeWindow window,ui::KeyboardCode key,bool control,bool shift,bool alt,bool command)43   bool SendKeyPress(gfx::NativeWindow window,
44                     ui::KeyboardCode key,
45                     bool control,
46                     bool shift,
47                     bool alt,
48                     bool command) override {
49     DCHECK(!command);  // No command key on Aura
50     return SendKeyPressNotifyWhenDone(window, key, control, shift, alt, command,
51                                       base::OnceClosure());
52   }
53 
SendKeyPressNotifyWhenDone(gfx::NativeWindow window,ui::KeyboardCode key,bool control,bool shift,bool alt,bool command,base::OnceClosure closure)54   bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
55                                   ui::KeyboardCode key,
56                                   bool control,
57                                   bool shift,
58                                   bool alt,
59                                   bool command,
60                                   base::OnceClosure closure) override {
61     DCHECK(!command);  // No command key on Aura
62 
63     aura::WindowTreeHost* host = window->GetHost();
64     x11_ui_controls_test_helper_.SendKeyPressEvent(host->GetAcceleratedWidget(),
65                                                    key, control, shift, alt,
66                                                    command, std::move(closure));
67     return true;
68   }
69 
SendMouseMove(int screen_x,int screen_y)70   bool SendMouseMove(int screen_x, int screen_y) override {
71     return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::OnceClosure());
72   }
SendMouseMoveNotifyWhenDone(int screen_x,int screen_y,base::OnceClosure closure)73   bool SendMouseMoveNotifyWhenDone(int screen_x,
74                                    int screen_y,
75                                    base::OnceClosure closure) override {
76     gfx::Point screen_location(screen_x, screen_y);
77     gfx::Point root_location = screen_location;
78     aura::Window* root_window = RootWindowForPoint(screen_location);
79 
80     aura::client::ScreenPositionClient* screen_position_client =
81         aura::client::GetScreenPositionClient(root_window);
82     if (screen_position_client) {
83       screen_position_client->ConvertPointFromScreen(root_window,
84                                                      &root_location);
85     }
86 
87     aura::WindowTreeHost* host = root_window->GetHost();
88     gfx::Point root_current_location =
89         aura::test::QueryLatestMousePositionRequestInHost(host);
90     host->ConvertPixelsToDIP(&root_current_location);
91 
92     auto* screen = views::test::TestDesktopScreenX11::GetInstance();
93     DCHECK_EQ(screen, display::Screen::GetScreen());
94     screen->set_cursor_screen_point(gfx::Point(screen_x, screen_y));
95 
96     if (root_location != root_current_location &&
97         x11_ui_controls_test_helper_.ButtonDownMask() == 0) {
98       // Move the cursor because EnterNotify/LeaveNotify are generated with the
99       // current mouse position as a result of XGrabPointer()
100       root_window->MoveCursorTo(root_location);
101     } else {
102       gfx::Point screen_point(root_location);
103       host->ConvertDIPToScreenInPixels(&screen_point);
104       x11_ui_controls_test_helper_.SendMouseMotionNotifyEvent(
105           host->GetAcceleratedWidget(), root_location, screen_point,
106           std::move(closure));
107     }
108     x11_ui_controls_test_helper_.RunClosureAfterAllPendingUIEvents(
109         std::move(closure));
110     return true;
111   }
SendMouseEvents(MouseButton type,int button_state,int accelerator_state)112   bool SendMouseEvents(MouseButton type,
113                        int button_state,
114                        int accelerator_state) override {
115     return SendMouseEventsNotifyWhenDone(
116         type, button_state, base::OnceClosure(), accelerator_state);
117   }
SendMouseEventsNotifyWhenDone(MouseButton type,int button_state,base::OnceClosure closure,int accelerator_state)118   bool SendMouseEventsNotifyWhenDone(MouseButton type,
119                                      int button_state,
120                                      base::OnceClosure closure,
121                                      int accelerator_state) override {
122     gfx::Point mouse_loc = aura::Env::GetInstance()->last_mouse_location();
123     aura::Window* root_window = RootWindowForPoint(mouse_loc);
124     aura::client::ScreenPositionClient* screen_position_client =
125         aura::client::GetScreenPositionClient(root_window);
126     if (screen_position_client)
127       screen_position_client->ConvertPointFromScreen(root_window, &mouse_loc);
128 
129     gfx::Point mouse_root_loc = mouse_loc;
130     root_window->GetHost()->ConvertDIPToScreenInPixels(&mouse_root_loc);
131     x11_ui_controls_test_helper_.SendMouseEvent(
132         root_window->GetHost()->GetAcceleratedWidget(), type, button_state,
133         accelerator_state, mouse_loc, mouse_root_loc, std::move(closure));
134     return true;
135   }
SendMouseClick(MouseButton type)136   bool SendMouseClick(MouseButton type) override {
137     return SendMouseEvents(type, UP | DOWN, ui_controls::kNoAccelerator);
138   }
139 
140  private:
RootWindowForPoint(const gfx::Point & point)141   aura::Window* RootWindowForPoint(const gfx::Point& point) {
142     // Most interactive_ui_tests run inside of the aura_test_helper
143     // environment. This means that we can't rely on display::Screen and several
144     // other things to work properly. Therefore we hack around this by
145     // iterating across the windows owned DesktopWindowTreeHostLinux since this
146     // doesn't rely on having a DesktopScreenX11.
147     std::vector<aura::Window*> windows =
148         DesktopWindowTreeHostLinux::GetAllOpenWindows();
149     const auto i =
150         std::find_if(windows.cbegin(), windows.cend(), [point](auto* window) {
151           return window->GetBoundsInScreen().Contains(point) ||
152                  window->HasCapture();
153         });
154     DCHECK(i != windows.cend()) << "Couldn't find RW for " << point.ToString()
155                                 << " among " << windows.size() << " RWs.";
156     return (*i)->GetRootWindow();
157   }
158 
159   ui::X11UIControlsTestHelper x11_ui_controls_test_helper_;
160 
161   DISALLOW_COPY_AND_ASSIGN(UIControlsDesktopX11);
162 };
163 
164 }  // namespace
165 
CreateUIControlsDesktopAura()166 UIControlsAura* CreateUIControlsDesktopAura() {
167   return new UIControlsDesktopX11();
168 }
169 
170 }  // namespace test
171 }  // namespace views
172