| Index: content/browser/renderer_host/input/buffered_input_router_unittest.cc
 | 
| diff --git a/content/browser/renderer_host/input/buffered_input_router_unittest.cc b/content/browser/renderer_host/input/buffered_input_router_unittest.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..e34cf604a39ec56a64b836d812ae686f4db86713
 | 
| --- /dev/null
 | 
| +++ b/content/browser/renderer_host/input/buffered_input_router_unittest.cc
 | 
| @@ -0,0 +1,335 @@
 | 
| +// Copyright 2013 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 "base/basictypes.h"
 | 
| +#include "content/browser/renderer_host/input/buffered_input_router.h"
 | 
| +#include "content/browser/renderer_host/input/input_router_unittest.h"
 | 
| +#include "content/common/input/event_packet.h"
 | 
| +#include "content/common/input_messages.h"
 | 
| +#include "content/common/view_messages.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +using WebKit::WebGestureEvent;
 | 
| +using WebKit::WebInputEvent;
 | 
| +using WebKit::WebMouseEvent;
 | 
| +using WebKit::WebMouseWheelEvent;
 | 
| +using WebKit::WebTouchEvent;
 | 
| +using WebKit::WebTouchPoint;
 | 
| +
 | 
| +namespace content {
 | 
| +
 | 
| +class TestBufferedInputRouter : public BufferedInputRouter {
 | 
| + public:
 | 
| +  TestBufferedInputRouter(IPC::Sender* sender,
 | 
| +                          InputRouterClient* client,
 | 
| +                          InputAckHandler* ack_handler,
 | 
| +                          int routing_id)
 | 
| +      : BufferedInputRouter(sender, client, ack_handler, routing_id) {}
 | 
| +
 | 
| +
 | 
| +  size_t QueuedEventCount() const { return input_queue()->QueuedEventCount(); }
 | 
| +};
 | 
| +
 | 
| +class BufferedInputRouterTest : public InputRouterTest {
 | 
| + public:
 | 
| +  BufferedInputRouterTest() {}
 | 
| +  virtual ~BufferedInputRouterTest() {}
 | 
| +
 | 
| + protected:
 | 
| +  // InputRouterTest
 | 
| +  virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process,
 | 
| +                                                    InputRouterClient* client,
 | 
| +                                                    InputAckHandler* handler,
 | 
| +                                                    int routing_id) OVERRIDE {
 | 
| +    return scoped_ptr<InputRouter>(
 | 
| +        new TestBufferedInputRouter(process, client, handler, routing_id));
 | 
| +  }
 | 
| +
 | 
| +  bool FinishFlush(const InputEventDispositions& dispositions) {
 | 
| +    if (!process_->sink().message_count())
 | 
| +      return false;
 | 
| +    IPC::Message message(*process_->sink().GetMessageAt(0));
 | 
| +    process_->sink().ClearMessages();
 | 
| +
 | 
| +    InputMsg_HandleEventPacket::Param param;
 | 
| +    InputMsg_HandleEventPacket::Read(&message, ¶m);
 | 
| +    EventPacket& packet = param.a;
 | 
| +
 | 
| +    return SendEventPacketACK(packet.id(), dispositions);
 | 
| +  }
 | 
| +
 | 
| +  bool FinishFlush(InputEventDisposition disposition) {
 | 
| +    if (!process_->sink().message_count())
 | 
| +      return false;
 | 
| +    IPC::Message message(*process_->sink().GetMessageAt(0));
 | 
| +    process_->sink().ClearMessages();
 | 
| +
 | 
| +    InputMsg_HandleEventPacket::Param param;
 | 
| +    InputMsg_HandleEventPacket::Read(&message, ¶m);
 | 
| +    EventPacket& packet = param.a;
 | 
| +
 | 
| +    return SendEventPacketACK(
 | 
| +        packet.id(), InputEventDispositions(packet.size(), disposition));
 | 
| +  }
 | 
| +
 | 
| +  bool SendEventPacketACK(int id, const InputEventDispositions& dispositions) {
 | 
| +    return input_router_->OnMessageReceived(
 | 
| +        InputHostMsg_HandleEventPacket_ACK(0, id, dispositions));
 | 
| +  }
 | 
| +
 | 
| +  size_t QueuedEventCount() const {
 | 
| +    return buffered_input_router()->QueuedEventCount();
 | 
| +  }
 | 
| +
 | 
| +  TestBufferedInputRouter* buffered_input_router() const {
 | 
| +    return static_cast<TestBufferedInputRouter*>(input_router_.get());
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, InputEventsProperlyQueued) {
 | 
| +  EXPECT_TRUE(input_router_->SendInput(
 | 
| +      scoped_ptr<IPC::Message>(new InputMsg_Redo(MSG_ROUTING_NONE))));
 | 
| +  EXPECT_EQ(1U, QueuedEventCount());
 | 
| +
 | 
| +  EXPECT_TRUE(input_router_->SendInput(
 | 
| +      scoped_ptr<IPC::Message>(new InputMsg_Cut(MSG_ROUTING_NONE))));
 | 
| +  EXPECT_EQ(2U, QueuedEventCount());
 | 
| +
 | 
| +  EXPECT_TRUE(input_router_->SendInput(
 | 
| +      scoped_ptr<IPC::Message>(new InputMsg_Copy(MSG_ROUTING_NONE))));
 | 
| +  EXPECT_EQ(3U, QueuedEventCount());
 | 
| +
 | 
| +  EXPECT_TRUE(input_router_->SendInput(
 | 
| +      scoped_ptr<IPC::Message>(new InputMsg_Paste(MSG_ROUTING_NONE))));
 | 
| +  EXPECT_EQ(4U, QueuedEventCount());
 | 
| +}
 | 
| +
 | 
| +#define SCOPED_EXPECT(CALL, MESSAGE) { SCOPED_TRACE(MESSAGE); CALL; }
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, ClientOnSendEventCalled) {
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  EXPECT_EQ(1U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateWheelEvent(5, 0, 0, false);
 | 
| +  EXPECT_EQ(2U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateMouseMove(5, 0, 0);
 | 
| +  EXPECT_EQ(3U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
 | 
| +                       WebGestureEvent::Touchpad);
 | 
| +  EXPECT_EQ(4U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateTouchEvent(1, 1);
 | 
| +  EXPECT_EQ(5U, QueuedEventCount());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, ClientOnSendEventHonored) {
 | 
| +  client_->set_allow_send_event(false);
 | 
| +
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateWheelEvent(5, 0, 0, false);
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateMouseMove(5, 0, 0);
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
 | 
| +                       WebGestureEvent::Touchpad);
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +
 | 
| +  SimulateTouchEvent(1, 1);
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, FlightCountIncrementedOnDeliver) {
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  EXPECT_EQ(0, client_->in_flight_event_count());
 | 
| +
 | 
| +  input_router_->Flush();
 | 
| +  EXPECT_EQ(1, client_->in_flight_event_count());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, FlightCountDecrementedOnAck) {
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  EXPECT_EQ(0, client_->in_flight_event_count());
 | 
| +
 | 
| +  input_router_->Flush();
 | 
| +  EXPECT_EQ(1, client_->in_flight_event_count());
 | 
| +
 | 
| +  // The in-flight count should continue until the flush has finished.
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER));
 | 
| +  EXPECT_EQ(1, client_->in_flight_event_count());
 | 
| +
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
 | 
| +  EXPECT_EQ(0, client_->in_flight_event_count());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, FilteredEventsNeverQueued) {
 | 
| +  // Event should not be queued, but should be ack'ed.
 | 
| +  client_->set_filter_state(INPUT_EVENT_ACK_STATE_CONSUMED);
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled");
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +  ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent());
 | 
| +
 | 
| +  // Event should not be queued, but should be ack'ed.
 | 
| +  client_->set_filter_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled");
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +  ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent());
 | 
| +
 | 
| +  // |INPUT_EVENT_DISPOSITION_UNKNOWN| should drop the event without ack'ing.
 | 
| +  client_->set_filter_state(INPUT_EVENT_ACK_STATE_UNKNOWN);
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled");
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +  ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent());
 | 
| +
 | 
| +  // Event should be queued.
 | 
| +  client_->set_filter_state(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled");
 | 
| +  EXPECT_EQ(1U, QueuedEventCount());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, FollowupEventsInjected) {
 | 
| +  // Enable a followup gesture event.
 | 
| +  WebGestureEvent followup_event;
 | 
| +  followup_event.type = WebInputEvent::GestureScrollBegin;
 | 
| +  followup_event.data.scrollUpdate.deltaX = 10;
 | 
| +  ack_handler_->set_followup_touch_event(make_scoped_ptr(
 | 
| +      new GestureEventWithLatencyInfo(followup_event, ui::LatencyInfo())));
 | 
| +
 | 
| +  // Create an initial packet of { Touch, Key } and start flushing.
 | 
| +  SimulateTouchEvent(1, 1);
 | 
| +  EXPECT_EQ(1U, QueuedEventCount());
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  EXPECT_EQ(2U, QueuedEventCount());
 | 
| +  input_router_->Flush();
 | 
| +
 | 
| +  // Followup only triggered when event handled.
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER));
 | 
| +  SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled");
 | 
| +  EXPECT_EQ(2U, QueuedEventCount());
 | 
| +
 | 
| +  // Ack the touch event.
 | 
| +  InputEventDispositions dispositions;
 | 
| +  dispositions.push_back(INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED);
 | 
| +  dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER);
 | 
| +  ASSERT_TRUE(FinishFlush(dispositions));
 | 
| +
 | 
| +  // Ack'ing the touch event should have inserted the followup gesture event;
 | 
| +  // the flush is not complete until the inserted event is ack'ed.
 | 
| +  SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled");
 | 
| +  SCOPED_EXPECT(client_->ExpectSendCalled(true), "SendGestureCalled");
 | 
| +  EXPECT_EQ(followup_event.type, client_->sent_gesture_event().event.type);
 | 
| +  EXPECT_EQ(2U, QueuedEventCount());
 | 
| +
 | 
| +  // Our packet is now { Gesture, Key }.
 | 
| +  InputMsg_HandleEventPacket::Param param;
 | 
| +  ASSERT_EQ(1U, process_->sink().message_count());
 | 
| +  ASSERT_TRUE(InputMsg_HandleEventPacket::Read(process_->sink().GetMessageAt(0),
 | 
| +                                               ¶m));
 | 
| +  EventPacket& followup_packet = param.a;
 | 
| +  ASSERT_EQ(2U, followup_packet.size());
 | 
| +  ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT,
 | 
| +            followup_packet.events()[0]->payload()->GetType());
 | 
| +  ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT,
 | 
| +            followup_packet.events()[1]->payload()->GetType());
 | 
| +  const WebInputEventPayload* payload0 =
 | 
| +      WebInputEventPayload::Cast(followup_packet.events()[0]->payload());
 | 
| +  const WebInputEventPayload* payload1 =
 | 
| +      WebInputEventPayload::Cast(followup_packet.events()[1]->payload());
 | 
| +  EXPECT_EQ(followup_event.type, payload0->web_event()->type);
 | 
| +  EXPECT_EQ(WebInputEvent::RawKeyDown, payload1->web_event()->type);
 | 
| +
 | 
| +  // Complete the flush; the gesture should have been ack'ed.
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
 | 
| +  SCOPED_EXPECT(client_->ExpectDidFlushCalled(true), "DidFlushCalled");
 | 
| +  EXPECT_EQ(followup_event.type, ack_handler_->acked_gesture_event().type);
 | 
| +  EXPECT_EQ(0U, QueuedEventCount());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, FlushRequestedOnQueue) {
 | 
| +  // The first queued event should trigger a flush request.
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  EXPECT_EQ(1U, QueuedEventCount());
 | 
| +  SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(true), "SetNeedsFlushCalled");
 | 
| +
 | 
| +  // Subsequently queued events will not trigger another flush request.
 | 
| +  SimulateWheelEvent(5, 0, 0, false);
 | 
| +  EXPECT_EQ(2U, QueuedEventCount());
 | 
| +  SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(false), "SetNeedsFlushCalled");
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, HasQueuedGestureEvents) {
 | 
| +  EXPECT_FALSE(input_router_->HasQueuedGestureEvents());
 | 
| +  SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
 | 
| +                       WebGestureEvent::Touchpad);
 | 
| +  EXPECT_TRUE(input_router_->HasQueuedGestureEvents());
 | 
| +
 | 
| +  // Only an ack'ed gesture should clear it from the queue.
 | 
| +  input_router_->Flush();
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER));
 | 
| +  EXPECT_TRUE(input_router_->HasQueuedGestureEvents());
 | 
| +
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
 | 
| +  EXPECT_FALSE(input_router_->HasQueuedGestureEvents());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, GetLastKeyboardEvent) {
 | 
| +  EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent());
 | 
| +
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  EXPECT_EQ(WebInputEvent::RawKeyDown,
 | 
| +            input_router_->GetLastKeyboardEvent()->type);
 | 
| +
 | 
| +  // Queueing another key event does not effect the "last" event.
 | 
| +  SimulateKeyboardEvent(WebInputEvent::KeyUp);
 | 
| +  EXPECT_EQ(WebInputEvent::RawKeyDown,
 | 
| +            input_router_->GetLastKeyboardEvent()->type);
 | 
| +
 | 
| +  input_router_->Flush();
 | 
| +
 | 
| +  // Ack'ing the first event should make the second event the "last" event.
 | 
| +  InputEventDispositions dispositions;
 | 
| +  dispositions.push_back(INPUT_EVENT_IMPL_THREAD_CONSUMED);
 | 
| +  dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER);
 | 
| +  ASSERT_TRUE(FinishFlush(dispositions));
 | 
| +  EXPECT_EQ(WebInputEvent::KeyUp, input_router_->GetLastKeyboardEvent()->type);
 | 
| +
 | 
| +  // A key event queued during a flush becomes "last" upon flush completion.
 | 
| +  SimulateKeyboardEvent(WebInputEvent::Char);
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
 | 
| +  EXPECT_EQ(WebInputEvent::Char, input_router_->GetLastKeyboardEvent()->type);
 | 
| +
 | 
| +  // An empty queue should produce a null "last" event.
 | 
| +  input_router_->Flush();
 | 
| +  ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
 | 
| +  EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, UnexpectedAck) {
 | 
| +  ASSERT_FALSE(ack_handler_->unexpected_event_ack_called());
 | 
| +  input_router_->OnMessageReceived(
 | 
| +      InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions()));
 | 
| +  EXPECT_TRUE(ack_handler_->unexpected_event_ack_called());
 | 
| +}
 | 
| +
 | 
| +TEST_F(BufferedInputRouterTest, BadAck) {
 | 
| +  SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 | 
| +  input_router_->Flush();
 | 
| +
 | 
| +  ASSERT_FALSE(ack_handler_->unexpected_event_ack_called());
 | 
| +  EventPacket packet;
 | 
| +  input_router_->OnMessageReceived(
 | 
| +      InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions()));
 | 
| +  EXPECT_TRUE(ack_handler_->unexpected_event_ack_called());
 | 
| +}
 | 
| +
 | 
| +}  // namespace content
 | 
| 
 |