1 // Copyright 2017 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 "base/run_loop.h"
6 #include "base/win/windows_version.h"
7 #include "build/build_config.h"
8 #include "chrome/browser/ui/views/test/view_event_test_base.h"
9 #include "chrome/test/base/testing_profile.h"
10 #include "ui/aura/env.h"
11 #include "ui/aura/test/env_test_helper.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_tree_host.h"
14 #include "ui/base/models/simple_menu_model.h"
15 #include "ui/base/test/ui_controls.h"
16 #include "ui/events/gestures/gesture_recognizer_impl.h"
17 #include "ui/views/controls/menu/menu_runner.h"
18 
19 namespace {
20 
21 class TouchEventHandler : public ui::EventHandler {
22  public:
TouchEventHandler()23   TouchEventHandler()
24       : call_depth_(0),
25         max_call_depth_(0),
26         num_touch_presses_(0),
27         num_pointers_down_(0),
28         recursion_enabled_(false) {}
29 
~TouchEventHandler()30   ~TouchEventHandler() override {}
31 
32   // OnTouchEvent will simulate a second touch event (at |touch_point|) to force
33   // recursion in event handling.
ForceRecursionInEventHandler(const gfx::Point & touch_point)34   void ForceRecursionInEventHandler(const gfx::Point& touch_point) {
35     recursion_enabled_ = true;
36     touch_point_ = touch_point;
37   }
38 
WaitForIdle()39   void WaitForIdle() {
40     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
41     run_loop.RunUntilIdle();
42   }
WaitForEvents()43   void WaitForEvents() {
44     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
45     quit_closure_ = run_loop.QuitClosure();
46     run_loop.Run();
47   }
48 
max_call_depth() const49   int max_call_depth() const { return max_call_depth_; }
50 
num_touch_presses() const51   int num_touch_presses() const { return num_touch_presses_; }
52 
num_pointers_down() const53   int num_pointers_down() const { return num_pointers_down_; }
54 
set_touch_point(const gfx::Point & pt)55   void set_touch_point(const gfx::Point& pt) { touch_point_ = pt; }
56 
57  private:
58   // ui::EventHandler:
OnTouchEvent(ui::TouchEvent * event)59   void OnTouchEvent(ui::TouchEvent* event) override {
60     max_call_depth_ = std::max(++call_depth_, max_call_depth_);
61 
62     if (recursion_enabled_ && (event->type() == ui::ET_TOUCH_RELEASED)) {
63       recursion_enabled_ = false;
64       ui_controls::SendTouchEvents(ui_controls::PRESS | ui_controls::PRESS, 1,
65                                    touch_point_.x(), touch_point_.y());
66       WaitForIdle();
67     }
68 
69     switch (event->type()) {
70       case ui::ET_TOUCH_PRESSED:
71         num_touch_presses_++;
72         num_pointers_down_++;
73         break;
74       case ui::ET_TOUCH_RELEASED:
75         num_pointers_down_--;
76         if (!quit_closure_.is_null() && num_pointers_down_ == 0) {
77           quit_closure_.Run();
78         }
79         break;
80       default:
81         break;
82     }
83     --call_depth_;
84   }
85 
86   int call_depth_;
87   int max_call_depth_;
88   int num_touch_presses_;
89   int num_pointers_down_;
90   base::Closure quit_closure_;
91   bool recursion_enabled_;
92   gfx::Point touch_point_;
93   DISALLOW_COPY_AND_ASSIGN(TouchEventHandler);
94 };
95 
96 class TestingGestureRecognizer : public ui::GestureRecognizerImpl {
97  public:
98   TestingGestureRecognizer() = default;
99   ~TestingGestureRecognizer() override = default;
100 
num_touch_press_events() const101   int num_touch_press_events() const { return num_touch_press_events_; }
num_touch_release_events() const102   int num_touch_release_events() const { return num_touch_release_events_; }
103 
104  protected:
105   // Overriden from GestureRecognizerImpl:
ProcessTouchEventPreDispatch(ui::TouchEvent * event,ui::GestureConsumer * consumer)106   bool ProcessTouchEventPreDispatch(ui::TouchEvent* event,
107                                     ui::GestureConsumer* consumer) override {
108     switch (event->type()) {
109       case ui::ET_TOUCH_PRESSED:
110         num_touch_press_events_++;
111         break;
112       case ui::ET_TOUCH_RELEASED:
113         num_touch_release_events_++;
114         break;
115       default:
116         break;
117     }
118 
119     return ui::GestureRecognizerImpl::ProcessTouchEventPreDispatch(event,
120                                                                    consumer);
121   }
122 
123  private:
124   int num_touch_press_events_ = 0;
125   int num_touch_release_events_ = 0;
126   DISALLOW_COPY_AND_ASSIGN(TestingGestureRecognizer);
127 };
128 
129 }  // namespace
130 
131 class TouchEventsViewTest : public ViewEventTestBase {
132  public:
133   TouchEventsViewTest() = default;
134 
135   // ViewEventTestBase:
SetUp()136   void SetUp() override {
137     ViewEventTestBase::SetUp();
138 
139     auto gesture_recognizer = std::make_unique<TestingGestureRecognizer>();
140     gesture_recognizer_ = gesture_recognizer.get();
141     aura::test::EnvTestHelper().SetGestureRecognizer(
142         std::move(gesture_recognizer));
143   }
144 
CreateContentsView()145   std::unique_ptr<views::View> CreateContentsView() override {
146     auto touch_view = std::make_unique<views::View>();
147     touch_view_ = touch_view.get();
148     return touch_view;
149   }
150 
GetPreferredSizeForContents() const151   gfx::Size GetPreferredSizeForContents() const override {
152     return gfx::Size(600, 600);
153   }
154 
DoTestOnMessageLoop()155   void DoTestOnMessageLoop() override {
156     // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
157     // on Windows 8 and up.
158     if (base::win::GetVersion() <= base::win::Version::WIN7) {
159       Done();
160       return;
161     }
162 
163     const int touch_pointer_count = 3;
164     TouchEventHandler touch_event_handler;
165     window()->GetNativeWindow()->GetHost()->window()->AddPreTargetHandler(
166         &touch_event_handler);
167     gfx::Point in_content(touch_view_->width() / 2, touch_view_->height() / 2);
168     views::View::ConvertPointToScreen(touch_view_, &in_content);
169 
170     ASSERT_TRUE(ui_controls::SendTouchEvents(ui_controls::PRESS,
171                                              touch_pointer_count,
172                                              in_content.x(), in_content.y()));
173     touch_event_handler.WaitForIdle();
174     touch_event_handler.WaitForEvents();
175     EXPECT_EQ(touch_pointer_count, touch_event_handler.num_touch_presses());
176     EXPECT_EQ(0, touch_event_handler.num_pointers_down());
177 
178     EXPECT_EQ(touch_pointer_count,
179               gesture_recognizer_->num_touch_press_events());
180     EXPECT_EQ(touch_pointer_count,
181               gesture_recognizer_->num_touch_release_events());
182 
183     window()->GetNativeWindow()->GetHost()->window()->RemovePreTargetHandler(
184         &touch_event_handler);
185     Done();
186   }
187 
188  protected:
189   views::View* touch_view_ = nullptr;
190   TestingGestureRecognizer* gesture_recognizer_ = nullptr;
191   ui::GestureRecognizer* initial_gr_ = nullptr;
192 
193   DISALLOW_COPY_AND_ASSIGN(TouchEventsViewTest);
194 };
195 
196 #if defined(OS_WIN)  // Fails on latest versions of Windows.
197                      // https://crbug.com/1108551.
198 #define MAYBE_CheckWindowsNativeMessageForTouchEvents \
199   DISABLED_CheckWindowsNativeMessageForTouchEvents
200 #else
201 #define MAYBE_CheckWindowsNativeMessageForTouchEvents \
202   CheckWindowsNativeMessageForTouchEvents
203 #endif
204 VIEW_TEST(TouchEventsViewTest, MAYBE_CheckWindowsNativeMessageForTouchEvents)
205 
206 class TouchEventsRecursiveViewTest : public TouchEventsViewTest {
207  public:
TouchEventsRecursiveViewTest()208   TouchEventsRecursiveViewTest() {}
209 
DoTestOnMessageLoop()210   void DoTestOnMessageLoop() override {
211     // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
212     // on Windows 8 and up.
213     if (base::win::GetVersion() <= base::win::Version::WIN7) {
214       Done();
215       return;
216     }
217 
218     const int touch_pointer_count = 1;
219     TouchEventHandler touch_event_handler;
220     window()->GetNativeWindow()->GetHost()->window()->AddPreTargetHandler(
221         &touch_event_handler);
222     gfx::Point in_content(touch_view_->width() / 2, touch_view_->height() / 2);
223     views::View::ConvertPointToScreen(touch_view_, &in_content);
224     touch_event_handler.ForceRecursionInEventHandler(in_content);
225 
226     ASSERT_TRUE(ui_controls::SendTouchEvents(ui_controls::PRESS,
227                                              touch_pointer_count,
228                                              in_content.x(), in_content.y()));
229     touch_event_handler.WaitForEvents();
230 
231     EXPECT_EQ(touch_pointer_count + 1, touch_event_handler.num_touch_presses());
232     EXPECT_EQ(0, touch_event_handler.num_pointers_down());
233     EXPECT_EQ(2, touch_event_handler.max_call_depth());
234     window()->GetNativeWindow()->GetHost()->window()->RemovePreTargetHandler(
235         &touch_event_handler);
236     Done();
237   }
238 
239  private:
240   DISALLOW_COPY_AND_ASSIGN(TouchEventsRecursiveViewTest);
241 };
242 
243 #if defined(OS_WIN)  // Fails on latest versions of Windows.
244                      // https://crbug.com/1108551.
245 #define MAYBE_CheckWindowsRecursiveHandler DISABLED_CheckWindowsRecursiveHandler
246 #else
247 #define MAYBE_CheckWindowsRecursiveHandler CheckWindowsRecursiveHandler
248 #endif
249 VIEW_TEST(TouchEventsRecursiveViewTest, MAYBE_CheckWindowsRecursiveHandler)
250