123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- // Copyright 2014 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_rewriter.h"
- #include <list>
- #include <map>
- #include <set>
- #include <utility>
- #include "base/check_op.h"
- #include "base/memory/raw_ptr.h"
- #include "base/notreached.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "ui/events/keycodes/dom/dom_code.h"
- #include "ui/events/keycodes/keyboard_codes.h"
- #include "ui/events/test/test_event_source.h"
- namespace ui {
- namespace {
- // TestEventRewriteSink is set up with a sequence of event types,
- // and fails if the events received via OnEventFromSource() do not match
- // this sequence. These expected event types are consumed on receipt.
- class TestEventRewriteSink : public EventSink {
- public:
- TestEventRewriteSink() {}
- TestEventRewriteSink(const TestEventRewriteSink&) = delete;
- TestEventRewriteSink& operator=(const TestEventRewriteSink&) = delete;
- ~TestEventRewriteSink() override { CheckAllReceived(); }
- void AddExpectedEvent(EventType type) { expected_events_.push_back(type); }
- // Test that all expected events have been received.
- void CheckAllReceived() { EXPECT_TRUE(expected_events_.empty()); }
- // EventSink override:
- EventDispatchDetails OnEventFromSource(Event* event) override {
- EXPECT_FALSE(expected_events_.empty());
- EXPECT_EQ(expected_events_.front(), event->type());
- expected_events_.pop_front();
- return EventDispatchDetails();
- }
- private:
- std::list<EventType> expected_events_;
- };
- std::unique_ptr<Event> CreateEventForType(EventType type) {
- switch (type) {
- case ET_CANCEL_MODE:
- return std::make_unique<CancelModeEvent>();
- case ET_MOUSE_DRAGGED:
- case ET_MOUSE_PRESSED:
- case ET_MOUSE_RELEASED:
- return std::make_unique<MouseEvent>(type, gfx::Point(), gfx::Point(),
- base::TimeTicks::Now(), 0, 0);
- case ET_KEY_PRESSED:
- case ET_KEY_RELEASED:
- return std::make_unique<KeyEvent>(type, ui::VKEY_TAB, DomCode::NONE, 0);
- case ET_SCROLL_FLING_CANCEL:
- case ET_SCROLL_FLING_START:
- return std::make_unique<ScrollEvent>(
- type, gfx::Point(), base::TimeTicks::Now(), 0, 0, 0, 0, 0, 0);
- default:
- NOTREACHED() << type;
- return nullptr;
- }
- }
- class TestEventRewriteSource : public test::TestEventSource {
- public:
- explicit TestEventRewriteSource(EventSink* sink) : TestEventSource(sink) {}
- EventDispatchDetails Send(EventType type) {
- auto event = CreateEventForType(type);
- return TestEventSource::Send(event.get());
- }
- };
- // This EventRewriter always returns the same status, and if rewriting, the
- // same event type; it is used to test simple rewriting, and rewriter addition,
- // removal, and sequencing. Consequently EVENT_REWRITE_DISPATCH_ANOTHER is not
- // supported here (calls to NextDispatchEvent() would continue indefinitely).
- class TestConstantEventRewriterOld : public EventRewriter {
- public:
- TestConstantEventRewriterOld(EventRewriteStatus status, EventType type)
- : status_(status), type_(type) {
- CHECK_NE(EVENT_REWRITE_DISPATCH_ANOTHER, status);
- }
- EventRewriteStatus RewriteEvent(
- const Event& event,
- std::unique_ptr<Event>* rewritten_event) override {
- if (status_ == EVENT_REWRITE_REWRITTEN)
- *rewritten_event = CreateEventForType(type_);
- return status_;
- }
- EventRewriteStatus NextDispatchEvent(
- const Event& last_event,
- std::unique_ptr<Event>* new_event) override {
- NOTREACHED();
- return status_;
- }
- private:
- EventRewriteStatus status_;
- EventType type_;
- };
- // This EventRewriter runs a simple state machine; it is used to test
- // EVENT_REWRITE_DISPATCH_ANOTHER.
- class TestStateMachineEventRewriterOld : public EventRewriter {
- public:
- TestStateMachineEventRewriterOld()
- : last_rewritten_event_(nullptr), state_(0) {}
- void AddRule(int from_state,
- EventType from_type,
- int to_state,
- EventType to_type,
- EventRewriteStatus to_status) {
- RewriteResult r = {to_state, to_type, to_status};
- rules_.emplace(RewriteCase(from_state, from_type), r);
- }
- EventRewriteStatus RewriteEvent(
- const Event& event,
- std::unique_ptr<Event>* rewritten_event) override {
- auto find = rules_.find(RewriteCase(state_, event.type()));
- if (find == rules_.end())
- return EVENT_REWRITE_CONTINUE;
- if ((find->second.status == EVENT_REWRITE_REWRITTEN) ||
- (find->second.status == EVENT_REWRITE_DISPATCH_ANOTHER)) {
- *rewritten_event = CreateEventForType(find->second.type);
- last_rewritten_event_ = rewritten_event->get();
- } else {
- last_rewritten_event_ = nullptr;
- }
- state_ = find->second.state;
- return find->second.status;
- }
- EventRewriteStatus NextDispatchEvent(
- const Event& last_event,
- std::unique_ptr<Event>* new_event) override {
- EXPECT_TRUE(last_rewritten_event_);
- EXPECT_EQ(last_rewritten_event_, &last_event);
- EXPECT_FALSE(new_event->get() && new_event->get() == &last_event);
- return RewriteEvent(last_event, new_event);
- }
- private:
- typedef std::pair<int, EventType> RewriteCase;
- struct RewriteResult {
- int state;
- EventType type;
- EventRewriteStatus status;
- };
- typedef std::map<RewriteCase, RewriteResult> RewriteRules;
- RewriteRules rules_;
- raw_ptr<Event> last_rewritten_event_;
- int state_;
- };
- // This EventRewriter always accepts the original event. It is used to test
- // simple rewriting, and rewriter addition, removal, and sequencing.
- class TestAlwaysAcceptEventRewriter : public EventRewriter {
- public:
- TestAlwaysAcceptEventRewriter() {}
- EventDispatchDetails RewriteEvent(const Event& event,
- const Continuation continuation) override {
- return SendEvent(continuation, &event);
- }
- };
- // This EventRewriter always rewrites with the same event type; it is used
- // to test simple rewriting, and rewriter addition, removal, and sequencing.
- class TestConstantEventRewriter : public EventRewriter {
- public:
- explicit TestConstantEventRewriter(EventType type) : type_(type) {}
- EventDispatchDetails RewriteEvent(const Event& event,
- const Continuation continuation) override {
- std::unique_ptr<Event> replacement_event = CreateEventForType(type_);
- return SendEventFinally(continuation, replacement_event.get());
- }
- private:
- EventType type_;
- };
- // This EventRewriter runs a simple state machine; it is used to test
- // EVENT_REWRITE_DISPATCH_ANOTHER.
- class TestStateMachineEventRewriter : public EventRewriter {
- public:
- enum RewriteAction { ACCEPT, DISCARD, REPLACE };
- enum StateAction { RETURN, PROCEED };
- TestStateMachineEventRewriter() : state_(0) {}
- void AddRule(int from_state,
- EventType from_type,
- int to_state,
- EventType to_type,
- RewriteAction rewrite_action,
- StateAction state_action) {
- RewriteResult r = {to_state, to_type, rewrite_action, state_action};
- rules_.insert({RewriteCase(from_state, from_type), r});
- }
- EventDispatchDetails RewriteEvent(const Event& event,
- const Continuation continuation) override {
- for (;;) {
- RewriteRules::iterator find =
- rules_.find(RewriteCase(state_, event.type()));
- if (find == rules_.end())
- return SendEvent(continuation, &event);
- state_ = find->second.state;
- EventDispatchDetails details;
- switch (find->second.rewrite_action) {
- case ACCEPT:
- details = SendEvent(continuation, &event);
- break;
- case DISCARD:
- break;
- case REPLACE:
- details = SendEventFinally(
- continuation, CreateEventForType(find->second.type).get());
- break;
- }
- if (details.dispatcher_destroyed || find->second.state_action == RETURN)
- return details;
- }
- NOTREACHED();
- }
- private:
- typedef std::pair<int, EventType> RewriteCase;
- struct RewriteResult {
- int state;
- EventType type;
- RewriteAction rewrite_action;
- StateAction state_action;
- };
- typedef std::map<RewriteCase, RewriteResult> RewriteRules;
- RewriteRules rules_;
- int state_;
- };
- } // namespace
- TEST(EventRewriterTest, EventRewritingOld) {
- // TestEventRewriter r0 always rewrites events to ET_CANCEL_MODE;
- // it is placed at the beginning of the chain and later removed,
- // to verify that rewriter removal works.
- TestConstantEventRewriterOld r0(EVENT_REWRITE_REWRITTEN, ET_CANCEL_MODE);
- // TestEventRewriter r1 always returns EVENT_REWRITE_CONTINUE;
- // it is at the beginning of the chain (once r0 is removed)
- // to verify that a later rewriter sees the events.
- TestConstantEventRewriterOld r1(EVENT_REWRITE_CONTINUE, ET_UNKNOWN);
- // TestEventRewriter r2 has a state machine, primarily to test
- // |EVENT_REWRITE_DISPATCH_ANOTHER|.
- TestStateMachineEventRewriterOld r2;
- // TestEventRewriter r3 always rewrites events to ET_CANCEL_MODE;
- // it is placed at the end of the chain to verify that previously
- // rewritten events are not passed further down the chain.
- TestConstantEventRewriterOld r3(EVENT_REWRITE_REWRITTEN, ET_CANCEL_MODE);
- TestEventRewriteSink p;
- TestEventRewriteSource s(&p);
- s.AddEventRewriter(&r0);
- s.AddEventRewriter(&r1);
- s.AddEventRewriter(&r2);
- // These events should be rewritten by r0 to ET_CANCEL_MODE.
- p.AddExpectedEvent(ET_CANCEL_MODE);
- s.Send(ET_MOUSE_DRAGGED);
- p.AddExpectedEvent(ET_CANCEL_MODE);
- s.Send(ET_MOUSE_PRESSED);
- p.CheckAllReceived();
- // Remove r0, and verify that it's gone and that events make it through.
- // - r0 is removed, so the resulting event should NOT be ET_CANCEL_MODE.
- // - r2 should rewrite ET_SCROLL_FLING_START to ET_SCROLL_FLING_CANCEL,
- // and skip subsequent rewriters, so the resulting event should be
- // ET_SCROLL_FLING_CANCEL.
- // - r3 should be skipped after r2 returns, so the resulting event
- // should NOT be ET_CANCEL_MODE.
- s.AddEventRewriter(&r3);
- s.RemoveEventRewriter(&r0);
- // clang-format off
- r2.AddRule(0, ET_SCROLL_FLING_START,
- 0, ET_SCROLL_FLING_CANCEL, EVENT_REWRITE_REWRITTEN);
- // clang-format on
- p.AddExpectedEvent(ET_SCROLL_FLING_CANCEL);
- s.Send(ET_SCROLL_FLING_START);
- p.CheckAllReceived();
- s.RemoveEventRewriter(&r3);
- // Verify EVENT_REWRITE_DISPATCH_ANOTHER using a state machine
- // (that happens to be analogous to sticky keys).
- // clang-format off
- r2.AddRule(0, ET_KEY_PRESSED,
- 1, ET_KEY_PRESSED, EVENT_REWRITE_CONTINUE);
- r2.AddRule(1, ET_MOUSE_PRESSED,
- 0, ET_MOUSE_PRESSED, EVENT_REWRITE_CONTINUE);
- r2.AddRule(1, ET_KEY_RELEASED,
- 2, ET_KEY_RELEASED, EVENT_REWRITE_DISCARD);
- r2.AddRule(2, ET_MOUSE_RELEASED,
- 3, ET_MOUSE_RELEASED, EVENT_REWRITE_DISPATCH_ANOTHER);
- r2.AddRule(3, ET_MOUSE_RELEASED,
- 0, ET_KEY_RELEASED, EVENT_REWRITE_REWRITTEN);
- // clang-format on
- p.AddExpectedEvent(ET_KEY_PRESSED);
- s.Send(ET_KEY_PRESSED);
- s.Send(ET_KEY_RELEASED);
- p.AddExpectedEvent(ET_MOUSE_PRESSED);
- s.Send(ET_MOUSE_PRESSED);
- // Removing rewriter r1 shouldn't affect r2.
- s.RemoveEventRewriter(&r1);
- // Continue with the state-based rewriting.
- p.AddExpectedEvent(ET_MOUSE_RELEASED);
- p.AddExpectedEvent(ET_KEY_RELEASED);
- s.Send(ET_MOUSE_RELEASED);
- p.CheckAllReceived();
- }
- TEST(EventRewriterTest, EventRewriting) {
- // TestEventRewriter r0 always rewrites events to ET_CANCEL_MODE;
- // it is placed at the beginning of the chain and later removed,
- // to verify that rewriter removal works.
- TestConstantEventRewriter r0(ET_CANCEL_MODE);
- // TestEventRewriter r1 always returns EVENT_REWRITE_CONTINUE;
- // it is at the beginning of the chain (once r0 is removed)
- // to verify that a later rewriter sees the events.
- TestAlwaysAcceptEventRewriter r1;
- // TestEventRewriter r2 has a state machine, primarily to test
- // |EVENT_REWRITE_DISPATCH_ANOTHER|.
- TestStateMachineEventRewriter r2;
- // TestEventRewriter r3 always rewrites events to ET_CANCEL_MODE;
- // it is placed at the end of the chain to verify that previously
- // rewritten events are not passed further down the chain.
- TestConstantEventRewriter r3(ET_CANCEL_MODE);
- TestEventRewriteSink p;
- TestEventRewriteSource s(&p);
- s.AddEventRewriter(&r0);
- s.AddEventRewriter(&r1);
- s.AddEventRewriter(&r2);
- // These events should be rewritten by r0 to ET_CANCEL_MODE.
- p.AddExpectedEvent(ET_CANCEL_MODE);
- s.Send(ET_MOUSE_DRAGGED);
- p.AddExpectedEvent(ET_CANCEL_MODE);
- s.Send(ET_MOUSE_PRESSED);
- p.CheckAllReceived();
- // Remove r0, and verify that it's gone and that events make it through.
- // - r0 is removed, so the resulting event should NOT be ET_CANCEL_MODE.
- // - r2 should rewrite ET_SCROLL_FLING_START to ET_SCROLL_FLING_CANCEL,
- // and skip subsequent rewriters, so the resulting event should be
- // ET_SCROLL_FLING_CANCEL.
- // - r3 should be skipped after r2 returns, so the resulting event
- // should NOT be ET_CANCEL_MODE.
- s.AddEventRewriter(&r3);
- s.RemoveEventRewriter(&r0);
- r2.AddRule(0, ET_SCROLL_FLING_START, 0, ET_SCROLL_FLING_CANCEL,
- TestStateMachineEventRewriter::REPLACE,
- TestStateMachineEventRewriter::RETURN);
- p.AddExpectedEvent(ET_SCROLL_FLING_CANCEL);
- s.Send(ET_SCROLL_FLING_START);
- p.CheckAllReceived();
- s.RemoveEventRewriter(&r3);
- // Verify replacing an event with multiple events using a state machine
- // (that happens to be analogous to sticky keys).
- r2.AddRule(0, ET_KEY_PRESSED, 1, ET_UNKNOWN,
- TestStateMachineEventRewriter::ACCEPT,
- TestStateMachineEventRewriter::RETURN);
- r2.AddRule(1, ET_MOUSE_PRESSED, 0, ET_UNKNOWN,
- TestStateMachineEventRewriter::ACCEPT,
- TestStateMachineEventRewriter::RETURN);
- r2.AddRule(1, ET_KEY_RELEASED, 2, ET_UNKNOWN,
- TestStateMachineEventRewriter::DISCARD,
- TestStateMachineEventRewriter::RETURN);
- r2.AddRule(2, ET_MOUSE_RELEASED, 3, ET_MOUSE_RELEASED,
- TestStateMachineEventRewriter::REPLACE,
- TestStateMachineEventRewriter::PROCEED);
- r2.AddRule(3, ET_MOUSE_RELEASED, 0, ET_KEY_RELEASED,
- TestStateMachineEventRewriter::REPLACE,
- TestStateMachineEventRewriter::RETURN);
- p.AddExpectedEvent(ET_KEY_PRESSED);
- s.Send(ET_KEY_PRESSED); // state 0 ET_KEY_PRESSED -> 1 ACCEPT ET_KEY_PRESSED
- s.Send(ET_KEY_RELEASED); // state 1 ET_KEY_RELEASED -> 2 DISCARD
- p.AddExpectedEvent(ET_MOUSE_PRESSED);
- s.Send(ET_MOUSE_PRESSED); // no matching rule; pass event through.
- // Removing rewriter r1 shouldn't affect r2.
- s.RemoveEventRewriter(&r1);
- // Continue with the state-based rewriting.
- p.AddExpectedEvent(ET_MOUSE_RELEASED);
- p.AddExpectedEvent(ET_KEY_RELEASED);
- s.Send(
- ET_MOUSE_RELEASED); // 2 ET_MOUSE_RELEASED -> 3 PROCEED ET_MOUSE_RELEASED
- // 3 ET_MOUSE_RELEASED -> 0 REPLACE ET_KEY_RELEASED
- p.CheckAllReceived();
- }
- } // namespace ui
|