OLD | NEW |
| (Empty) |
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/aura/root_window.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/run_loop.h" | |
11 #include "testing/gtest/include/gtest/gtest.h" | |
12 #include "ui/aura/client/event_client.h" | |
13 #include "ui/aura/client/focus_client.h" | |
14 #include "ui/aura/env.h" | |
15 #include "ui/aura/test/aura_test_base.h" | |
16 #include "ui/aura/test/event_generator.h" | |
17 #include "ui/aura/test/test_cursor_client.h" | |
18 #include "ui/aura/test/test_event_handler.h" | |
19 #include "ui/aura/test/test_screen.h" | |
20 #include "ui/aura/test/test_window_delegate.h" | |
21 #include "ui/aura/test/test_windows.h" | |
22 #include "ui/aura/window.h" | |
23 #include "ui/aura/window_tracker.h" | |
24 #include "ui/base/hit_test.h" | |
25 #include "ui/events/event.h" | |
26 #include "ui/events/event_handler.h" | |
27 #include "ui/events/event_utils.h" | |
28 #include "ui/events/gestures/gesture_configuration.h" | |
29 #include "ui/events/keycodes/keyboard_codes.h" | |
30 #include "ui/gfx/point.h" | |
31 #include "ui/gfx/rect.h" | |
32 #include "ui/gfx/screen.h" | |
33 #include "ui/gfx/transform.h" | |
34 | |
35 namespace aura { | |
36 namespace { | |
37 | |
38 // A delegate that always returns a non-client component for hit tests. | |
39 class NonClientDelegate : public test::TestWindowDelegate { | |
40 public: | |
41 NonClientDelegate() | |
42 : non_client_count_(0), | |
43 mouse_event_count_(0), | |
44 mouse_event_flags_(0x0) { | |
45 } | |
46 virtual ~NonClientDelegate() {} | |
47 | |
48 int non_client_count() const { return non_client_count_; } | |
49 gfx::Point non_client_location() const { return non_client_location_; } | |
50 int mouse_event_count() const { return mouse_event_count_; } | |
51 gfx::Point mouse_event_location() const { return mouse_event_location_; } | |
52 int mouse_event_flags() const { return mouse_event_flags_; } | |
53 | |
54 virtual int GetNonClientComponent(const gfx::Point& location) const OVERRIDE { | |
55 NonClientDelegate* self = const_cast<NonClientDelegate*>(this); | |
56 self->non_client_count_++; | |
57 self->non_client_location_ = location; | |
58 return HTTOPLEFT; | |
59 } | |
60 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
61 mouse_event_count_++; | |
62 mouse_event_location_ = event->location(); | |
63 mouse_event_flags_ = event->flags(); | |
64 event->SetHandled(); | |
65 } | |
66 | |
67 private: | |
68 int non_client_count_; | |
69 gfx::Point non_client_location_; | |
70 int mouse_event_count_; | |
71 gfx::Point mouse_event_location_; | |
72 int mouse_event_flags_; | |
73 | |
74 DISALLOW_COPY_AND_ASSIGN(NonClientDelegate); | |
75 }; | |
76 | |
77 // A simple event handler that consumes key events. | |
78 class ConsumeKeyHandler : public test::TestEventHandler { | |
79 public: | |
80 ConsumeKeyHandler() {} | |
81 virtual ~ConsumeKeyHandler() {} | |
82 | |
83 // Overridden from ui::EventHandler: | |
84 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { | |
85 test::TestEventHandler::OnKeyEvent(event); | |
86 event->StopPropagation(); | |
87 } | |
88 | |
89 private: | |
90 DISALLOW_COPY_AND_ASSIGN(ConsumeKeyHandler); | |
91 }; | |
92 | |
93 bool IsFocusedWindow(aura::Window* window) { | |
94 return client::GetFocusClient(window)->GetFocusedWindow() == window; | |
95 } | |
96 | |
97 } // namespace | |
98 | |
99 typedef test::AuraTestBase WindowEventDispatcherTest; | |
100 | |
101 TEST_F(WindowEventDispatcherTest, OnHostMouseEvent) { | |
102 // Create two non-overlapping windows so we don't have to worry about which | |
103 // is on top. | |
104 scoped_ptr<NonClientDelegate> delegate1(new NonClientDelegate()); | |
105 scoped_ptr<NonClientDelegate> delegate2(new NonClientDelegate()); | |
106 const int kWindowWidth = 123; | |
107 const int kWindowHeight = 45; | |
108 gfx::Rect bounds1(100, 200, kWindowWidth, kWindowHeight); | |
109 gfx::Rect bounds2(300, 400, kWindowWidth, kWindowHeight); | |
110 scoped_ptr<aura::Window> window1(CreateTestWindowWithDelegate( | |
111 delegate1.get(), -1234, bounds1, root_window())); | |
112 scoped_ptr<aura::Window> window2(CreateTestWindowWithDelegate( | |
113 delegate2.get(), -5678, bounds2, root_window())); | |
114 | |
115 // Send a mouse event to window1. | |
116 gfx::Point point(101, 201); | |
117 ui::MouseEvent event1( | |
118 ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON, | |
119 ui::EF_LEFT_MOUSE_BUTTON); | |
120 DispatchEventUsingWindowDispatcher(&event1); | |
121 | |
122 // Event was tested for non-client area for the target window. | |
123 EXPECT_EQ(1, delegate1->non_client_count()); | |
124 EXPECT_EQ(0, delegate2->non_client_count()); | |
125 // The non-client component test was in local coordinates. | |
126 EXPECT_EQ(gfx::Point(1, 1), delegate1->non_client_location()); | |
127 // Mouse event was received by target window. | |
128 EXPECT_EQ(1, delegate1->mouse_event_count()); | |
129 EXPECT_EQ(0, delegate2->mouse_event_count()); | |
130 // Event was in local coordinates. | |
131 EXPECT_EQ(gfx::Point(1, 1), delegate1->mouse_event_location()); | |
132 // Non-client flag was set. | |
133 EXPECT_TRUE(delegate1->mouse_event_flags() & ui::EF_IS_NON_CLIENT); | |
134 } | |
135 | |
136 TEST_F(WindowEventDispatcherTest, RepostEvent) { | |
137 // Test RepostEvent in RootWindow. It only works for Mouse Press. | |
138 EXPECT_FALSE(Env::GetInstance()->IsMouseButtonDown()); | |
139 gfx::Point point(10, 10); | |
140 ui::MouseEvent event( | |
141 ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON, | |
142 ui::EF_LEFT_MOUSE_BUTTON); | |
143 dispatcher()->RepostEvent(event); | |
144 RunAllPendingInMessageLoop(); | |
145 EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown()); | |
146 } | |
147 | |
148 // Check that we correctly track the state of the mouse buttons in response to | |
149 // button press and release events. | |
150 TEST_F(WindowEventDispatcherTest, MouseButtonState) { | |
151 EXPECT_FALSE(Env::GetInstance()->IsMouseButtonDown()); | |
152 | |
153 gfx::Point location; | |
154 scoped_ptr<ui::MouseEvent> event; | |
155 | |
156 // Press the left button. | |
157 event.reset(new ui::MouseEvent( | |
158 ui::ET_MOUSE_PRESSED, | |
159 location, | |
160 location, | |
161 ui::EF_LEFT_MOUSE_BUTTON, | |
162 ui::EF_LEFT_MOUSE_BUTTON)); | |
163 DispatchEventUsingWindowDispatcher(event.get()); | |
164 EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown()); | |
165 | |
166 // Additionally press the right. | |
167 event.reset(new ui::MouseEvent( | |
168 ui::ET_MOUSE_PRESSED, | |
169 location, | |
170 location, | |
171 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON, | |
172 ui::EF_RIGHT_MOUSE_BUTTON)); | |
173 DispatchEventUsingWindowDispatcher(event.get()); | |
174 EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown()); | |
175 | |
176 // Release the left button. | |
177 event.reset(new ui::MouseEvent( | |
178 ui::ET_MOUSE_RELEASED, | |
179 location, | |
180 location, | |
181 ui::EF_RIGHT_MOUSE_BUTTON, | |
182 ui::EF_LEFT_MOUSE_BUTTON)); | |
183 DispatchEventUsingWindowDispatcher(event.get()); | |
184 EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown()); | |
185 | |
186 // Release the right button. We should ignore the Shift-is-down flag. | |
187 event.reset(new ui::MouseEvent( | |
188 ui::ET_MOUSE_RELEASED, | |
189 location, | |
190 location, | |
191 ui::EF_SHIFT_DOWN, | |
192 ui::EF_RIGHT_MOUSE_BUTTON)); | |
193 DispatchEventUsingWindowDispatcher(event.get()); | |
194 EXPECT_FALSE(Env::GetInstance()->IsMouseButtonDown()); | |
195 | |
196 // Press the middle button. | |
197 event.reset(new ui::MouseEvent( | |
198 ui::ET_MOUSE_PRESSED, | |
199 location, | |
200 location, | |
201 ui::EF_MIDDLE_MOUSE_BUTTON, | |
202 ui::EF_MIDDLE_MOUSE_BUTTON)); | |
203 DispatchEventUsingWindowDispatcher(event.get()); | |
204 EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown()); | |
205 } | |
206 | |
207 TEST_F(WindowEventDispatcherTest, TranslatedEvent) { | |
208 scoped_ptr<Window> w1(test::CreateTestWindowWithDelegate(NULL, 1, | |
209 gfx::Rect(50, 50, 100, 100), root_window())); | |
210 | |
211 gfx::Point origin(100, 100); | |
212 ui::MouseEvent root(ui::ET_MOUSE_PRESSED, origin, origin, 0, 0); | |
213 | |
214 EXPECT_EQ("100,100", root.location().ToString()); | |
215 EXPECT_EQ("100,100", root.root_location().ToString()); | |
216 | |
217 ui::MouseEvent translated_event( | |
218 root, static_cast<Window*>(root_window()), w1.get(), | |
219 ui::ET_MOUSE_ENTERED, root.flags()); | |
220 EXPECT_EQ("50,50", translated_event.location().ToString()); | |
221 EXPECT_EQ("100,100", translated_event.root_location().ToString()); | |
222 } | |
223 | |
224 namespace { | |
225 | |
226 class TestEventClient : public client::EventClient { | |
227 public: | |
228 static const int kNonLockWindowId = 100; | |
229 static const int kLockWindowId = 200; | |
230 | |
231 explicit TestEventClient(Window* root_window) | |
232 : root_window_(root_window), | |
233 lock_(false) { | |
234 client::SetEventClient(root_window_, this); | |
235 Window* lock_window = | |
236 test::CreateTestWindowWithBounds(root_window_->bounds(), root_window_); | |
237 lock_window->set_id(kLockWindowId); | |
238 Window* non_lock_window = | |
239 test::CreateTestWindowWithBounds(root_window_->bounds(), root_window_); | |
240 non_lock_window->set_id(kNonLockWindowId); | |
241 } | |
242 virtual ~TestEventClient() { | |
243 client::SetEventClient(root_window_, NULL); | |
244 } | |
245 | |
246 // Starts/stops locking. Locking prevents windows other than those inside | |
247 // the lock container from receiving events, getting focus etc. | |
248 void Lock() { | |
249 lock_ = true; | |
250 } | |
251 void Unlock() { | |
252 lock_ = false; | |
253 } | |
254 | |
255 Window* GetLockWindow() { | |
256 return const_cast<Window*>( | |
257 static_cast<const TestEventClient*>(this)->GetLockWindow()); | |
258 } | |
259 const Window* GetLockWindow() const { | |
260 return root_window_->GetChildById(kLockWindowId); | |
261 } | |
262 Window* GetNonLockWindow() { | |
263 return root_window_->GetChildById(kNonLockWindowId); | |
264 } | |
265 | |
266 private: | |
267 // Overridden from client::EventClient: | |
268 virtual bool CanProcessEventsWithinSubtree( | |
269 const Window* window) const OVERRIDE { | |
270 return lock_ ? | |
271 window->Contains(GetLockWindow()) || GetLockWindow()->Contains(window) : | |
272 true; | |
273 } | |
274 | |
275 virtual ui::EventTarget* GetToplevelEventTarget() OVERRIDE { | |
276 return NULL; | |
277 } | |
278 | |
279 Window* root_window_; | |
280 bool lock_; | |
281 | |
282 DISALLOW_COPY_AND_ASSIGN(TestEventClient); | |
283 }; | |
284 | |
285 } // namespace | |
286 | |
287 TEST_F(WindowEventDispatcherTest, CanProcessEventsWithinSubtree) { | |
288 TestEventClient client(root_window()); | |
289 test::TestWindowDelegate d; | |
290 | |
291 test::TestEventHandler* nonlock_ef = new test::TestEventHandler; | |
292 test::TestEventHandler* lock_ef = new test::TestEventHandler; | |
293 client.GetNonLockWindow()->SetEventFilter(nonlock_ef); | |
294 client.GetLockWindow()->SetEventFilter(lock_ef); | |
295 | |
296 Window* w1 = test::CreateTestWindowWithBounds(gfx::Rect(10, 10, 20, 20), | |
297 client.GetNonLockWindow()); | |
298 w1->set_id(1); | |
299 Window* w2 = test::CreateTestWindowWithBounds(gfx::Rect(30, 30, 20, 20), | |
300 client.GetNonLockWindow()); | |
301 w2->set_id(2); | |
302 scoped_ptr<Window> w3( | |
303 test::CreateTestWindowWithDelegate(&d, 3, gfx::Rect(30, 30, 20, 20), | |
304 client.GetLockWindow())); | |
305 | |
306 w1->Focus(); | |
307 EXPECT_TRUE(IsFocusedWindow(w1)); | |
308 | |
309 client.Lock(); | |
310 | |
311 // Since we're locked, the attempt to focus w2 will be ignored. | |
312 w2->Focus(); | |
313 EXPECT_TRUE(IsFocusedWindow(w1)); | |
314 EXPECT_FALSE(IsFocusedWindow(w2)); | |
315 | |
316 { | |
317 // Attempting to send a key event to w1 (not in the lock container) should | |
318 // cause focus to be reset. | |
319 test::EventGenerator generator(root_window()); | |
320 generator.PressKey(ui::VKEY_SPACE, 0); | |
321 EXPECT_EQ(NULL, client::GetFocusClient(w1)->GetFocusedWindow()); | |
322 EXPECT_FALSE(IsFocusedWindow(w1)); | |
323 } | |
324 | |
325 { | |
326 // Events sent to a window not in the lock container will not be processed. | |
327 // i.e. never sent to the non-lock container's event filter. | |
328 test::EventGenerator generator(root_window(), w1); | |
329 generator.ClickLeftButton(); | |
330 EXPECT_EQ(0, nonlock_ef->num_mouse_events()); | |
331 | |
332 // Events sent to a window in the lock container will be processed. | |
333 test::EventGenerator generator3(root_window(), w3.get()); | |
334 generator3.PressLeftButton(); | |
335 EXPECT_EQ(1, lock_ef->num_mouse_events()); | |
336 } | |
337 | |
338 // Prevent w3 from being deleted by the hierarchy since its delegate is owned | |
339 // by this scope. | |
340 w3->parent()->RemoveChild(w3.get()); | |
341 } | |
342 | |
343 TEST_F(WindowEventDispatcherTest, IgnoreUnknownKeys) { | |
344 test::TestEventHandler* filter = new ConsumeKeyHandler; | |
345 root_window()->SetEventFilter(filter); // passes ownership | |
346 | |
347 ui::KeyEvent unknown_event(ui::ET_KEY_PRESSED, ui::VKEY_UNKNOWN, 0, false); | |
348 DispatchEventUsingWindowDispatcher(&unknown_event); | |
349 EXPECT_FALSE(unknown_event.handled()); | |
350 EXPECT_EQ(0, filter->num_key_events()); | |
351 | |
352 ui::KeyEvent known_event(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, false); | |
353 DispatchEventUsingWindowDispatcher(&known_event); | |
354 EXPECT_TRUE(known_event.handled()); | |
355 EXPECT_EQ(1, filter->num_key_events()); | |
356 } | |
357 | |
358 TEST_F(WindowEventDispatcherTest, NoDelegateWindowReceivesKeyEvents) { | |
359 scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), NULL)); | |
360 w1->Show(); | |
361 w1->Focus(); | |
362 | |
363 test::TestEventHandler handler; | |
364 w1->AddPreTargetHandler(&handler); | |
365 ui::KeyEvent key_press(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, false); | |
366 DispatchEventUsingWindowDispatcher(&key_press); | |
367 EXPECT_TRUE(key_press.handled()); | |
368 EXPECT_EQ(1, handler.num_key_events()); | |
369 | |
370 w1->RemovePreTargetHandler(&handler); | |
371 } | |
372 | |
373 // Tests that touch-events that are beyond the bounds of the root-window do get | |
374 // propagated to the event filters correctly with the root as the target. | |
375 TEST_F(WindowEventDispatcherTest, TouchEventsOutsideBounds) { | |
376 test::TestEventHandler* filter = new test::TestEventHandler; | |
377 root_window()->SetEventFilter(filter); // passes ownership | |
378 | |
379 gfx::Point position = root_window()->bounds().origin(); | |
380 position.Offset(-10, -10); | |
381 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, position, 0, base::TimeDelta()); | |
382 DispatchEventUsingWindowDispatcher(&press); | |
383 EXPECT_EQ(1, filter->num_touch_events()); | |
384 | |
385 position = root_window()->bounds().origin(); | |
386 position.Offset(root_window()->bounds().width() + 10, | |
387 root_window()->bounds().height() + 10); | |
388 ui::TouchEvent release(ui::ET_TOUCH_RELEASED, position, 0, base::TimeDelta()); | |
389 DispatchEventUsingWindowDispatcher(&release); | |
390 EXPECT_EQ(2, filter->num_touch_events()); | |
391 } | |
392 | |
393 // Tests that scroll events are dispatched correctly. | |
394 TEST_F(WindowEventDispatcherTest, ScrollEventDispatch) { | |
395 base::TimeDelta now = ui::EventTimeForNow(); | |
396 test::TestEventHandler* filter = new test::TestEventHandler; | |
397 root_window()->SetEventFilter(filter); | |
398 | |
399 test::TestWindowDelegate delegate; | |
400 scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), &delegate)); | |
401 w1->SetBounds(gfx::Rect(20, 20, 40, 40)); | |
402 | |
403 // A scroll event on the root-window itself is dispatched. | |
404 ui::ScrollEvent scroll1(ui::ET_SCROLL, | |
405 gfx::Point(10, 10), | |
406 now, | |
407 0, | |
408 0, -10, | |
409 0, -10, | |
410 2); | |
411 DispatchEventUsingWindowDispatcher(&scroll1); | |
412 EXPECT_EQ(1, filter->num_scroll_events()); | |
413 | |
414 // Scroll event on a window should be dispatched properly. | |
415 ui::ScrollEvent scroll2(ui::ET_SCROLL, | |
416 gfx::Point(25, 30), | |
417 now, | |
418 0, | |
419 -10, 0, | |
420 -10, 0, | |
421 2); | |
422 DispatchEventUsingWindowDispatcher(&scroll2); | |
423 EXPECT_EQ(2, filter->num_scroll_events()); | |
424 } | |
425 | |
426 namespace { | |
427 | |
428 // FilterFilter that tracks the types of events it's seen. | |
429 class EventFilterRecorder : public ui::EventHandler { | |
430 public: | |
431 typedef std::vector<ui::EventType> Events; | |
432 typedef std::vector<gfx::Point> EventLocations; | |
433 | |
434 EventFilterRecorder() | |
435 : wait_until_event_(ui::ET_UNKNOWN) { | |
436 } | |
437 | |
438 const Events& events() const { return events_; } | |
439 | |
440 const EventLocations& mouse_locations() const { return mouse_locations_; } | |
441 gfx::Point mouse_location(int i) const { return mouse_locations_[i]; } | |
442 const EventLocations& touch_locations() const { return touch_locations_; } | |
443 | |
444 void WaitUntilReceivedEvent(ui::EventType type) { | |
445 wait_until_event_ = type; | |
446 run_loop_.reset(new base::RunLoop()); | |
447 run_loop_->Run(); | |
448 } | |
449 | |
450 Events GetAndResetEvents() { | |
451 Events events = events_; | |
452 Reset(); | |
453 return events; | |
454 } | |
455 | |
456 void Reset() { | |
457 events_.clear(); | |
458 mouse_locations_.clear(); | |
459 touch_locations_.clear(); | |
460 } | |
461 | |
462 // ui::EventHandler overrides: | |
463 virtual void OnEvent(ui::Event* event) OVERRIDE { | |
464 ui::EventHandler::OnEvent(event); | |
465 events_.push_back(event->type()); | |
466 if (wait_until_event_ == event->type() && run_loop_) { | |
467 run_loop_->Quit(); | |
468 wait_until_event_ = ui::ET_UNKNOWN; | |
469 } | |
470 } | |
471 | |
472 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
473 mouse_locations_.push_back(event->location()); | |
474 } | |
475 | |
476 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE { | |
477 touch_locations_.push_back(event->location()); | |
478 } | |
479 | |
480 private: | |
481 scoped_ptr<base::RunLoop> run_loop_; | |
482 ui::EventType wait_until_event_; | |
483 | |
484 Events events_; | |
485 EventLocations mouse_locations_; | |
486 EventLocations touch_locations_; | |
487 | |
488 DISALLOW_COPY_AND_ASSIGN(EventFilterRecorder); | |
489 }; | |
490 | |
491 // Converts an EventType to a string. | |
492 std::string EventTypeToString(ui::EventType type) { | |
493 switch (type) { | |
494 case ui::ET_TOUCH_RELEASED: | |
495 return "TOUCH_RELEASED"; | |
496 | |
497 case ui::ET_TOUCH_CANCELLED: | |
498 return "TOUCH_CANCELLED"; | |
499 | |
500 case ui::ET_TOUCH_PRESSED: | |
501 return "TOUCH_PRESSED"; | |
502 | |
503 case ui::ET_TOUCH_MOVED: | |
504 return "TOUCH_MOVED"; | |
505 | |
506 case ui::ET_MOUSE_PRESSED: | |
507 return "MOUSE_PRESSED"; | |
508 | |
509 case ui::ET_MOUSE_DRAGGED: | |
510 return "MOUSE_DRAGGED"; | |
511 | |
512 case ui::ET_MOUSE_RELEASED: | |
513 return "MOUSE_RELEASED"; | |
514 | |
515 case ui::ET_MOUSE_MOVED: | |
516 return "MOUSE_MOVED"; | |
517 | |
518 case ui::ET_MOUSE_ENTERED: | |
519 return "MOUSE_ENTERED"; | |
520 | |
521 case ui::ET_MOUSE_EXITED: | |
522 return "MOUSE_EXITED"; | |
523 | |
524 case ui::ET_GESTURE_SCROLL_BEGIN: | |
525 return "GESTURE_SCROLL_BEGIN"; | |
526 | |
527 case ui::ET_GESTURE_SCROLL_END: | |
528 return "GESTURE_SCROLL_END"; | |
529 | |
530 case ui::ET_GESTURE_SCROLL_UPDATE: | |
531 return "GESTURE_SCROLL_UPDATE"; | |
532 | |
533 case ui::ET_GESTURE_PINCH_BEGIN: | |
534 return "GESTURE_PINCH_BEGIN"; | |
535 | |
536 case ui::ET_GESTURE_PINCH_END: | |
537 return "GESTURE_PINCH_END"; | |
538 | |
539 case ui::ET_GESTURE_PINCH_UPDATE: | |
540 return "GESTURE_PINCH_UPDATE"; | |
541 | |
542 case ui::ET_GESTURE_TAP: | |
543 return "GESTURE_TAP"; | |
544 | |
545 case ui::ET_GESTURE_TAP_DOWN: | |
546 return "GESTURE_TAP_DOWN"; | |
547 | |
548 case ui::ET_GESTURE_TAP_CANCEL: | |
549 return "GESTURE_TAP_CANCEL"; | |
550 | |
551 case ui::ET_GESTURE_SHOW_PRESS: | |
552 return "GESTURE_SHOW_PRESS"; | |
553 | |
554 case ui::ET_GESTURE_BEGIN: | |
555 return "GESTURE_BEGIN"; | |
556 | |
557 case ui::ET_GESTURE_END: | |
558 return "GESTURE_END"; | |
559 | |
560 default: | |
561 // We should explicitly require each event type. | |
562 NOTREACHED(); | |
563 break; | |
564 } | |
565 return ""; | |
566 } | |
567 | |
568 std::string EventTypesToString(const EventFilterRecorder::Events& events) { | |
569 std::string result; | |
570 for (size_t i = 0; i < events.size(); ++i) { | |
571 if (i != 0) | |
572 result += " "; | |
573 result += EventTypeToString(events[i]); | |
574 } | |
575 return result; | |
576 } | |
577 | |
578 } // namespace | |
579 | |
580 // Verifies a repost mouse event targets the window with capture (if there is | |
581 // one). | |
582 TEST_F(WindowEventDispatcherTest, RepostTargetsCaptureWindow) { | |
583 // Set capture on |window| generate a mouse event (that is reposted) and not | |
584 // over |window| and verify |window| gets it (|window| gets it because it has | |
585 // capture). | |
586 EXPECT_FALSE(Env::GetInstance()->IsMouseButtonDown()); | |
587 scoped_ptr<Window> window(CreateNormalWindow(1, root_window(), NULL)); | |
588 window->SetBounds(gfx::Rect(20, 20, 40, 30)); | |
589 EventFilterRecorder* recorder = new EventFilterRecorder; | |
590 window->SetEventFilter(recorder); // Takes ownership. | |
591 window->SetCapture(); | |
592 const ui::MouseEvent press_event( | |
593 ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), | |
594 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); | |
595 dispatcher()->RepostEvent(press_event); | |
596 RunAllPendingInMessageLoop(); // Necessitated by RepostEvent(). | |
597 // Mouse moves/enters may be generated. We only care about a pressed. | |
598 EXPECT_TRUE(EventTypesToString(recorder->events()).find("MOUSE_PRESSED") != | |
599 std::string::npos) << EventTypesToString(recorder->events()); | |
600 } | |
601 | |
602 TEST_F(WindowEventDispatcherTest, MouseMovesHeld) { | |
603 EventFilterRecorder* filter = new EventFilterRecorder; | |
604 root_window()->SetEventFilter(filter); // passes ownership | |
605 | |
606 test::TestWindowDelegate delegate; | |
607 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
608 &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window())); | |
609 | |
610 ui::MouseEvent mouse_move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0), | |
611 gfx::Point(0, 0), 0, 0); | |
612 DispatchEventUsingWindowDispatcher(&mouse_move_event); | |
613 // Discard MOUSE_ENTER. | |
614 filter->Reset(); | |
615 | |
616 dispatcher()->HoldPointerMoves(); | |
617 | |
618 // Check that we don't immediately dispatch the MOUSE_DRAGGED event. | |
619 ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), | |
620 gfx::Point(0, 0), 0, 0); | |
621 DispatchEventUsingWindowDispatcher(&mouse_dragged_event); | |
622 EXPECT_TRUE(filter->events().empty()); | |
623 | |
624 // Check that we do dispatch the held MOUSE_DRAGGED event before another type | |
625 // of event. | |
626 ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), | |
627 gfx::Point(0, 0), 0, 0); | |
628 DispatchEventUsingWindowDispatcher(&mouse_pressed_event); | |
629 EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED", | |
630 EventTypesToString(filter->events())); | |
631 filter->Reset(); | |
632 | |
633 // Check that we coalesce held MOUSE_DRAGGED events. | |
634 ui::MouseEvent mouse_dragged_event2(ui::ET_MOUSE_DRAGGED, gfx::Point(10, 10), | |
635 gfx::Point(10, 10), 0, 0); | |
636 DispatchEventUsingWindowDispatcher(&mouse_dragged_event); | |
637 DispatchEventUsingWindowDispatcher(&mouse_dragged_event2); | |
638 EXPECT_TRUE(filter->events().empty()); | |
639 DispatchEventUsingWindowDispatcher(&mouse_pressed_event); | |
640 EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED", | |
641 EventTypesToString(filter->events())); | |
642 filter->Reset(); | |
643 | |
644 // Check that on ReleasePointerMoves, held events are not dispatched | |
645 // immediately, but posted instead. | |
646 DispatchEventUsingWindowDispatcher(&mouse_dragged_event); | |
647 dispatcher()->ReleasePointerMoves(); | |
648 EXPECT_TRUE(filter->events().empty()); | |
649 RunAllPendingInMessageLoop(); | |
650 EXPECT_EQ("MOUSE_DRAGGED", EventTypesToString(filter->events())); | |
651 filter->Reset(); | |
652 | |
653 // However if another message comes in before the dispatch of the posted | |
654 // event, check that the posted event is dispatched before this new event. | |
655 dispatcher()->HoldPointerMoves(); | |
656 DispatchEventUsingWindowDispatcher(&mouse_dragged_event); | |
657 dispatcher()->ReleasePointerMoves(); | |
658 DispatchEventUsingWindowDispatcher(&mouse_pressed_event); | |
659 EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED", | |
660 EventTypesToString(filter->events())); | |
661 filter->Reset(); | |
662 RunAllPendingInMessageLoop(); | |
663 EXPECT_TRUE(filter->events().empty()); | |
664 | |
665 // Check that if the other message is another MOUSE_DRAGGED, we still coalesce | |
666 // them. | |
667 dispatcher()->HoldPointerMoves(); | |
668 DispatchEventUsingWindowDispatcher(&mouse_dragged_event); | |
669 dispatcher()->ReleasePointerMoves(); | |
670 DispatchEventUsingWindowDispatcher(&mouse_dragged_event2); | |
671 EXPECT_EQ("MOUSE_DRAGGED", EventTypesToString(filter->events())); | |
672 filter->Reset(); | |
673 RunAllPendingInMessageLoop(); | |
674 EXPECT_TRUE(filter->events().empty()); | |
675 | |
676 // Check that synthetic mouse move event has a right location when issued | |
677 // while holding pointer moves. | |
678 ui::MouseEvent mouse_dragged_event3(ui::ET_MOUSE_DRAGGED, gfx::Point(28, 28), | |
679 gfx::Point(28, 28), 0, 0); | |
680 dispatcher()->HoldPointerMoves(); | |
681 DispatchEventUsingWindowDispatcher(&mouse_dragged_event); | |
682 DispatchEventUsingWindowDispatcher(&mouse_dragged_event2); | |
683 window->SetBounds(gfx::Rect(15, 15, 80, 80)); | |
684 DispatchEventUsingWindowDispatcher(&mouse_dragged_event3); | |
685 RunAllPendingInMessageLoop(); | |
686 EXPECT_TRUE(filter->events().empty()); | |
687 dispatcher()->ReleasePointerMoves(); | |
688 RunAllPendingInMessageLoop(); | |
689 EXPECT_EQ("MOUSE_MOVED", EventTypesToString(filter->events())); | |
690 EXPECT_EQ(gfx::Point(13, 13), filter->mouse_location(0)); | |
691 filter->Reset(); | |
692 } | |
693 | |
694 TEST_F(WindowEventDispatcherTest, TouchMovesHeld) { | |
695 EventFilterRecorder* filter = new EventFilterRecorder; | |
696 root_window()->SetEventFilter(filter); // passes ownership | |
697 | |
698 test::TestWindowDelegate delegate; | |
699 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
700 &delegate, 1, gfx::Rect(50, 50, 100, 100), root_window())); | |
701 | |
702 const gfx::Point touch_location(60, 60); | |
703 // Starting the touch and throwing out the first few events, since the system | |
704 // is going to generate synthetic mouse events that are not relevant to the | |
705 // test. | |
706 ui::TouchEvent touch_pressed_event(ui::ET_TOUCH_PRESSED, touch_location, | |
707 0, base::TimeDelta()); | |
708 DispatchEventUsingWindowDispatcher(&touch_pressed_event); | |
709 filter->WaitUntilReceivedEvent(ui::ET_GESTURE_SHOW_PRESS); | |
710 filter->Reset(); | |
711 | |
712 dispatcher()->HoldPointerMoves(); | |
713 | |
714 // Check that we don't immediately dispatch the TOUCH_MOVED event. | |
715 ui::TouchEvent touch_moved_event(ui::ET_TOUCH_MOVED, touch_location, | |
716 0, base::TimeDelta()); | |
717 DispatchEventUsingWindowDispatcher(&touch_moved_event); | |
718 EXPECT_TRUE(filter->events().empty()); | |
719 | |
720 // Check that on ReleasePointerMoves, held events are not dispatched | |
721 // immediately, but posted instead. | |
722 DispatchEventUsingWindowDispatcher(&touch_moved_event); | |
723 dispatcher()->ReleasePointerMoves(); | |
724 EXPECT_TRUE(filter->events().empty()); | |
725 | |
726 RunAllPendingInMessageLoop(); | |
727 EXPECT_EQ("TOUCH_MOVED", EventTypesToString(filter->events())); | |
728 filter->Reset(); | |
729 | |
730 // If another touch event occurs then the held touch should be dispatched | |
731 // immediately before it. | |
732 ui::TouchEvent touch_released_event(ui::ET_TOUCH_RELEASED, touch_location, | |
733 0, base::TimeDelta()); | |
734 filter->Reset(); | |
735 dispatcher()->HoldPointerMoves(); | |
736 DispatchEventUsingWindowDispatcher(&touch_moved_event); | |
737 DispatchEventUsingWindowDispatcher(&touch_released_event); | |
738 EXPECT_EQ("TOUCH_MOVED TOUCH_RELEASED GESTURE_TAP_CANCEL GESTURE_END", | |
739 EventTypesToString(filter->events())); | |
740 filter->Reset(); | |
741 dispatcher()->ReleasePointerMoves(); | |
742 RunAllPendingInMessageLoop(); | |
743 EXPECT_TRUE(filter->events().empty()); | |
744 } | |
745 | |
746 class HoldPointerOnScrollHandler : public test::TestEventHandler { | |
747 public: | |
748 HoldPointerOnScrollHandler(WindowEventDispatcher* dispatcher, | |
749 EventFilterRecorder* filter) | |
750 : dispatcher_(dispatcher), | |
751 filter_(filter), | |
752 holding_moves_(false) { | |
753 } | |
754 virtual ~HoldPointerOnScrollHandler() {} | |
755 | |
756 private: | |
757 virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE { | |
758 if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { | |
759 CHECK(!holding_moves_); | |
760 holding_moves_ = true; | |
761 dispatcher_->HoldPointerMoves(); | |
762 filter_->Reset(); | |
763 } else if (gesture->type() == ui::ET_GESTURE_SCROLL_END) { | |
764 dispatcher_->ReleasePointerMoves(); | |
765 holding_moves_ = false; | |
766 } | |
767 } | |
768 | |
769 WindowEventDispatcher* dispatcher_; | |
770 EventFilterRecorder* filter_; | |
771 bool holding_moves_; | |
772 | |
773 DISALLOW_COPY_AND_ASSIGN(HoldPointerOnScrollHandler); | |
774 }; | |
775 | |
776 // Tests that touch-move events don't contribute to an in-progress scroll | |
777 // gesture if touch-move events are being held by the dispatcher. | |
778 TEST_F(WindowEventDispatcherTest, TouchMovesHeldOnScroll) { | |
779 EventFilterRecorder* filter = new EventFilterRecorder; | |
780 root_window()->SetEventFilter(filter); | |
781 test::TestWindowDelegate delegate; | |
782 HoldPointerOnScrollHandler handler(dispatcher(), filter); | |
783 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
784 &delegate, 1, gfx::Rect(50, 50, 100, 100), root_window())); | |
785 window->AddPreTargetHandler(&handler); | |
786 | |
787 test::EventGenerator generator(root_window()); | |
788 generator.GestureScrollSequence( | |
789 gfx::Point(60, 60), gfx::Point(10, 60), | |
790 base::TimeDelta::FromMilliseconds(100), 25); | |
791 | |
792 // |handler| will have reset |filter| and started holding the touch-move | |
793 // events when scrolling started. At the end of the scroll (i.e. upon | |
794 // touch-release), the held touch-move event will have been dispatched first, | |
795 // along with the subsequent events (i.e. touch-release, scroll-end, and | |
796 // gesture-end). | |
797 const EventFilterRecorder::Events& events = filter->events(); | |
798 EXPECT_EQ("TOUCH_MOVED TOUCH_RELEASED GESTURE_SCROLL_END GESTURE_END", | |
799 EventTypesToString(events)); | |
800 ASSERT_EQ(2u, filter->touch_locations().size()); | |
801 EXPECT_EQ(gfx::Point(-40, 10).ToString(), | |
802 filter->touch_locations()[0].ToString()); | |
803 EXPECT_EQ(gfx::Point(-40, 10).ToString(), | |
804 filter->touch_locations()[1].ToString()); | |
805 } | |
806 | |
807 // Tests that synthetic mouse events are ignored when mouse | |
808 // events are disabled. | |
809 TEST_F(WindowEventDispatcherTest, DispatchSyntheticMouseEvents) { | |
810 EventFilterRecorder* filter = new EventFilterRecorder; | |
811 root_window()->SetEventFilter(filter); // passes ownership | |
812 | |
813 test::TestWindowDelegate delegate; | |
814 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
815 &delegate, 1234, gfx::Rect(5, 5, 100, 100), root_window())); | |
816 window->Show(); | |
817 window->SetCapture(); | |
818 | |
819 test::TestCursorClient cursor_client(root_window()); | |
820 | |
821 // Dispatch a non-synthetic mouse event when mouse events are enabled. | |
822 ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, gfx::Point(10, 10), | |
823 gfx::Point(10, 10), 0, 0); | |
824 DispatchEventUsingWindowDispatcher(&mouse1); | |
825 EXPECT_FALSE(filter->events().empty()); | |
826 filter->Reset(); | |
827 | |
828 // Dispatch a synthetic mouse event when mouse events are enabled. | |
829 ui::MouseEvent mouse2(ui::ET_MOUSE_MOVED, gfx::Point(10, 10), | |
830 gfx::Point(10, 10), ui::EF_IS_SYNTHESIZED, 0); | |
831 DispatchEventUsingWindowDispatcher(&mouse2); | |
832 EXPECT_FALSE(filter->events().empty()); | |
833 filter->Reset(); | |
834 | |
835 // Dispatch a synthetic mouse event when mouse events are disabled. | |
836 cursor_client.DisableMouseEvents(); | |
837 DispatchEventUsingWindowDispatcher(&mouse2); | |
838 EXPECT_TRUE(filter->events().empty()); | |
839 } | |
840 | |
841 // Tests that a mouse exit is dispatched to the last known cursor location | |
842 // when the cursor becomes invisible. | |
843 TEST_F(WindowEventDispatcherTest, DispatchMouseExitWhenCursorHidden) { | |
844 EventFilterRecorder* filter = new EventFilterRecorder; | |
845 root_window()->SetEventFilter(filter); // passes ownership | |
846 | |
847 test::TestWindowDelegate delegate; | |
848 gfx::Point window_origin(7, 18); | |
849 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
850 &delegate, 1234, gfx::Rect(window_origin, gfx::Size(100, 100)), | |
851 root_window())); | |
852 window->Show(); | |
853 | |
854 // Dispatch a mouse move event into the window. | |
855 gfx::Point mouse_location(gfx::Point(15, 25)); | |
856 ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, mouse_location, | |
857 mouse_location, 0, 0); | |
858 EXPECT_TRUE(filter->events().empty()); | |
859 DispatchEventUsingWindowDispatcher(&mouse1); | |
860 EXPECT_FALSE(filter->events().empty()); | |
861 filter->Reset(); | |
862 | |
863 // Hide the cursor and verify a mouse exit was dispatched. | |
864 dispatcher()->host()->OnCursorVisibilityChanged(false); | |
865 EXPECT_FALSE(filter->events().empty()); | |
866 EXPECT_EQ("MOUSE_EXITED", EventTypesToString(filter->events())); | |
867 | |
868 // Verify the mouse exit was dispatched at the correct location | |
869 // (in the correct coordinate space). | |
870 int translated_x = mouse_location.x() - window_origin.x(); | |
871 int translated_y = mouse_location.y() - window_origin.y(); | |
872 gfx::Point translated_point(translated_x, translated_y); | |
873 EXPECT_EQ(filter->mouse_location(0).ToString(), translated_point.ToString()); | |
874 } | |
875 | |
876 class DeletingEventFilter : public ui::EventHandler { | |
877 public: | |
878 DeletingEventFilter() | |
879 : delete_during_pre_handle_(false) {} | |
880 virtual ~DeletingEventFilter() {} | |
881 | |
882 void Reset(bool delete_during_pre_handle) { | |
883 delete_during_pre_handle_ = delete_during_pre_handle; | |
884 } | |
885 | |
886 private: | |
887 // Overridden from ui::EventHandler: | |
888 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { | |
889 if (delete_during_pre_handle_) | |
890 delete event->target(); | |
891 } | |
892 | |
893 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
894 if (delete_during_pre_handle_) | |
895 delete event->target(); | |
896 } | |
897 | |
898 bool delete_during_pre_handle_; | |
899 | |
900 DISALLOW_COPY_AND_ASSIGN(DeletingEventFilter); | |
901 }; | |
902 | |
903 class DeletingWindowDelegate : public test::TestWindowDelegate { | |
904 public: | |
905 DeletingWindowDelegate() | |
906 : window_(NULL), | |
907 delete_during_handle_(false), | |
908 got_event_(false) {} | |
909 virtual ~DeletingWindowDelegate() {} | |
910 | |
911 void Reset(Window* window, bool delete_during_handle) { | |
912 window_ = window; | |
913 delete_during_handle_ = delete_during_handle; | |
914 got_event_ = false; | |
915 } | |
916 bool got_event() const { return got_event_; } | |
917 | |
918 private: | |
919 // Overridden from WindowDelegate: | |
920 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { | |
921 if (delete_during_handle_) | |
922 delete window_; | |
923 got_event_ = true; | |
924 } | |
925 | |
926 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
927 if (delete_during_handle_) | |
928 delete window_; | |
929 got_event_ = true; | |
930 } | |
931 | |
932 Window* window_; | |
933 bool delete_during_handle_; | |
934 bool got_event_; | |
935 | |
936 DISALLOW_COPY_AND_ASSIGN(DeletingWindowDelegate); | |
937 }; | |
938 | |
939 TEST_F(WindowEventDispatcherTest, DeleteWindowDuringDispatch) { | |
940 // Verifies that we can delete a window during each phase of event handling. | |
941 // Deleting the window should not cause a crash, only prevent further | |
942 // processing from occurring. | |
943 scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), NULL)); | |
944 DeletingWindowDelegate d11; | |
945 Window* w11 = CreateNormalWindow(11, w1.get(), &d11); | |
946 WindowTracker tracker; | |
947 DeletingEventFilter* w1_filter = new DeletingEventFilter; | |
948 w1->SetEventFilter(w1_filter); | |
949 client::GetFocusClient(w1.get())->FocusWindow(w11); | |
950 | |
951 test::EventGenerator generator(root_window(), w11); | |
952 | |
953 // First up, no one deletes anything. | |
954 tracker.Add(w11); | |
955 d11.Reset(w11, false); | |
956 | |
957 generator.PressLeftButton(); | |
958 EXPECT_TRUE(tracker.Contains(w11)); | |
959 EXPECT_TRUE(d11.got_event()); | |
960 generator.ReleaseLeftButton(); | |
961 | |
962 // Delegate deletes w11. This will prevent the post-handle step from applying. | |
963 w1_filter->Reset(false); | |
964 d11.Reset(w11, true); | |
965 generator.PressKey(ui::VKEY_A, 0); | |
966 EXPECT_FALSE(tracker.Contains(w11)); | |
967 EXPECT_TRUE(d11.got_event()); | |
968 | |
969 // Pre-handle step deletes w11. This will prevent the delegate and the post- | |
970 // handle steps from applying. | |
971 w11 = CreateNormalWindow(11, w1.get(), &d11); | |
972 w1_filter->Reset(true); | |
973 d11.Reset(w11, false); | |
974 generator.PressLeftButton(); | |
975 EXPECT_FALSE(tracker.Contains(w11)); | |
976 EXPECT_FALSE(d11.got_event()); | |
977 } | |
978 | |
979 namespace { | |
980 | |
981 // A window delegate that detaches the parent of the target's parent window when | |
982 // it receives a tap event. | |
983 class DetachesParentOnTapDelegate : public test::TestWindowDelegate { | |
984 public: | |
985 DetachesParentOnTapDelegate() {} | |
986 virtual ~DetachesParentOnTapDelegate() {} | |
987 | |
988 private: | |
989 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { | |
990 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { | |
991 event->SetHandled(); | |
992 return; | |
993 } | |
994 | |
995 if (event->type() == ui::ET_GESTURE_TAP) { | |
996 Window* parent = static_cast<Window*>(event->target())->parent(); | |
997 parent->parent()->RemoveChild(parent); | |
998 event->SetHandled(); | |
999 } | |
1000 } | |
1001 | |
1002 DISALLOW_COPY_AND_ASSIGN(DetachesParentOnTapDelegate); | |
1003 }; | |
1004 | |
1005 } // namespace | |
1006 | |
1007 // Tests that the gesture recognizer is reset for all child windows when a | |
1008 // window hides. No expectations, just checks that the test does not crash. | |
1009 TEST_F(WindowEventDispatcherTest, | |
1010 GestureRecognizerResetsTargetWhenParentHides) { | |
1011 scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), NULL)); | |
1012 DetachesParentOnTapDelegate delegate; | |
1013 scoped_ptr<Window> parent(CreateNormalWindow(22, w1.get(), NULL)); | |
1014 Window* child = CreateNormalWindow(11, parent.get(), &delegate); | |
1015 test::EventGenerator generator(root_window(), child); | |
1016 generator.GestureTapAt(gfx::Point(40, 40)); | |
1017 } | |
1018 | |
1019 namespace { | |
1020 | |
1021 // A window delegate that processes nested gestures on tap. | |
1022 class NestedGestureDelegate : public test::TestWindowDelegate { | |
1023 public: | |
1024 NestedGestureDelegate(test::EventGenerator* generator, | |
1025 const gfx::Point tap_location) | |
1026 : generator_(generator), | |
1027 tap_location_(tap_location), | |
1028 gesture_end_count_(0) {} | |
1029 virtual ~NestedGestureDelegate() {} | |
1030 | |
1031 int gesture_end_count() const { return gesture_end_count_; } | |
1032 | |
1033 private: | |
1034 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { | |
1035 switch (event->type()) { | |
1036 case ui::ET_GESTURE_TAP_DOWN: | |
1037 event->SetHandled(); | |
1038 break; | |
1039 case ui::ET_GESTURE_TAP: | |
1040 if (generator_) | |
1041 generator_->GestureTapAt(tap_location_); | |
1042 event->SetHandled(); | |
1043 break; | |
1044 case ui::ET_GESTURE_END: | |
1045 ++gesture_end_count_; | |
1046 break; | |
1047 default: | |
1048 break; | |
1049 } | |
1050 } | |
1051 | |
1052 test::EventGenerator* generator_; | |
1053 const gfx::Point tap_location_; | |
1054 int gesture_end_count_; | |
1055 DISALLOW_COPY_AND_ASSIGN(NestedGestureDelegate); | |
1056 }; | |
1057 | |
1058 } // namespace | |
1059 | |
1060 // Tests that gesture end is delivered after nested gesture processing. | |
1061 TEST_F(WindowEventDispatcherTest, GestureEndDeliveredAfterNestedGestures) { | |
1062 NestedGestureDelegate d1(NULL, gfx::Point()); | |
1063 scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), &d1)); | |
1064 w1->SetBounds(gfx::Rect(0, 0, 100, 100)); | |
1065 | |
1066 test::EventGenerator nested_generator(root_window(), w1.get()); | |
1067 NestedGestureDelegate d2(&nested_generator, w1->bounds().CenterPoint()); | |
1068 scoped_ptr<Window> w2(CreateNormalWindow(1, root_window(), &d2)); | |
1069 w2->SetBounds(gfx::Rect(100, 0, 100, 100)); | |
1070 | |
1071 // Tap on w2 which triggers nested gestures for w1. | |
1072 test::EventGenerator generator(root_window(), w2.get()); | |
1073 generator.GestureTapAt(w2->bounds().CenterPoint()); | |
1074 | |
1075 // Both windows should get their gesture end events. | |
1076 EXPECT_EQ(1, d1.gesture_end_count()); | |
1077 EXPECT_EQ(1, d2.gesture_end_count()); | |
1078 } | |
1079 | |
1080 // Tests whether we can repost the Tap down gesture event. | |
1081 TEST_F(WindowEventDispatcherTest, RepostTapdownGestureTest) { | |
1082 EventFilterRecorder* filter = new EventFilterRecorder; | |
1083 root_window()->SetEventFilter(filter); // passes ownership | |
1084 | |
1085 test::TestWindowDelegate delegate; | |
1086 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
1087 &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window())); | |
1088 | |
1089 ui::GestureEventDetails details(ui::ET_GESTURE_TAP_DOWN, 0.0f, 0.0f); | |
1090 gfx::Point point(10, 10); | |
1091 ui::GestureEvent event( | |
1092 ui::ET_GESTURE_TAP_DOWN, | |
1093 point.x(), | |
1094 point.y(), | |
1095 0, | |
1096 ui::EventTimeForNow(), | |
1097 details, | |
1098 0); | |
1099 dispatcher()->RepostEvent(event); | |
1100 RunAllPendingInMessageLoop(); | |
1101 // TODO(rbyers): Currently disabled - crbug.com/170987 | |
1102 EXPECT_FALSE(EventTypesToString(filter->events()).find("GESTURE_TAP_DOWN") != | |
1103 std::string::npos); | |
1104 filter->Reset(); | |
1105 } | |
1106 | |
1107 // This class inherits from the EventFilterRecorder class which provides a | |
1108 // facility to record events. This class additionally provides a facility to | |
1109 // repost the ET_GESTURE_TAP_DOWN gesture to the target window and records | |
1110 // events after that. | |
1111 class RepostGestureEventRecorder : public EventFilterRecorder { | |
1112 public: | |
1113 RepostGestureEventRecorder(aura::Window* repost_source, | |
1114 aura::Window* repost_target) | |
1115 : repost_source_(repost_source), | |
1116 repost_target_(repost_target), | |
1117 reposted_(false), | |
1118 done_cleanup_(false) {} | |
1119 | |
1120 virtual ~RepostGestureEventRecorder() {} | |
1121 | |
1122 virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE { | |
1123 if (reposted_ && event->type() == ui::ET_TOUCH_PRESSED) { | |
1124 done_cleanup_ = true; | |
1125 Reset(); | |
1126 } | |
1127 EventFilterRecorder::OnTouchEvent(event); | |
1128 } | |
1129 | |
1130 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { | |
1131 EXPECT_EQ(done_cleanup_ ? repost_target_ : repost_source_, event->target()); | |
1132 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { | |
1133 if (!reposted_) { | |
1134 EXPECT_NE(repost_target_, event->target()); | |
1135 reposted_ = true; | |
1136 repost_target_->GetDispatcher()->RepostEvent(*event); | |
1137 // Ensure that the reposted gesture event above goes to the | |
1138 // repost_target_; | |
1139 repost_source_->GetRootWindow()->RemoveChild(repost_source_); | |
1140 return; | |
1141 } | |
1142 } | |
1143 EventFilterRecorder::OnGestureEvent(event); | |
1144 } | |
1145 | |
1146 // Ignore mouse events as they don't fire at all times. This causes | |
1147 // the GestureRepostEventOrder test to fail randomly. | |
1148 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {} | |
1149 | |
1150 private: | |
1151 aura::Window* repost_source_; | |
1152 aura::Window* repost_target_; | |
1153 // set to true if we reposted the ET_GESTURE_TAP_DOWN event. | |
1154 bool reposted_; | |
1155 // set true if we're done cleaning up after hiding repost_source_; | |
1156 bool done_cleanup_; | |
1157 DISALLOW_COPY_AND_ASSIGN(RepostGestureEventRecorder); | |
1158 }; | |
1159 | |
1160 // Tests whether events which are generated after the reposted gesture event | |
1161 // are received after that. In this case the scroll sequence events should | |
1162 // be received after the reposted gesture event. | |
1163 TEST_F(WindowEventDispatcherTest, GestureRepostEventOrder) { | |
1164 // Expected events at the end for the repost_target window defined below. | |
1165 const char kExpectedTargetEvents[] = | |
1166 // TODO)(rbyers): Gesture event reposting is disabled - crbug.com/279039. | |
1167 // "GESTURE_BEGIN GESTURE_TAP_DOWN " | |
1168 "TOUCH_PRESSED GESTURE_BEGIN GESTURE_TAP_DOWN TOUCH_MOVED " | |
1169 "GESTURE_TAP_CANCEL GESTURE_SCROLL_BEGIN GESTURE_SCROLL_UPDATE TOUCH_MOVED " | |
1170 "GESTURE_SCROLL_UPDATE TOUCH_MOVED GESTURE_SCROLL_UPDATE TOUCH_RELEASED " | |
1171 "GESTURE_SCROLL_END GESTURE_END"; | |
1172 // We create two windows. | |
1173 // The first window (repost_source) is the one to which the initial tap | |
1174 // gesture is sent. It reposts this event to the second window | |
1175 // (repost_target). | |
1176 // We then generate the scroll sequence for repost_target and look for two | |
1177 // ET_GESTURE_TAP_DOWN events in the event list at the end. | |
1178 test::TestWindowDelegate delegate; | |
1179 scoped_ptr<aura::Window> repost_target(CreateTestWindowWithDelegate( | |
1180 &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window())); | |
1181 | |
1182 scoped_ptr<aura::Window> repost_source(CreateTestWindowWithDelegate( | |
1183 &delegate, 1, gfx::Rect(0, 0, 50, 50), root_window())); | |
1184 | |
1185 RepostGestureEventRecorder* repost_event_recorder = | |
1186 new RepostGestureEventRecorder(repost_source.get(), repost_target.get()); | |
1187 root_window()->SetEventFilter(repost_event_recorder); // passes ownership | |
1188 | |
1189 // Generate a tap down gesture for the repost_source. This will be reposted | |
1190 // to repost_target. | |
1191 test::EventGenerator repost_generator(root_window(), repost_source.get()); | |
1192 repost_generator.GestureTapAt(gfx::Point(40, 40)); | |
1193 RunAllPendingInMessageLoop(); | |
1194 | |
1195 test::EventGenerator scroll_generator(root_window(), repost_target.get()); | |
1196 scroll_generator.GestureScrollSequence( | |
1197 gfx::Point(80, 80), | |
1198 gfx::Point(100, 100), | |
1199 base::TimeDelta::FromMilliseconds(100), | |
1200 3); | |
1201 RunAllPendingInMessageLoop(); | |
1202 | |
1203 int tap_down_count = 0; | |
1204 for (size_t i = 0; i < repost_event_recorder->events().size(); ++i) { | |
1205 if (repost_event_recorder->events()[i] == ui::ET_GESTURE_TAP_DOWN) | |
1206 ++tap_down_count; | |
1207 } | |
1208 | |
1209 // We expect two tap down events. One from the repost and the other one from | |
1210 // the scroll sequence posted above. | |
1211 // TODO(rbyers): Currently disabled - crbug.com/170987 | |
1212 EXPECT_EQ(1, tap_down_count); | |
1213 | |
1214 EXPECT_EQ(kExpectedTargetEvents, | |
1215 EventTypesToString(repost_event_recorder->events())); | |
1216 } | |
1217 | |
1218 class OnMouseExitDeletingEventFilter : public EventFilterRecorder { | |
1219 public: | |
1220 OnMouseExitDeletingEventFilter() : window_to_delete_(NULL) {} | |
1221 virtual ~OnMouseExitDeletingEventFilter() {} | |
1222 | |
1223 void set_window_to_delete(Window* window_to_delete) { | |
1224 window_to_delete_ = window_to_delete; | |
1225 } | |
1226 | |
1227 private: | |
1228 // Overridden from ui::EventHandler: | |
1229 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
1230 EventFilterRecorder::OnMouseEvent(event); | |
1231 if (window_to_delete_) { | |
1232 delete window_to_delete_; | |
1233 window_to_delete_ = NULL; | |
1234 } | |
1235 } | |
1236 | |
1237 Window* window_to_delete_; | |
1238 | |
1239 DISALLOW_COPY_AND_ASSIGN(OnMouseExitDeletingEventFilter); | |
1240 }; | |
1241 | |
1242 // Tests that RootWindow drops mouse-moved event that is supposed to be sent to | |
1243 // a child, but the child is destroyed because of the synthesized mouse-exit | |
1244 // event generated on the previous mouse_moved_handler_. | |
1245 TEST_F(WindowEventDispatcherTest, DeleteWindowDuringMouseMovedDispatch) { | |
1246 // Create window 1 and set its event filter. Window 1 will take ownership of | |
1247 // the event filter. | |
1248 scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), NULL)); | |
1249 OnMouseExitDeletingEventFilter* w1_filter = | |
1250 new OnMouseExitDeletingEventFilter(); | |
1251 w1->SetEventFilter(w1_filter); | |
1252 w1->SetBounds(gfx::Rect(20, 20, 60, 60)); | |
1253 EXPECT_EQ(NULL, dispatcher()->mouse_moved_handler()); | |
1254 | |
1255 test::EventGenerator generator(root_window(), w1.get()); | |
1256 | |
1257 // Move mouse over window 1 to set it as the |mouse_moved_handler_| for the | |
1258 // root window. | |
1259 generator.MoveMouseTo(51, 51); | |
1260 EXPECT_EQ(w1.get(), dispatcher()->mouse_moved_handler()); | |
1261 | |
1262 // Create window 2 under the mouse cursor and stack it above window 1. | |
1263 Window* w2 = CreateNormalWindow(2, root_window(), NULL); | |
1264 w2->SetBounds(gfx::Rect(30, 30, 40, 40)); | |
1265 root_window()->StackChildAbove(w2, w1.get()); | |
1266 | |
1267 // Set window 2 as the window that is to be deleted when a mouse-exited event | |
1268 // happens on window 1. | |
1269 w1_filter->set_window_to_delete(w2); | |
1270 | |
1271 // Move mosue over window 2. This should generate a mouse-exited event for | |
1272 // window 1 resulting in deletion of window 2. The original mouse-moved event | |
1273 // that was targeted to window 2 should be dropped since window 2 is | |
1274 // destroyed. This test passes if no crash happens. | |
1275 generator.MoveMouseTo(52, 52); | |
1276 EXPECT_EQ(NULL, dispatcher()->mouse_moved_handler()); | |
1277 | |
1278 // Check events received by window 1. | |
1279 EXPECT_EQ("MOUSE_ENTERED MOUSE_MOVED MOUSE_EXITED", | |
1280 EventTypesToString(w1_filter->events())); | |
1281 } | |
1282 | |
1283 namespace { | |
1284 | |
1285 // Used to track if OnWindowDestroying() is invoked and if there is a valid | |
1286 // RootWindow at such time. | |
1287 class ValidRootDuringDestructionWindowObserver : public aura::WindowObserver { | |
1288 public: | |
1289 ValidRootDuringDestructionWindowObserver(bool* got_destroying, | |
1290 bool* has_valid_root) | |
1291 : got_destroying_(got_destroying), | |
1292 has_valid_root_(has_valid_root) { | |
1293 } | |
1294 | |
1295 // WindowObserver: | |
1296 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { | |
1297 *got_destroying_ = true; | |
1298 *has_valid_root_ = (window->GetRootWindow() != NULL); | |
1299 } | |
1300 | |
1301 private: | |
1302 bool* got_destroying_; | |
1303 bool* has_valid_root_; | |
1304 | |
1305 DISALLOW_COPY_AND_ASSIGN(ValidRootDuringDestructionWindowObserver); | |
1306 }; | |
1307 | |
1308 } // namespace | |
1309 | |
1310 #if defined(USE_OZONE) | |
1311 // Creating multiple WindowTreeHostOzone instances is broken. | |
1312 #define MAYBE_ValidRootDuringDestruction DISABLED_ValidRootDuringDestruction | |
1313 #else | |
1314 #define MAYBE_ValidRootDuringDestruction ValidRootDuringDestruction | |
1315 #endif | |
1316 | |
1317 // Verifies GetRootWindow() from ~Window returns a valid root. | |
1318 TEST_F(WindowEventDispatcherTest, MAYBE_ValidRootDuringDestruction) { | |
1319 bool got_destroying = false; | |
1320 bool has_valid_root = false; | |
1321 ValidRootDuringDestructionWindowObserver observer(&got_destroying, | |
1322 &has_valid_root); | |
1323 { | |
1324 scoped_ptr<WindowEventDispatcher> dispatcher( | |
1325 new WindowEventDispatcher( | |
1326 WindowEventDispatcher::CreateParams(gfx::Rect(0, 0, 100, 100)))); | |
1327 dispatcher->host()->InitHost(); | |
1328 // Owned by WindowEventDispatcher. | |
1329 Window* w1 = CreateNormalWindow(1, dispatcher->window(), NULL); | |
1330 w1->AddObserver(&observer); | |
1331 } | |
1332 EXPECT_TRUE(got_destroying); | |
1333 EXPECT_TRUE(has_valid_root); | |
1334 } | |
1335 | |
1336 namespace { | |
1337 | |
1338 // See description above DontResetHeldEvent for details. | |
1339 class DontResetHeldEventWindowDelegate : public test::TestWindowDelegate { | |
1340 public: | |
1341 explicit DontResetHeldEventWindowDelegate(aura::Window* root) | |
1342 : root_(root), | |
1343 mouse_event_count_(0) {} | |
1344 virtual ~DontResetHeldEventWindowDelegate() {} | |
1345 | |
1346 int mouse_event_count() const { return mouse_event_count_; } | |
1347 | |
1348 // TestWindowDelegate: | |
1349 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
1350 if ((event->flags() & ui::EF_SHIFT_DOWN) != 0 && | |
1351 mouse_event_count_++ == 0) { | |
1352 ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, | |
1353 gfx::Point(10, 10), gfx::Point(10, 10), | |
1354 ui::EF_SHIFT_DOWN, 0); | |
1355 root_->GetDispatcher()->RepostEvent(mouse_event); | |
1356 } | |
1357 } | |
1358 | |
1359 private: | |
1360 Window* root_; | |
1361 int mouse_event_count_; | |
1362 | |
1363 DISALLOW_COPY_AND_ASSIGN(DontResetHeldEventWindowDelegate); | |
1364 }; | |
1365 | |
1366 } // namespace | |
1367 | |
1368 // Verifies RootWindow doesn't reset |RootWindow::held_repostable_event_| after | |
1369 // dispatching. This is done by using DontResetHeldEventWindowDelegate, which | |
1370 // tracks the number of events with ui::EF_SHIFT_DOWN set (all reposted events | |
1371 // have EF_SHIFT_DOWN). When the first event is seen RepostEvent() is used to | |
1372 // schedule another reposted event. | |
1373 TEST_F(WindowEventDispatcherTest, DontResetHeldEvent) { | |
1374 DontResetHeldEventWindowDelegate delegate(root_window()); | |
1375 scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), &delegate)); | |
1376 w1->SetBounds(gfx::Rect(0, 0, 40, 40)); | |
1377 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, | |
1378 gfx::Point(10, 10), gfx::Point(10, 10), | |
1379 ui::EF_SHIFT_DOWN, 0); | |
1380 root_window()->GetDispatcher()->RepostEvent(pressed); | |
1381 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, | |
1382 gfx::Point(10, 10), gfx::Point(10, 10), 0, 0); | |
1383 // Dispatch an event to flush event scheduled by way of RepostEvent(). | |
1384 DispatchEventUsingWindowDispatcher(&pressed2); | |
1385 // Delegate should have seen reposted event (identified by way of | |
1386 // EF_SHIFT_DOWN). Dispatch another event to flush the second | |
1387 // RepostedEvent(). | |
1388 EXPECT_EQ(1, delegate.mouse_event_count()); | |
1389 DispatchEventUsingWindowDispatcher(&pressed2); | |
1390 EXPECT_EQ(2, delegate.mouse_event_count()); | |
1391 } | |
1392 | |
1393 namespace { | |
1394 | |
1395 // See description above DeleteDispatcherFromHeldMouseEvent for details. | |
1396 class DeleteDispatcherFromHeldMouseEventDelegate | |
1397 : public test::TestWindowDelegate { | |
1398 public: | |
1399 explicit DeleteDispatcherFromHeldMouseEventDelegate( | |
1400 WindowEventDispatcher* dispatcher) | |
1401 : dispatcher_(dispatcher), | |
1402 got_mouse_event_(false), | |
1403 got_destroy_(false) { | |
1404 } | |
1405 virtual ~DeleteDispatcherFromHeldMouseEventDelegate() {} | |
1406 | |
1407 bool got_mouse_event() const { return got_mouse_event_; } | |
1408 bool got_destroy() const { return got_destroy_; } | |
1409 | |
1410 // TestWindowDelegate: | |
1411 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
1412 if ((event->flags() & ui::EF_SHIFT_DOWN) != 0) { | |
1413 got_mouse_event_ = true; | |
1414 delete dispatcher_; | |
1415 } | |
1416 } | |
1417 virtual void OnWindowDestroyed() OVERRIDE { | |
1418 got_destroy_ = true; | |
1419 } | |
1420 | |
1421 private: | |
1422 WindowEventDispatcher* dispatcher_; | |
1423 bool got_mouse_event_; | |
1424 bool got_destroy_; | |
1425 | |
1426 DISALLOW_COPY_AND_ASSIGN(DeleteDispatcherFromHeldMouseEventDelegate); | |
1427 }; | |
1428 | |
1429 } // namespace | |
1430 | |
1431 #if defined(USE_OZONE) | |
1432 // Creating multiple WindowTreeHostOzone instances is broken. | |
1433 #define MAYBE_DeleteDispatcherFromHeldMouseEvent DISABLED_DeleteDispatcherFromHe
ldMouseEvent | |
1434 #else | |
1435 #define MAYBE_DeleteDispatcherFromHeldMouseEvent DeleteDispatcherFromHeldMouseEv
ent | |
1436 #endif | |
1437 | |
1438 // Verifies if a RootWindow is deleted from dispatching a held mouse event we | |
1439 // don't crash. | |
1440 TEST_F(WindowEventDispatcherTest, MAYBE_DeleteDispatcherFromHeldMouseEvent) { | |
1441 // Should be deleted by |delegate|. | |
1442 WindowEventDispatcher* d2 = new WindowEventDispatcher( | |
1443 WindowEventDispatcher::CreateParams(gfx::Rect(0, 0, 100, 100))); | |
1444 d2->host()->InitHost(); | |
1445 DeleteDispatcherFromHeldMouseEventDelegate delegate(d2); | |
1446 // Owned by |d2|. | |
1447 Window* w1 = CreateNormalWindow(1, d2->window(), &delegate); | |
1448 w1->SetBounds(gfx::Rect(0, 0, 40, 40)); | |
1449 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, | |
1450 gfx::Point(10, 10), gfx::Point(10, 10), | |
1451 ui::EF_SHIFT_DOWN, 0); | |
1452 d2->RepostEvent(pressed); | |
1453 // RunAllPendingInMessageLoop() to make sure the |pressed| is run. | |
1454 RunAllPendingInMessageLoop(); | |
1455 EXPECT_TRUE(delegate.got_mouse_event()); | |
1456 EXPECT_TRUE(delegate.got_destroy()); | |
1457 } | |
1458 | |
1459 TEST_F(WindowEventDispatcherTest, WindowHideCancelsActiveTouches) { | |
1460 EventFilterRecorder* filter = new EventFilterRecorder; | |
1461 root_window()->SetEventFilter(filter); // passes ownership | |
1462 | |
1463 test::TestWindowDelegate delegate; | |
1464 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
1465 &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window())); | |
1466 | |
1467 gfx::Point position1 = root_window()->bounds().origin(); | |
1468 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, position1, 0, base::TimeDelta()); | |
1469 DispatchEventUsingWindowDispatcher(&press); | |
1470 | |
1471 EXPECT_EQ("TOUCH_PRESSED GESTURE_BEGIN GESTURE_TAP_DOWN", | |
1472 EventTypesToString(filter->GetAndResetEvents())); | |
1473 | |
1474 window->Hide(); | |
1475 | |
1476 EXPECT_EQ("TOUCH_CANCELLED GESTURE_TAP_CANCEL GESTURE_END", | |
1477 EventTypesToString(filter->events())); | |
1478 } | |
1479 | |
1480 TEST_F(WindowEventDispatcherTest, WindowHideCancelsActiveGestures) { | |
1481 EventFilterRecorder* filter = new EventFilterRecorder; | |
1482 root_window()->SetEventFilter(filter); // passes ownership | |
1483 | |
1484 test::TestWindowDelegate delegate; | |
1485 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
1486 &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window())); | |
1487 | |
1488 gfx::Point position1 = root_window()->bounds().origin(); | |
1489 gfx::Point position2 = root_window()->bounds().CenterPoint(); | |
1490 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, position1, 0, base::TimeDelta()); | |
1491 DispatchEventUsingWindowDispatcher(&press); | |
1492 | |
1493 ui::TouchEvent move(ui::ET_TOUCH_MOVED, position2, 0, base::TimeDelta()); | |
1494 DispatchEventUsingWindowDispatcher(&move); | |
1495 | |
1496 ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, position1, 1, base::TimeDelta()); | |
1497 DispatchEventUsingWindowDispatcher(&press2); | |
1498 | |
1499 EXPECT_EQ("TOUCH_PRESSED GESTURE_BEGIN GESTURE_TAP_DOWN TOUCH_MOVED " | |
1500 "GESTURE_TAP_CANCEL GESTURE_SCROLL_BEGIN GESTURE_SCROLL_UPDATE " | |
1501 "TOUCH_PRESSED GESTURE_BEGIN GESTURE_PINCH_BEGIN", | |
1502 EventTypesToString(filter->GetAndResetEvents())); | |
1503 | |
1504 window->Hide(); | |
1505 | |
1506 EXPECT_EQ("TOUCH_CANCELLED GESTURE_PINCH_END GESTURE_END TOUCH_CANCELLED " | |
1507 "GESTURE_SCROLL_END GESTURE_END", | |
1508 EventTypesToString(filter->events())); | |
1509 } | |
1510 | |
1511 // Places two windows side by side. Presses down on one window, and starts a | |
1512 // scroll. Sets capture on the other window and ensures that the "ending" events | |
1513 // aren't sent to the window which gained capture. | |
1514 TEST_F(WindowEventDispatcherTest, EndingEventDoesntRetarget) { | |
1515 scoped_ptr<Window> window1(CreateNormalWindow(1, root_window(), NULL)); | |
1516 window1->SetBounds(gfx::Rect(0, 0, 40, 40)); | |
1517 | |
1518 scoped_ptr<Window> window2(CreateNormalWindow(2, root_window(), NULL)); | |
1519 window2->SetBounds(gfx::Rect(40, 0, 40, 40)); | |
1520 | |
1521 EventFilterRecorder* filter1 = new EventFilterRecorder(); | |
1522 window1->SetEventFilter(filter1); // passes ownership | |
1523 EventFilterRecorder* filter2 = new EventFilterRecorder(); | |
1524 window2->SetEventFilter(filter2); // passes ownership | |
1525 | |
1526 gfx::Point position = window1->bounds().origin(); | |
1527 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, position, 0, base::TimeDelta()); | |
1528 DispatchEventUsingWindowDispatcher(&press); | |
1529 | |
1530 gfx::Point position2 = window1->bounds().CenterPoint(); | |
1531 ui::TouchEvent move(ui::ET_TOUCH_MOVED, position2, 0, base::TimeDelta()); | |
1532 DispatchEventUsingWindowDispatcher(&move); | |
1533 | |
1534 window2->SetCapture(); | |
1535 | |
1536 EXPECT_EQ("TOUCH_PRESSED GESTURE_BEGIN GESTURE_TAP_DOWN TOUCH_MOVED " | |
1537 "GESTURE_TAP_CANCEL GESTURE_SCROLL_BEGIN GESTURE_SCROLL_UPDATE " | |
1538 "TOUCH_CANCELLED GESTURE_SCROLL_END GESTURE_END", | |
1539 EventTypesToString(filter1->events())); | |
1540 | |
1541 EXPECT_TRUE(filter2->events().empty()); | |
1542 } | |
1543 | |
1544 namespace { | |
1545 | |
1546 // This class creates and manages a window which is destroyed as soon as | |
1547 // capture is lost. This is the case for the drag and drop capture window. | |
1548 class CaptureWindowTracker : public test::TestWindowDelegate { | |
1549 public: | |
1550 CaptureWindowTracker() {} | |
1551 virtual ~CaptureWindowTracker() {} | |
1552 | |
1553 void CreateCaptureWindow(aura::Window* root_window) { | |
1554 capture_window_.reset(test::CreateTestWindowWithDelegate( | |
1555 this, -1234, gfx::Rect(20, 20, 20, 20), root_window)); | |
1556 capture_window_->SetCapture(); | |
1557 } | |
1558 | |
1559 void reset() { | |
1560 capture_window_.reset(); | |
1561 } | |
1562 | |
1563 virtual void OnCaptureLost() OVERRIDE { | |
1564 capture_window_.reset(); | |
1565 } | |
1566 | |
1567 virtual void OnWindowDestroyed() OVERRIDE { | |
1568 TestWindowDelegate::OnWindowDestroyed(); | |
1569 capture_window_.reset(); | |
1570 } | |
1571 | |
1572 aura::Window* capture_window() { return capture_window_.get(); } | |
1573 | |
1574 private: | |
1575 scoped_ptr<aura::Window> capture_window_; | |
1576 | |
1577 DISALLOW_COPY_AND_ASSIGN(CaptureWindowTracker); | |
1578 }; | |
1579 | |
1580 } | |
1581 | |
1582 // Verifies handling loss of capture by the capture window being hidden. | |
1583 TEST_F(WindowEventDispatcherTest, CaptureWindowHidden) { | |
1584 CaptureWindowTracker capture_window_tracker; | |
1585 capture_window_tracker.CreateCaptureWindow(root_window()); | |
1586 capture_window_tracker.capture_window()->Hide(); | |
1587 EXPECT_EQ(NULL, capture_window_tracker.capture_window()); | |
1588 } | |
1589 | |
1590 // Verifies handling loss of capture by the capture window being destroyed. | |
1591 TEST_F(WindowEventDispatcherTest, CaptureWindowDestroyed) { | |
1592 CaptureWindowTracker capture_window_tracker; | |
1593 capture_window_tracker.CreateCaptureWindow(root_window()); | |
1594 capture_window_tracker.reset(); | |
1595 EXPECT_EQ(NULL, capture_window_tracker.capture_window()); | |
1596 } | |
1597 | |
1598 class ExitMessageLoopOnMousePress : public test::TestEventHandler { | |
1599 public: | |
1600 ExitMessageLoopOnMousePress() {} | |
1601 virtual ~ExitMessageLoopOnMousePress() {} | |
1602 | |
1603 protected: | |
1604 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
1605 test::TestEventHandler::OnMouseEvent(event); | |
1606 if (event->type() == ui::ET_MOUSE_PRESSED) | |
1607 base::MessageLoopForUI::current()->Quit(); | |
1608 } | |
1609 | |
1610 private: | |
1611 DISALLOW_COPY_AND_ASSIGN(ExitMessageLoopOnMousePress); | |
1612 }; | |
1613 | |
1614 class WindowEventDispatcherTestWithMessageLoop | |
1615 : public WindowEventDispatcherTest { | |
1616 public: | |
1617 WindowEventDispatcherTestWithMessageLoop() {} | |
1618 virtual ~WindowEventDispatcherTestWithMessageLoop() {} | |
1619 | |
1620 void RunTest() { | |
1621 // Reset any event the window may have received when bringing up the window | |
1622 // (e.g. mouse-move events if the mouse cursor is over the window). | |
1623 handler_.Reset(); | |
1624 | |
1625 // Start a nested message-loop, post an event to be dispatched, and then | |
1626 // terminate the message-loop. When the message-loop unwinds and gets back, | |
1627 // the reposted event should not have fired. | |
1628 ui::MouseEvent mouse(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10), | |
1629 gfx::Point(10, 10), ui::EF_NONE, ui::EF_NONE); | |
1630 message_loop()->PostTask(FROM_HERE, | |
1631 base::Bind(&WindowEventDispatcher::RepostEvent, | |
1632 base::Unretained(dispatcher()), | |
1633 mouse)); | |
1634 message_loop()->PostTask(FROM_HERE, | |
1635 message_loop()->QuitClosure()); | |
1636 | |
1637 base::MessageLoop::ScopedNestableTaskAllower allow(message_loop()); | |
1638 base::RunLoop loop; | |
1639 loop.Run(); | |
1640 EXPECT_EQ(0, handler_.num_mouse_events()); | |
1641 | |
1642 // Let the current message-loop run. The event-handler will terminate the | |
1643 // message-loop when it receives the reposted event. | |
1644 } | |
1645 | |
1646 base::MessageLoop* message_loop() { | |
1647 return base::MessageLoopForUI::current(); | |
1648 } | |
1649 | |
1650 protected: | |
1651 virtual void SetUp() OVERRIDE { | |
1652 WindowEventDispatcherTest::SetUp(); | |
1653 window_.reset(CreateNormalWindow(1, root_window(), NULL)); | |
1654 window_->AddPreTargetHandler(&handler_); | |
1655 } | |
1656 | |
1657 virtual void TearDown() OVERRIDE { | |
1658 window_.reset(); | |
1659 WindowEventDispatcherTest::TearDown(); | |
1660 } | |
1661 | |
1662 private: | |
1663 scoped_ptr<Window> window_; | |
1664 ExitMessageLoopOnMousePress handler_; | |
1665 | |
1666 DISALLOW_COPY_AND_ASSIGN(WindowEventDispatcherTestWithMessageLoop); | |
1667 }; | |
1668 | |
1669 TEST_F(WindowEventDispatcherTestWithMessageLoop, EventRepostedInNonNestedLoop) { | |
1670 CHECK(!message_loop()->is_running()); | |
1671 // Perform the test in a callback, so that it runs after the message-loop | |
1672 // starts. | |
1673 message_loop()->PostTask( | |
1674 FROM_HERE, base::Bind( | |
1675 &WindowEventDispatcherTestWithMessageLoop::RunTest, | |
1676 base::Unretained(this))); | |
1677 message_loop()->Run(); | |
1678 } | |
1679 | |
1680 class WindowEventDispatcherTestInHighDPI : public WindowEventDispatcherTest { | |
1681 public: | |
1682 WindowEventDispatcherTestInHighDPI() {} | |
1683 virtual ~WindowEventDispatcherTestInHighDPI() {} | |
1684 | |
1685 protected: | |
1686 virtual void SetUp() OVERRIDE { | |
1687 WindowEventDispatcherTest::SetUp(); | |
1688 test_screen()->SetDeviceScaleFactor(2.f); | |
1689 } | |
1690 }; | |
1691 | |
1692 TEST_F(WindowEventDispatcherTestInHighDPI, EventLocationTransform) { | |
1693 test::TestWindowDelegate delegate; | |
1694 scoped_ptr<aura::Window> child(test::CreateTestWindowWithDelegate(&delegate, | |
1695 1234, gfx::Rect(20, 20, 100, 100), root_window())); | |
1696 child->Show(); | |
1697 | |
1698 test::TestEventHandler handler_child; | |
1699 test::TestEventHandler handler_root; | |
1700 root_window()->AddPreTargetHandler(&handler_root); | |
1701 child->AddPreTargetHandler(&handler_child); | |
1702 | |
1703 { | |
1704 ui::MouseEvent move(ui::ET_MOUSE_MOVED, | |
1705 gfx::Point(30, 30), gfx::Point(30, 30), | |
1706 ui::EF_NONE, ui::EF_NONE); | |
1707 DispatchEventUsingWindowDispatcher(&move); | |
1708 EXPECT_EQ(0, handler_child.num_mouse_events()); | |
1709 EXPECT_EQ(1, handler_root.num_mouse_events()); | |
1710 } | |
1711 | |
1712 { | |
1713 ui::MouseEvent move(ui::ET_MOUSE_MOVED, | |
1714 gfx::Point(50, 50), gfx::Point(50, 50), | |
1715 ui::EF_NONE, ui::EF_NONE); | |
1716 DispatchEventUsingWindowDispatcher(&move); | |
1717 // The child receives an ENTER, and a MOVED event. | |
1718 EXPECT_EQ(2, handler_child.num_mouse_events()); | |
1719 // The root receives both the ENTER and the MOVED events dispatched to | |
1720 // |child|, as well as an EXIT event. | |
1721 EXPECT_EQ(3, handler_root.num_mouse_events()); | |
1722 } | |
1723 | |
1724 child->RemovePreTargetHandler(&handler_child); | |
1725 root_window()->RemovePreTargetHandler(&handler_root); | |
1726 } | |
1727 | |
1728 TEST_F(WindowEventDispatcherTestInHighDPI, TouchMovesHeldOnScroll) { | |
1729 EventFilterRecorder* filter = new EventFilterRecorder; | |
1730 root_window()->SetEventFilter(filter); | |
1731 test::TestWindowDelegate delegate; | |
1732 HoldPointerOnScrollHandler handler(dispatcher(), filter); | |
1733 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
1734 &delegate, 1, gfx::Rect(50, 50, 100, 100), root_window())); | |
1735 window->AddPreTargetHandler(&handler); | |
1736 | |
1737 test::EventGenerator generator(root_window()); | |
1738 generator.GestureScrollSequence( | |
1739 gfx::Point(120, 120), gfx::Point(20, 120), | |
1740 base::TimeDelta::FromMilliseconds(100), 25); | |
1741 | |
1742 // |handler| will have reset |filter| and started holding the touch-move | |
1743 // events when scrolling started. At the end of the scroll (i.e. upon | |
1744 // touch-release), the held touch-move event will have been dispatched first, | |
1745 // along with the subsequent events (i.e. touch-release, scroll-end, and | |
1746 // gesture-end). | |
1747 const EventFilterRecorder::Events& events = filter->events(); | |
1748 EXPECT_EQ("TOUCH_MOVED TOUCH_RELEASED GESTURE_SCROLL_END GESTURE_END", | |
1749 EventTypesToString(events)); | |
1750 ASSERT_EQ(2u, filter->touch_locations().size()); | |
1751 EXPECT_EQ(gfx::Point(-40, 10).ToString(), | |
1752 filter->touch_locations()[0].ToString()); | |
1753 EXPECT_EQ(gfx::Point(-40, 10).ToString(), | |
1754 filter->touch_locations()[1].ToString()); | |
1755 } | |
1756 | |
1757 class SelfDestructDelegate : public test::TestWindowDelegate { | |
1758 public: | |
1759 SelfDestructDelegate() {} | |
1760 virtual ~SelfDestructDelegate() {} | |
1761 | |
1762 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { | |
1763 window_.reset(); | |
1764 } | |
1765 | |
1766 void set_window(scoped_ptr<aura::Window> window) { | |
1767 window_ = window.Pass(); | |
1768 } | |
1769 bool has_window() const { return !!window_.get(); } | |
1770 | |
1771 private: | |
1772 scoped_ptr<aura::Window> window_; | |
1773 DISALLOW_COPY_AND_ASSIGN(SelfDestructDelegate); | |
1774 }; | |
1775 | |
1776 TEST_F(WindowEventDispatcherTest, SynthesizedLocatedEvent) { | |
1777 test::EventGenerator generator(root_window()); | |
1778 generator.MoveMouseTo(10, 10); | |
1779 EXPECT_EQ("10,10", | |
1780 Env::GetInstance()->last_mouse_location().ToString()); | |
1781 | |
1782 // Synthesized event should not update the mouse location. | |
1783 ui::MouseEvent mouseev(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), | |
1784 ui::EF_IS_SYNTHESIZED, 0); | |
1785 generator.Dispatch(&mouseev); | |
1786 EXPECT_EQ("10,10", | |
1787 Env::GetInstance()->last_mouse_location().ToString()); | |
1788 | |
1789 generator.MoveMouseTo(0, 0); | |
1790 EXPECT_EQ("0,0", | |
1791 Env::GetInstance()->last_mouse_location().ToString()); | |
1792 | |
1793 // Make sure the location gets updated when a syntheiszed enter | |
1794 // event destroyed the window. | |
1795 SelfDestructDelegate delegate; | |
1796 scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( | |
1797 &delegate, 1, gfx::Rect(50, 50, 100, 100), root_window())); | |
1798 delegate.set_window(window.Pass()); | |
1799 EXPECT_TRUE(delegate.has_window()); | |
1800 | |
1801 generator.MoveMouseTo(100, 100); | |
1802 EXPECT_FALSE(delegate.has_window()); | |
1803 EXPECT_EQ("100,100", | |
1804 Env::GetInstance()->last_mouse_location().ToString()); | |
1805 } | |
1806 | |
1807 } // namespace aura | |
OLD | NEW |