| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/events/event_dispatcher.h" |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_dispatcher.h" |
| #include "ui/events/event_target.h" |
| #include "ui/events/event_target_iterator.h" |
| #include "ui/events/event_utils.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| class TestTarget : public EventTarget { |
| public: |
| TestTarget() : parent_(NULL), valid_(true) {} |
| ~TestTarget() override {} |
| |
| void set_parent(TestTarget* parent) { parent_ = parent; } |
| |
| bool valid() const { return valid_; } |
| void set_valid(bool valid) { valid_ = valid; } |
| |
| void AddHandlerId(int id) { |
| handler_list_.push_back(id); |
| } |
| |
| const std::vector<int>& handler_list() const { return handler_list_; } |
| |
| void Reset() { |
| handler_list_.clear(); |
| valid_ = true; |
| } |
| |
| private: |
| // Overridden from EventTarget: |
| bool CanAcceptEvent(const ui::Event& event) override { |
| return true; |
| } |
| |
| EventTarget* GetParentTarget() override { |
| return parent_; |
| } |
| |
| scoped_ptr<EventTargetIterator> GetChildIterator() override { |
| return scoped_ptr<EventTargetIterator>(); |
| } |
| |
| EventTargeter* GetEventTargeter() override { |
| return NULL; |
| } |
| |
| TestTarget* parent_; |
| std::vector<int> handler_list_; |
| bool valid_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestTarget); |
| }; |
| |
| class TestEventHandler : public EventHandler { |
| public: |
| TestEventHandler(int id) |
| : id_(id), |
| event_result_(ER_UNHANDLED), |
| expect_pre_target_(false), |
| expect_post_target_(false), |
| received_pre_target_(false) { |
| } |
| |
| ~TestEventHandler() override {} |
| |
| virtual void ReceivedEvent(Event* event) { |
| static_cast<TestTarget*>(event->target())->AddHandlerId(id_); |
| if (event->phase() == ui::EP_POSTTARGET) { |
| EXPECT_TRUE(expect_post_target_); |
| if (expect_pre_target_) |
| EXPECT_TRUE(received_pre_target_); |
| } else if (event->phase() == ui::EP_PRETARGET) { |
| EXPECT_TRUE(expect_pre_target_); |
| received_pre_target_ = true; |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void set_event_result(EventResult result) { event_result_ = result; } |
| |
| void set_expect_pre_target(bool expect) { expect_pre_target_ = expect; } |
| void set_expect_post_target(bool expect) { expect_post_target_ = expect; } |
| |
| private: |
| // Overridden from EventHandler: |
| void OnEvent(Event* event) override { |
| ui::EventHandler::OnEvent(event); |
| ReceivedEvent(event); |
| SetStatusOnEvent(event); |
| } |
| |
| void SetStatusOnEvent(Event* event) { |
| if (event_result_ & ui::ER_CONSUMED) |
| event->StopPropagation(); |
| if (event_result_ & ui::ER_HANDLED) |
| event->SetHandled(); |
| } |
| |
| int id_; |
| EventResult event_result_; |
| bool expect_pre_target_; |
| bool expect_post_target_; |
| bool received_pre_target_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestEventHandler); |
| }; |
| |
| class NonCancelableEvent : public Event { |
| public: |
| NonCancelableEvent() |
| : Event(ui::ET_CANCEL_MODE, ui::EventTimeForNow(), 0) { |
| set_cancelable(false); |
| } |
| |
| ~NonCancelableEvent() override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(NonCancelableEvent); |
| }; |
| |
| // Destroys the dispatcher-delegate when it receives any event. |
| class EventHandlerDestroyDispatcherDelegate : public TestEventHandler { |
| public: |
| EventHandlerDestroyDispatcherDelegate(EventDispatcherDelegate* delegate, |
| int id) |
| : TestEventHandler(id), |
| dispatcher_delegate_(delegate) { |
| } |
| |
| ~EventHandlerDestroyDispatcherDelegate() override {} |
| |
| private: |
| void ReceivedEvent(Event* event) override { |
| TestEventHandler::ReceivedEvent(event); |
| delete dispatcher_delegate_; |
| } |
| |
| EventDispatcherDelegate* dispatcher_delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(EventHandlerDestroyDispatcherDelegate); |
| }; |
| |
| // Invalidates the target when it receives any event. |
| class InvalidateTargetEventHandler : public TestEventHandler { |
| public: |
| explicit InvalidateTargetEventHandler(int id) : TestEventHandler(id) {} |
| ~InvalidateTargetEventHandler() override {} |
| |
| private: |
| void ReceivedEvent(Event* event) override { |
| TestEventHandler::ReceivedEvent(event); |
| TestTarget* target = static_cast<TestTarget*>(event->target()); |
| target->set_valid(false); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(InvalidateTargetEventHandler); |
| }; |
| |
| // Destroys a second event handler when this handler gets an event. |
| // Optionally also destroys the dispatcher. |
| class EventHandlerDestroyer : public TestEventHandler { |
| public: |
| EventHandlerDestroyer(int id, EventHandler* destroy) |
| : TestEventHandler(id), |
| to_destroy_(destroy), |
| dispatcher_delegate_(NULL) { |
| } |
| |
| ~EventHandlerDestroyer() override { |
| CHECK(!to_destroy_); |
| } |
| |
| void set_dispatcher_delegate(EventDispatcherDelegate* dispatcher_delegate) { |
| dispatcher_delegate_ = dispatcher_delegate; |
| } |
| |
| private: |
| void ReceivedEvent(Event* event) override { |
| TestEventHandler::ReceivedEvent(event); |
| delete to_destroy_; |
| to_destroy_ = NULL; |
| |
| if (dispatcher_delegate_) { |
| delete dispatcher_delegate_; |
| dispatcher_delegate_ = NULL; |
| } |
| } |
| |
| EventHandler* to_destroy_; |
| EventDispatcherDelegate* dispatcher_delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(EventHandlerDestroyer); |
| }; |
| |
| class TestEventDispatcher : public EventDispatcherDelegate { |
| public: |
| TestEventDispatcher() {} |
| |
| ~TestEventDispatcher() override {} |
| |
| EventDispatchDetails ProcessEvent(EventTarget* target, Event* event) { |
| return DispatchEvent(target, event); |
| } |
| |
| private: |
| // Overridden from EventDispatcherDelegate: |
| bool CanDispatchToTarget(EventTarget* target) override { |
| TestTarget* test_target = static_cast<TestTarget*>(target); |
| return test_target->valid(); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(TestEventDispatcher); |
| }; |
| |
| } // namespace |
| |
| TEST(EventDispatcherTest, EventDispatchOrder) { |
| TestEventDispatcher dispatcher; |
| TestTarget parent, child; |
| TestEventHandler h1(1), h2(2), h3(3), h4(4); |
| TestEventHandler h5(5), h6(6), h7(7), h8(8); |
| |
| child.set_parent(&parent); |
| |
| parent.AddPreTargetHandler(&h1); |
| parent.AddPreTargetHandler(&h2); |
| |
| child.AddPreTargetHandler(&h3); |
| child.AddPreTargetHandler(&h4); |
| |
| h1.set_expect_pre_target(true); |
| h2.set_expect_pre_target(true); |
| h3.set_expect_pre_target(true); |
| h4.set_expect_pre_target(true); |
| |
| child.AddPostTargetHandler(&h5); |
| child.AddPostTargetHandler(&h6); |
| |
| parent.AddPostTargetHandler(&h7); |
| parent.AddPostTargetHandler(&h8); |
| |
| h5.set_expect_post_target(true); |
| h6.set_expect_post_target(true); |
| h7.set_expect_post_target(true); |
| h8.set_expect_post_target(true); |
| |
| MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), |
| gfx::Point(3, 4), 0, 0); |
| Event::DispatcherApi event_mod(&mouse); |
| dispatcher.ProcessEvent(&child, &mouse); |
| EXPECT_FALSE(mouse.stopped_propagation()); |
| EXPECT_FALSE(mouse.handled()); |
| |
| { |
| int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; |
| EXPECT_EQ( |
| std::vector<int>(expected, expected + sizeof(expected) / sizeof(int)), |
| child.handler_list()); |
| } |
| |
| child.Reset(); |
| event_mod.set_phase(EP_PREDISPATCH); |
| event_mod.set_result(ER_UNHANDLED); |
| |
| h1.set_event_result(ER_HANDLED); |
| dispatcher.ProcessEvent(&child, &mouse); |
| EXPECT_EQ(EP_POSTDISPATCH, mouse.phase()); |
| EXPECT_FALSE(mouse.stopped_propagation()); |
| EXPECT_TRUE(mouse.handled()); |
| { |
| // |h1| marks the event as handled. So only the pre-target handlers should |
| // receive the event. |
| int expected[] = { 1, 2, 3, 4 }; |
| EXPECT_EQ( |
| std::vector<int>(expected, expected + sizeof(expected) / sizeof(int)), |
| child.handler_list()); |
| } |
| |
| child.Reset(); |
| event_mod.set_phase(EP_PREDISPATCH); |
| event_mod.set_result(ER_UNHANDLED); |
| |
| int nexpected[] = { 1, 2, 3, 4, 5 }; |
| h1.set_event_result(ER_UNHANDLED); |
| h5.set_event_result(ER_CONSUMED); |
| dispatcher.ProcessEvent(&child, &mouse); |
| EXPECT_EQ(EP_POSTDISPATCH, mouse.phase()); |
| EXPECT_TRUE(mouse.stopped_propagation()); |
| EXPECT_TRUE(mouse.handled()); |
| EXPECT_EQ( |
| std::vector<int>(nexpected, nexpected + sizeof(nexpected) / sizeof(int)), |
| child.handler_list()); |
| |
| child.Reset(); |
| event_mod.set_phase(EP_PREDISPATCH); |
| event_mod.set_result(ER_UNHANDLED); |
| |
| int exp[] = { 1 }; |
| h1.set_event_result(ER_CONSUMED); |
| dispatcher.ProcessEvent(&child, &mouse); |
| EXPECT_EQ(EP_POSTDISPATCH, mouse.phase()); |
| EXPECT_TRUE(mouse.stopped_propagation()); |
| EXPECT_TRUE(mouse.handled()); |
| EXPECT_EQ( |
| std::vector<int>(exp, exp + sizeof(exp) / sizeof(int)), |
| child.handler_list()); |
| } |
| |
| // Tests that the event-phases are correct. |
| TEST(EventDispatcherTest, EventDispatchPhase) { |
| TestEventDispatcher dispatcher; |
| TestTarget target; |
| |
| TestEventHandler handler(11); |
| |
| target.AddPreTargetHandler(&handler); |
| target.AddPostTargetHandler(&handler); |
| handler.set_expect_pre_target(true); |
| handler.set_expect_post_target(true); |
| |
| MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), |
| gfx::Point(3, 4), 0, 0); |
| Event::DispatcherApi event_mod(&mouse); |
| dispatcher.ProcessEvent(&target, &mouse); |
| EXPECT_EQ(ER_UNHANDLED, mouse.result()); |
| |
| int handlers[] = { 11, 11 }; |
| EXPECT_EQ( |
| std::vector<int>(handlers, handlers + sizeof(handlers) / sizeof(int)), |
| target.handler_list()); |
| } |
| |
| // Tests that if the dispatcher is destroyed in the middle of pre or post-target |
| // dispatching events, it doesn't cause a crash. |
| TEST(EventDispatcherTest, EventDispatcherDestroyedDuringDispatch) { |
| // Test for pre-target first. |
| { |
| TestEventDispatcher* dispatcher = new TestEventDispatcher(); |
| TestTarget target; |
| EventHandlerDestroyDispatcherDelegate handler(dispatcher, 5); |
| TestEventHandler h1(1), h2(2); |
| |
| target.AddPreTargetHandler(&h1); |
| target.AddPreTargetHandler(&handler); |
| target.AddPreTargetHandler(&h2); |
| |
| h1.set_expect_pre_target(true); |
| handler.set_expect_pre_target(true); |
| // |h2| should not receive any events at all since |handler| will have |
| // destroyed the dispatcher. |
| h2.set_expect_pre_target(false); |
| |
| MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), |
| gfx::Point(3, 4), 0, 0); |
| EventDispatchDetails details = dispatcher->ProcessEvent(&target, &mouse); |
| EXPECT_TRUE(details.dispatcher_destroyed); |
| EXPECT_EQ(ER_CONSUMED, mouse.result()); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(5, target.handler_list()[1]); |
| } |
| |
| // Test for non-cancelable event. |
| { |
| TestEventDispatcher* dispatcher = new TestEventDispatcher(); |
| TestTarget target; |
| EventHandlerDestroyDispatcherDelegate handler(dispatcher, 5); |
| TestEventHandler h1(1), h2(2); |
| |
| target.AddPreTargetHandler(&h1); |
| target.AddPreTargetHandler(&handler); |
| target.AddPreTargetHandler(&h2); |
| |
| h1.set_expect_pre_target(true); |
| handler.set_expect_pre_target(true); |
| // |h2| should not receive any events at all since |handler| will have |
| // destroyed the dispatcher. |
| h2.set_expect_pre_target(false); |
| |
| NonCancelableEvent event; |
| EventDispatchDetails details = dispatcher->ProcessEvent(&target, &event); |
| EXPECT_TRUE(details.dispatcher_destroyed); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(5, target.handler_list()[1]); |
| } |
| |
| // Now test for post-target. |
| { |
| TestEventDispatcher* dispatcher = new TestEventDispatcher(); |
| TestTarget target; |
| EventHandlerDestroyDispatcherDelegate handler(dispatcher, 5); |
| TestEventHandler h1(1), h2(2); |
| |
| target.AddPostTargetHandler(&h1); |
| target.AddPostTargetHandler(&handler); |
| target.AddPostTargetHandler(&h2); |
| |
| h1.set_expect_post_target(true); |
| handler.set_expect_post_target(true); |
| // |h2| should not receive any events at all since |handler| will have |
| // destroyed the dispatcher. |
| h2.set_expect_post_target(false); |
| |
| MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), |
| gfx::Point(3, 4), 0, 0); |
| EventDispatchDetails details = dispatcher->ProcessEvent(&target, &mouse); |
| EXPECT_TRUE(details.dispatcher_destroyed); |
| EXPECT_EQ(ER_CONSUMED, mouse.result()); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(5, target.handler_list()[1]); |
| } |
| |
| // Test for non-cancelable event. |
| { |
| TestEventDispatcher* dispatcher = new TestEventDispatcher(); |
| TestTarget target; |
| EventHandlerDestroyDispatcherDelegate handler(dispatcher, 5); |
| TestEventHandler h1(1), h2(2); |
| |
| target.AddPostTargetHandler(&h1); |
| target.AddPostTargetHandler(&handler); |
| target.AddPostTargetHandler(&h2); |
| |
| h1.set_expect_post_target(true); |
| handler.set_expect_post_target(true); |
| // |h2| should not receive any events at all since |handler| will have |
| // destroyed the dispatcher. |
| h2.set_expect_post_target(false); |
| |
| NonCancelableEvent event; |
| EventDispatchDetails details = dispatcher->ProcessEvent(&target, &event); |
| EXPECT_TRUE(details.dispatcher_destroyed); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(5, target.handler_list()[1]); |
| } |
| } |
| |
| // Tests that a target becoming invalid in the middle of pre- or post-target |
| // event processing aborts processing. |
| TEST(EventDispatcherTest, EventDispatcherInvalidateTarget) { |
| TestEventDispatcher dispatcher; |
| TestTarget target; |
| TestEventHandler h1(1); |
| InvalidateTargetEventHandler invalidate_handler(2); |
| TestEventHandler h3(3); |
| |
| target.AddPreTargetHandler(&h1); |
| target.AddPreTargetHandler(&invalidate_handler); |
| target.AddPreTargetHandler(&h3); |
| |
| h1.set_expect_pre_target(true); |
| invalidate_handler.set_expect_pre_target(true); |
| // |h3| should not receive events as the target will be invalidated. |
| h3.set_expect_pre_target(false); |
| |
| MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4), 0, |
| 0); |
| EventDispatchDetails details = dispatcher.ProcessEvent(&target, &mouse); |
| EXPECT_FALSE(details.dispatcher_destroyed); |
| EXPECT_TRUE(details.target_destroyed); |
| EXPECT_FALSE(target.valid()); |
| EXPECT_TRUE(mouse.stopped_propagation()); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(2, target.handler_list()[1]); |
| |
| // Test for non-cancelable event. |
| target.Reset(); |
| NonCancelableEvent event; |
| details = dispatcher.ProcessEvent(&target, &event); |
| EXPECT_FALSE(details.dispatcher_destroyed); |
| EXPECT_TRUE(details.target_destroyed); |
| EXPECT_FALSE(target.valid()); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(2, target.handler_list()[1]); |
| } |
| |
| // Tests that if an event-handler gets destroyed during event-dispatch, it does |
| // not cause a crash. |
| TEST(EventDispatcherTest, EventHandlerDestroyedDuringDispatch) { |
| { |
| TestEventDispatcher dispatcher; |
| TestTarget target; |
| TestEventHandler h1(1); |
| TestEventHandler* h3 = new TestEventHandler(3); |
| EventHandlerDestroyer handle_destroyer(2, h3); |
| |
| target.AddPreTargetHandler(&h1); |
| target.AddPreTargetHandler(&handle_destroyer); |
| target.AddPreTargetHandler(h3); |
| |
| h1.set_expect_pre_target(true); |
| handle_destroyer.set_expect_pre_target(true); |
| // |h3| should not receive events since |handle_destroyer| will have |
| // destroyed it. |
| h3->set_expect_pre_target(false); |
| |
| MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4), 0, |
| 0); |
| EventDispatchDetails details = dispatcher.ProcessEvent(&target, &mouse); |
| EXPECT_FALSE(details.dispatcher_destroyed); |
| EXPECT_FALSE(details.target_destroyed); |
| EXPECT_FALSE(mouse.stopped_propagation()); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(2, target.handler_list()[1]); |
| } |
| |
| // Test for non-cancelable events. |
| { |
| TestEventDispatcher dispatcher; |
| TestTarget target; |
| TestEventHandler h1(1); |
| TestEventHandler* h3 = new TestEventHandler(3); |
| EventHandlerDestroyer handle_destroyer(2, h3); |
| |
| target.AddPreTargetHandler(&h1); |
| target.AddPreTargetHandler(&handle_destroyer); |
| target.AddPreTargetHandler(h3); |
| |
| h1.set_expect_pre_target(true); |
| handle_destroyer.set_expect_pre_target(true); |
| h3->set_expect_pre_target(false); |
| |
| NonCancelableEvent event; |
| EventDispatchDetails details = dispatcher.ProcessEvent(&target, &event); |
| EXPECT_FALSE(details.dispatcher_destroyed); |
| EXPECT_FALSE(details.target_destroyed); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(2, target.handler_list()[1]); |
| } |
| } |
| |
| // Tests that things work correctly if an event-handler destroys both the |
| // dispatcher and a handler. |
| TEST(EventDispatcherTest, EventHandlerAndDispatcherDestroyedDuringDispatch) { |
| { |
| TestEventDispatcher* dispatcher = new TestEventDispatcher(); |
| TestTarget target; |
| TestEventHandler h1(1); |
| TestEventHandler* h3 = new TestEventHandler(3); |
| EventHandlerDestroyer destroyer(2, h3); |
| |
| target.AddPreTargetHandler(&h1); |
| target.AddPreTargetHandler(&destroyer); |
| target.AddPreTargetHandler(h3); |
| |
| h1.set_expect_pre_target(true); |
| destroyer.set_expect_pre_target(true); |
| destroyer.set_dispatcher_delegate(dispatcher); |
| // |h3| should not receive events since |destroyer| will have destroyed |
| // it. |
| h3->set_expect_pre_target(false); |
| |
| MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4), 0, |
| 0); |
| EventDispatchDetails details = dispatcher->ProcessEvent(&target, &mouse); |
| EXPECT_TRUE(details.dispatcher_destroyed); |
| EXPECT_TRUE(mouse.stopped_propagation()); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(2, target.handler_list()[1]); |
| } |
| |
| // Test for non-cancelable events. |
| { |
| TestEventDispatcher* dispatcher = new TestEventDispatcher(); |
| TestTarget target; |
| TestEventHandler h1(1); |
| TestEventHandler* h3 = new TestEventHandler(3); |
| EventHandlerDestroyer destroyer(2, h3); |
| |
| target.AddPreTargetHandler(&h1); |
| target.AddPreTargetHandler(&destroyer); |
| target.AddPreTargetHandler(h3); |
| |
| h1.set_expect_pre_target(true); |
| destroyer.set_expect_pre_target(true); |
| destroyer.set_dispatcher_delegate(dispatcher); |
| // |h3| should not receive events since |destroyer| will have destroyed |
| // it. |
| h3->set_expect_pre_target(false); |
| |
| NonCancelableEvent event; |
| EventDispatchDetails details = dispatcher->ProcessEvent(&target, &event); |
| EXPECT_TRUE(details.dispatcher_destroyed); |
| EXPECT_EQ(2U, target.handler_list().size()); |
| EXPECT_EQ(1, target.handler_list()[0]); |
| EXPECT_EQ(2, target.handler_list()[1]); |
| } |
| } |
| |
| } // namespace ui |