1// Copyright (c) 2012 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/wm/core/compound_event_filter.h"
6
7#include "ui/aura/client/cursor_client.h"
8#include "ui/aura/env.h"
9#include "ui/aura/test/aura_test_base.h"
10#include "ui/aura/test/test_cursor_client.h"
11#include "ui/aura/test/test_windows.h"
12#include "ui/aura/window.h"
13#include "ui/aura/window_event_dispatcher.h"
14#include "ui/events/event.h"
15#include "ui/events/event_utils.h"
16#include "ui/events/test/event_generator.h"
17#include "ui/wm/core/default_activation_client.h"
18#include "ui/wm/public/activation_client.h"
19
20namespace {
21
22#if defined(OS_CHROMEOS) || defined(OS_WIN)
23base::TimeDelta GetTime() {
24  return ui::EventTimeForNow();
25}
26#endif  // defined(OS_CHROMEOS) || defined(OS_WIN)
27
28}
29
30namespace wm {
31
32namespace {
33
34// An event filter that consumes all gesture events.
35class ConsumeGestureEventFilter : public ui::EventHandler {
36 public:
37  ConsumeGestureEventFilter() {}
38  virtual ~ConsumeGestureEventFilter() {}
39
40 private:
41  // Overridden from ui::EventHandler:
42  virtual void OnGestureEvent(ui::GestureEvent* e) OVERRIDE {
43    e->StopPropagation();
44  }
45
46  DISALLOW_COPY_AND_ASSIGN(ConsumeGestureEventFilter);
47};
48
49}  // namespace
50
51typedef aura::test::AuraTestBase CompoundEventFilterTest;
52
53#if defined(OS_CHROMEOS)
54// A keypress only hides the cursor on ChromeOS (crbug.com/304296).
55TEST_F(CompoundEventFilterTest, CursorVisibilityChange) {
56  scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
57  aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
58  aura::test::TestWindowDelegate delegate;
59  scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
60      gfx::Rect(5, 5, 100, 100), root_window()));
61  window->Show();
62  window->SetCapture();
63
64  aura::test::TestCursorClient cursor_client(root_window());
65
66  // Send key event to hide the cursor.
67  ui::KeyEvent key('a', ui::VKEY_A, ui::EF_NONE);
68  DispatchEventUsingWindowDispatcher(&key);
69  EXPECT_FALSE(cursor_client.IsCursorVisible());
70
71  // Synthesized mouse event should not show the cursor.
72  ui::MouseEvent enter(ui::ET_MOUSE_ENTERED, gfx::Point(10, 10),
73                       gfx::Point(10, 10), 0, 0);
74  enter.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
75  DispatchEventUsingWindowDispatcher(&enter);
76  EXPECT_FALSE(cursor_client.IsCursorVisible());
77
78  ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
79                      gfx::Point(10, 10), 0, 0);
80  move.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
81  DispatchEventUsingWindowDispatcher(&move);
82  EXPECT_FALSE(cursor_client.IsCursorVisible());
83
84  // A real mouse event should show the cursor.
85  ui::MouseEvent real_move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
86                           gfx::Point(10, 10), 0, 0);
87  DispatchEventUsingWindowDispatcher(&real_move);
88  EXPECT_TRUE(cursor_client.IsCursorVisible());
89
90  // Disallow hiding the cursor on keypress.
91  cursor_client.set_should_hide_cursor_on_key_event(false);
92  key = ui::KeyEvent('a', ui::VKEY_A, ui::EF_NONE);
93  DispatchEventUsingWindowDispatcher(&key);
94  EXPECT_TRUE(cursor_client.IsCursorVisible());
95
96  // Allow hiding the cursor on keypress.
97  cursor_client.set_should_hide_cursor_on_key_event(true);
98  key = ui::KeyEvent('a', ui::VKEY_A, ui::EF_NONE);
99  DispatchEventUsingWindowDispatcher(&key);
100  EXPECT_FALSE(cursor_client.IsCursorVisible());
101
102  // Mouse synthesized exit event should not show the cursor.
103  ui::MouseEvent exit(ui::ET_MOUSE_EXITED, gfx::Point(10, 10),
104                      gfx::Point(10, 10), 0, 0);
105  exit.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
106  DispatchEventUsingWindowDispatcher(&exit);
107  EXPECT_FALSE(cursor_client.IsCursorVisible());
108
109  aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
110}
111#endif  // defined(OS_CHROMEOS)
112
113#if defined(OS_CHROMEOS) || defined(OS_WIN)
114// Touch visually hides the cursor on ChromeOS and Windows.
115TEST_F(CompoundEventFilterTest, TouchHidesCursor) {
116  new wm::DefaultActivationClient(root_window());
117  scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
118  aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
119  aura::test::TestWindowDelegate delegate;
120  scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
121      gfx::Rect(5, 5, 100, 100), root_window()));
122  window->Show();
123  window->SetCapture();
124
125  aura::test::TestCursorClient cursor_client(root_window());
126
127  ui::MouseEvent mouse0(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
128                        gfx::Point(10, 10), 0, 0);
129  DispatchEventUsingWindowDispatcher(&mouse0);
130  EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
131
132  // This press is required for the GestureRecognizer to associate a target
133  // with kTouchId
134  ui::TouchEvent press0(
135      ui::ET_TOUCH_PRESSED, gfx::Point(90, 90), 1, GetTime());
136  DispatchEventUsingWindowDispatcher(&press0);
137  EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
138
139  ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 10), 1, GetTime());
140  DispatchEventUsingWindowDispatcher(&move);
141  EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
142
143  ui::TouchEvent release(
144      ui::ET_TOUCH_RELEASED, gfx::Point(10, 10), 1, GetTime());
145  DispatchEventUsingWindowDispatcher(&release);
146  EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
147
148  ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
149                        gfx::Point(10, 10), 0, 0);
150  // Move the cursor again. The cursor should be visible.
151  DispatchEventUsingWindowDispatcher(&mouse1);
152  EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
153
154  // Now activate the window and press on it again.
155  ui::TouchEvent press1(
156      ui::ET_TOUCH_PRESSED, gfx::Point(90, 90), 1, GetTime());
157  aura::client::GetActivationClient(
158      root_window())->ActivateWindow(window.get());
159  DispatchEventUsingWindowDispatcher(&press1);
160  EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
161  aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
162}
163#endif  // defined(OS_CHROMEOS) || defined(OS_WIN)
164
165// Tests that if an event filter consumes a gesture, then it doesn't focus the
166// window.
167TEST_F(CompoundEventFilterTest, FilterConsumedGesture) {
168  scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
169  scoped_ptr<ui::EventHandler> gesure_handler(new ConsumeGestureEventFilter);
170  compound_filter->AddHandler(gesure_handler.get());
171  aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
172  aura::test::TestWindowDelegate delegate;
173  DCHECK(root_window());
174  scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
175      gfx::Rect(5, 5, 100, 100), root_window()));
176  window->Show();
177
178  EXPECT_TRUE(window->CanFocus());
179  EXPECT_FALSE(window->HasFocus());
180
181  // Tap on the window should not focus it since the filter will be consuming
182  // the gestures.
183  ui::test::EventGenerator generator(root_window(), gfx::Point(50, 50));
184  generator.PressTouch();
185  EXPECT_FALSE(window->HasFocus());
186
187  compound_filter->RemoveHandler(gesure_handler.get());
188  aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
189}
190
191// Verifies we don't attempt to hide the mouse when the mouse is down and a
192// touch event comes in.
193TEST_F(CompoundEventFilterTest, DontHideWhenMouseDown) {
194  ui::test::EventGenerator event_generator(root_window());
195
196  scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
197  aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
198  aura::test::TestWindowDelegate delegate;
199  scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
200      gfx::Rect(5, 5, 100, 100), root_window()));
201  window->Show();
202
203  aura::test::TestCursorClient cursor_client(root_window());
204
205  // Move and press the mouse over the window.
206  event_generator.MoveMouseTo(10, 10);
207  EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
208  event_generator.PressLeftButton();
209  EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
210  EXPECT_TRUE(aura::Env::GetInstance()->IsMouseButtonDown());
211
212  // Do a touch event. As the mouse button is down this should not disable mouse
213  // events.
214  event_generator.PressTouch();
215  EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
216  aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
217}
218
219#if defined(OS_WIN)
220// Windows synthesizes mouse messages for touch events. We should not be
221// showing the cursor when we receive such messages.
222TEST_F(CompoundEventFilterTest, DontShowCursorOnMouseMovesFromTouch) {
223  scoped_ptr<CompoundEventFilter> compound_filter(new CompoundEventFilter);
224  aura::Env::GetInstance()->AddPreTargetHandler(compound_filter.get());
225  aura::test::TestWindowDelegate delegate;
226  scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(&delegate, 1234,
227      gfx::Rect(5, 5, 100, 100), root_window()));
228  window->Show();
229  window->SetCapture();
230
231  aura::test::TestCursorClient cursor_client(root_window());
232  cursor_client.DisableMouseEvents();
233  EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
234
235  ui::MouseEvent mouse0(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
236                        gfx::Point(10, 10), 0, 0);
237  mouse0.set_flags(mouse0.flags() | ui::EF_FROM_TOUCH);
238
239  DispatchEventUsingWindowDispatcher(&mouse0);
240  EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
241
242  mouse0.set_flags(mouse0.flags() & ~ui::EF_FROM_TOUCH);
243  DispatchEventUsingWindowDispatcher(&mouse0);
244  EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
245
246  aura::Env::GetInstance()->RemovePreTargetHandler(compound_filter.get());
247}
248#endif
249
250}  // namespace wm
251