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/chromeos/touch_exploration_controller.h"
6
7#include "ash/accessibility_delegate.h"
8#include "ash/shell.h"
9#include "ash/test/ash_test_base.h"
10#include "base/test/simple_test_tick_clock.h"
11#include "base/time/time.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/tabs/tab_strip_model.h"
14#include "chrome/test/base/in_process_browser_test.h"
15#include "chrome/test/base/ui_test_utils.h"
16#include "content/public/test/browser_test_utils.h"
17#include "ui/aura/client/cursor_client.h"
18#include "ui/aura/window_tree_host.h"
19#include "ui/compositor/compositor.h"
20#include "ui/compositor/test/draw_waiter_for_test.h"
21#include "ui/events/event.h"
22#include "ui/events/event_utils.h"
23#include "ui/events/test/event_generator.h"
24#include "ui/events/test/test_event_handler.h"
25
26namespace ui {
27
28class TouchExplorationTest : public InProcessBrowserTest {
29 public:
30  TouchExplorationTest() : simulated_clock_(new base::SimpleTestTickClock()) {
31    // Tests fail if time is ever 0.
32    simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(10));
33  }
34  virtual ~TouchExplorationTest() {}
35
36 protected:
37  virtual void SetUpOnMainThread() OVERRIDE {
38    // The RenderView for WebContents is created as a result of the
39    // navigation to the New Tab page which is done as part of the test
40    // SetUp. The creation involves sending a resize message to the renderer
41    // process. Here we wait for the resize ack to be received, because
42    // currently WindowEventDispatcher has code to hold touch and mouse
43    // move events until resize is complete (crbug.com/384342) which
44    // interferes with this test.
45    content::WebContents* web_contents =
46        browser()->tab_strip_model()->GetActiveWebContents();
47    content::WaitForResizeComplete(web_contents);
48    root_window_ = ash::Shell::GetInstance()->GetPrimaryRootWindow();
49    event_handler_.reset(new ui::test::TestEventHandler());
50    root_window_->AddPreTargetHandler(event_handler_.get());
51  }
52
53  virtual void TearDownOnMainThread() OVERRIDE {
54    SwitchTouchExplorationMode(false);
55    root_window_->RemovePreTargetHandler(event_handler_.get());
56  }
57
58  void SwitchTouchExplorationMode(bool on) {
59    ash::AccessibilityDelegate* ad =
60        ash::Shell::GetInstance()->accessibility_delegate();
61    if (on != ad->IsSpokenFeedbackEnabled())
62      ad->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE);
63  }
64
65  base::TimeDelta Now() {
66    return base::TimeDelta::FromInternalValue(
67        simulated_clock_->NowTicks().ToInternalValue());
68  }
69
70  ui::GestureDetector::Config gesture_detector_config_;
71  base::SimpleTestTickClock* simulated_clock_;
72  aura::Window* root_window_;
73  scoped_ptr<ui::test::TestEventHandler> event_handler_;
74
75private:
76  DISALLOW_COPY_AND_ASSIGN(TouchExplorationTest);
77};
78
79// This test turns the touch exploration mode off and confirms that events
80// aren't modified.
81IN_PROC_BROWSER_TEST_F(TouchExplorationTest, NoRewritingEventsWhenOff) {
82  SwitchTouchExplorationMode(false);
83  ui::test::EventGenerator generator(root_window_);
84
85  base::TimeDelta initial_time = Now();
86  ui::TouchEvent initial_press(
87      ui::ET_TOUCH_PRESSED, gfx::Point(99, 200), 1, initial_time);
88  generator.Dispatch(&initial_press);
89
90  // Since the touch exploration controller doesn't know if the user is
91  // double-tapping or not, touch exploration is only initiated if the
92  // 300 ms has elapsed and the finger does not move fast enough to begin
93  // gestures. Here, the touch move event is not important as a move, but
94  // a way to create time advancement.
95  ui::TouchEvent touch_time_advance(ui::ET_TOUCH_MOVED,
96                            gfx::Point(100, 200),
97                            1,
98                            initial_time +
99                                gesture_detector_config_.double_tap_timeout +
100                                base::TimeDelta::FromMilliseconds(1));
101  generator.Dispatch(&touch_time_advance);
102
103  EXPECT_EQ(0, event_handler_->num_mouse_events());
104  EXPECT_EQ(2, event_handler_->num_touch_events());
105  event_handler_->Reset();
106
107  generator.MoveTouchId(gfx::Point(11, 12), 1);
108  EXPECT_EQ(0, event_handler_->num_mouse_events());
109  EXPECT_EQ(1, event_handler_->num_touch_events());
110  event_handler_->Reset();
111
112  initial_time = Now();
113  ui::TouchEvent second_initial_press(
114      ui::ET_TOUCH_PRESSED, gfx::Point(499, 600), 2, initial_time);
115  generator.Dispatch(&second_initial_press);
116  ui::TouchEvent second_touch_time_advance(
117      ui::ET_TOUCH_MOVED,
118      gfx::Point(500, 600),
119      2,
120      initial_time + gesture_detector_config_.double_tap_timeout +
121          base::TimeDelta::FromMilliseconds(1));
122  generator.Dispatch(&second_touch_time_advance);
123  EXPECT_EQ(0, event_handler_->num_mouse_events());
124  EXPECT_EQ(2, event_handler_->num_touch_events());
125}
126
127// This test turns the touch exploration mode on and confirms that events get
128// rewritten.
129IN_PROC_BROWSER_TEST_F(TouchExplorationTest, RewritesEventsWhenOn) {
130  SwitchTouchExplorationMode(true);
131  ui::test::EventGenerator generator(root_window_);
132
133  base::TimeDelta initial_time = Now();
134  ui::TouchEvent initial_press(
135      ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), 1, initial_time);
136  generator.Dispatch(&initial_press);
137
138  // Since the touch exploration controller doesn't know if the user is
139  // double-tapping or not, touch exploration is only initiated if the
140  // 300 ms has elapsed and the finger does not move fast enough to begin
141  // gestures. Here, the touch move event is not important as a move, but
142  // a way to create time advancement.
143  ui::TouchEvent touch_time_advance(ui::ET_TOUCH_MOVED,
144                            gfx::Point(100, 200),
145                            1,
146                            initial_time +
147                                gesture_detector_config_.double_tap_timeout +
148                                base::TimeDelta::FromMilliseconds(1));
149  generator.Dispatch(&touch_time_advance);
150
151  // Number of mouse events may be greater than 1 because of ET_MOUSE_ENTERED.
152  EXPECT_GT(event_handler_->num_mouse_events(), 0);
153  EXPECT_EQ(0, event_handler_->num_touch_events());
154  event_handler_->Reset();
155
156  initial_time = Now();
157  ui::TouchEvent second_initial_press(
158      ui::ET_TOUCH_PRESSED, gfx::Point(500, 600), 2, initial_time);
159  generator.Dispatch(&second_initial_press);
160  ui::TouchEvent second_touch_time_advance(
161      ui::ET_TOUCH_MOVED,
162      gfx::Point(500, 600),
163      2,
164      initial_time + gesture_detector_config_.double_tap_timeout +
165          base::TimeDelta::FromMilliseconds(1));
166  generator.Dispatch(&second_touch_time_advance);
167  EXPECT_GT(event_handler_->num_mouse_events(), 0);
168  EXPECT_EQ(1, event_handler_->num_touch_events());
169  event_handler_->Reset();
170
171  // Stop the pending long press event. In some configurations, shutting down
172  // the browser can take longer than the long press timeout, and a long press
173  // event can come after the browser is already partly shut down, which causes
174  // the test to crash.
175  ui::TouchEvent release_second_touch(
176      ui::ET_TOUCH_RELEASED,
177      gfx::Point(500, 600),
178      2,
179      initial_time + gesture_detector_config_.double_tap_timeout +
180          base::TimeDelta::FromMilliseconds(1));
181  generator.Dispatch(&release_second_touch);
182  EXPECT_GT(event_handler_->num_mouse_events(), 0);
183  EXPECT_EQ(1, event_handler_->num_touch_events());
184}
185
186// This test makes sure that after the user clicks with split tap,
187// they continue to touch exploration mode if the original touch exploration
188// finger is still on the screen.
189IN_PROC_BROWSER_TEST_F(TouchExplorationTest, SplitTapExplore) {
190  SwitchTouchExplorationMode(true);
191  ui::test::EventGenerator generator(root_window_);
192  aura::client::CursorClient* cursor_client =
193      aura::client::GetCursorClient(root_window_);
194
195  // Mouse events should show the cursor.
196  generator.MoveMouseTo(gfx::Point(30, 31));
197  EXPECT_TRUE(cursor_client->IsMouseEventsEnabled());
198  EXPECT_TRUE(cursor_client->IsCursorVisible());
199
200  // The cursor should be shown immediately after the  press, and hidden
201  // after the move.
202  base::TimeDelta initial_time = Now();
203  ui::TouchEvent initial_press(
204      ui::ET_TOUCH_PRESSED, gfx::Point(100, 200), 1, initial_time);
205  generator.Dispatch(&initial_press);
206  EXPECT_TRUE(cursor_client->IsMouseEventsEnabled());
207  EXPECT_TRUE(cursor_client->IsCursorVisible());
208
209  // Initiate touch explore by waiting for the tap timer timeout. Time is
210  // advanced by sending a move event after the timeout period.
211  ui::TouchEvent touch_time_advance(
212      ui::ET_TOUCH_MOVED,
213      gfx::Point(100, 200),
214      1,
215      initial_time + gesture_detector_config_.double_tap_timeout +
216          base::TimeDelta::FromMilliseconds(1));
217  generator.Dispatch(&touch_time_advance);
218  EXPECT_TRUE(cursor_client->IsMouseEventsEnabled());
219  EXPECT_FALSE(cursor_client->IsCursorVisible());
220  event_handler_->Reset();
221
222  // Press and release with a second finger for split tap. This should send
223  // touch press and release events which should send a click press and release.
224  // Once the press is passed through, mouse events should be disabled.
225  // Mouse events are reenabled after the release.
226  generator.set_current_location(gfx::Point(102, 202));
227  generator.PressTouchId(2);
228  EXPECT_FALSE(cursor_client->IsMouseEventsEnabled());
229  EXPECT_FALSE(cursor_client->IsCursorVisible());
230  generator.ReleaseTouchId(2);
231  EXPECT_TRUE(cursor_client->IsMouseEventsEnabled());
232  EXPECT_FALSE(cursor_client->IsCursorVisible());
233  EXPECT_EQ(2, event_handler_->num_touch_events());
234  event_handler_->Reset();
235
236  // Continuing to move the touch exploration finger should send more mouse
237  // events.
238  generator.MoveTouchId(gfx::Point(509, 609), 1);
239  EXPECT_EQ(0, event_handler_->num_touch_events());
240  EXPECT_TRUE(cursor_client->IsMouseEventsEnabled());
241  EXPECT_FALSE(cursor_client->IsCursorVisible());
242}
243
244}  // namespace ui
245