blob: f3d080f1179c2ff60395707f1b3c2b9763f688bb [file] [log] [blame]
// 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 "base/macros.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 EventHandler {
public:
TestTarget() : parent_(NULL), valid_(true) {
SetTargetHandler(this);
}
~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_; }
std::unique_ptr<EventTargetIterator> GetChildIterator() const override {
return nullptr;
}
EventTargeter* GetEventTargeter() override { return NULL; }
TestTarget* parent_;
std::vector<int> handler_list_;
bool valid_;
DISALLOW_COPY_AND_ASSIGN(TestTarget);
};
class TestEventHandler : public EventHandler {
public:
explicit TestEventHandler(int id) : id_(id) {}
~TestEventHandler() override {
if (pre_target_)
pre_target_->RemovePreTargetHandler(this);
}
void set_pre_target(EventTarget* pre_target) { pre_target_ = pre_target; }
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();
}
const int id_;
EventResult event_result_ = ER_UNHANDLED;
bool expect_pre_target_ = false;
bool expect_post_target_ = false;
bool received_pre_target_ = false;
EventTarget* pre_target_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TestEventHandler);
};
typedef CancelModeEvent 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),
ui::EventTimeForNow(), 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());
parent.RemovePreTargetHandler(&h1);
parent.RemovePreTargetHandler(&h2);
child.RemovePreTargetHandler(&h3);
child.RemovePreTargetHandler(&h4);
}
// 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),
ui::EventTimeForNow(), 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());
target.RemovePreTargetHandler(&handler);
}
// 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),
ui::EventTimeForNow(), 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]);
target.RemovePreTargetHandler(&h1);
target.RemovePreTargetHandler(&handler);
target.RemovePreTargetHandler(&h2);
}
// 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]);
target.RemovePreTargetHandler(&h1);
target.RemovePreTargetHandler(&handler);
target.RemovePreTargetHandler(&h2);
}
// 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),
ui::EventTimeForNow(), 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]);
target.RemovePostTargetHandler(&h1);
target.RemovePostTargetHandler(&handler);
target.RemovePostTargetHandler(&h2);
}
// 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]);
target.RemovePostTargetHandler(&h1);
target.RemovePostTargetHandler(&handler);
target.RemovePostTargetHandler(&h2);
}
}
// 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),
ui::EventTimeForNow(), 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]);
target.RemovePreTargetHandler(&h1);
target.RemovePreTargetHandler(&invalidate_handler);
target.RemovePreTargetHandler(&h3);
}
// 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);
h3->set_pre_target(&target);
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),
ui::EventTimeForNow(), 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]);
target.RemovePreTargetHandler(&h1);
target.RemovePreTargetHandler(&handle_destroyer);
}
// 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);
h3->set_pre_target(&target);
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]);
target.RemovePreTargetHandler(&h1);
target.RemovePreTargetHandler(&handle_destroyer);
}
}
// 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);
h3->set_pre_target(&target);
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),
ui::EventTimeForNow(), 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]);
target.RemovePreTargetHandler(&h1);
target.RemovePreTargetHandler(&destroyer);
}
// 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);
h3->set_pre_target(&target);
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]);
target.RemovePreTargetHandler(&h1);
target.RemovePreTargetHandler(&destroyer);
}
}
} // namespace ui