Index: content/renderer/gpu/input_event_filter_unittest.cc |
diff --git a/content/renderer/gpu/input_event_filter_unittest.cc b/content/renderer/gpu/input_event_filter_unittest.cc |
index d8e605268b04d1803597488d5b4ca7945b9a5082..7c74adfb1881069c95ccc3072947698cbc566e29 100644 |
--- a/content/renderer/gpu/input_event_filter_unittest.cc |
+++ b/content/renderer/gpu/input_event_filter_unittest.cc |
@@ -8,6 +8,8 @@ |
#include "base/bind.h" |
#include "base/message_loop/message_loop.h" |
+#include "content/common/input/event_packet.h" |
+#include "content/common/input/input_event.h" |
#include "content/common/input_messages.h" |
#include "content/common/view_messages.h" |
#include "content/renderer/gpu/input_event_filter.h" |
@@ -16,6 +18,7 @@ |
using WebKit::WebInputEvent; |
using WebKit::WebMouseEvent; |
+using WebKit::WebTouchEvent; |
namespace content { |
namespace { |
@@ -107,11 +110,20 @@ void InitMouseEvent(WebMouseEvent* event, WebInputEvent::Type type, |
event->y = y; |
} |
+void InitTouchEvent(WebTouchEvent* event, WebInputEvent::Type type, |
+ int touchesLength) { |
+ // Avoid valgrind false positives by initializing memory completely. |
+ memset(event, 0, sizeof(*event)); |
+ |
+ new (event) WebTouchEvent(); |
+ event->type = type; |
+ event->touchesLength = touchesLength; |
+} |
+ |
void AddMessagesToFilter(IPC::ChannelProxy::MessageFilter* message_filter, |
const std::vector<IPC::Message>& events) { |
- for (size_t i = 0; i < events.size(); ++i) { |
+ for (size_t i = 0; i < events.size(); ++i) |
message_filter->OnMessageReceived(events[i]); |
- } |
base::MessageLoop::current()->RunUntilIdle(); |
} |
@@ -129,6 +141,35 @@ void AddEventsToFilter(IPC::ChannelProxy::MessageFilter* message_filter, |
AddMessagesToFilter(message_filter, messages); |
} |
+void AddPacketToFilter(IPC::ChannelProxy::MessageFilter* message_filter, |
+ const EventPacket& packet) { |
+ message_filter->OnMessageReceived( |
+ InputMsg_HandleEventPacket(kTestRoutingID, |
+ packet, |
+ InputEventDispositions())); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+} |
+ |
+void AddMessagesToPacket(EventPacket* packet, |
+ const std::vector<IPC::Message>& messages) { |
+ for (size_t i = 0; i < messages.size(); ++i) { |
+ const IPC::Message& message = messages[i]; |
+ scoped_ptr<InputEvent> event_from_message; |
+ if (message.type() == InputMsg_HandleInputEvent::ID) { |
+ InputMsg_HandleInputEvent::Param param; |
+ ASSERT_TRUE(InputMsg_HandleInputEvent::Read(&message, ¶m)); |
+ event_from_message = InputEvent::Create(i+1, |
+ WebInputEventPayload::Create(*param.a, param.b, param.c)); |
+ } else { |
+ event_from_message = InputEvent::Create(i+1, |
+ IPCInputEventPayload::Create( |
+ make_scoped_ptr(new IPC::Message(message)))); |
+ } |
+ ASSERT_TRUE(!!event_from_message); |
+ packet->Add(event_from_message.Pass()); |
+ } |
+} |
+ |
} // namespace |
class InputEventFilterTest : public testing::Test { |
@@ -148,6 +189,65 @@ class InputEventFilterTest : public testing::Test { |
protected: |
+ |
+ void CheckEventPacket(const IPC::Message* packet_message, |
+ const EventPacket& source_packet, |
+ const InputEventDispositions& expected_dispositions) { |
+ EXPECT_EQ(kTestRoutingID, packet_message->routing_id()); |
+ |
+ InputMsg_HandleEventPacket::Param param; |
+ ASSERT_TRUE(InputMsg_HandleEventPacket::Read(packet_message, ¶m)); |
+ const EventPacket& packet = param.a; |
+ const InputEventDispositions& dispositions = param.b; |
+ |
+ EXPECT_EQ(source_packet.id(), packet.id()); |
+ ASSERT_EQ(packet.size(), dispositions.size()); |
+ ASSERT_EQ(source_packet.size(), packet.size()); |
+ |
+ for (size_t i = 0; i < packet.size(); ++i) { |
+ const InputEvent& event = *packet.events()[i]; |
+ const InputEventDisposition disposition = dispositions[i]; |
+ EXPECT_EQ(source_packet.events()[i]->id(), event.id()); |
+ EXPECT_EQ(source_packet.events()[i]->payload()->GetType(), |
+ event.payload()->GetType()); |
+ EXPECT_EQ(expected_dispositions[i], disposition) << i; |
+ } |
+ } |
+ |
+ void CheckEventPacketAck( |
+ const IPC::Message* packet_message, |
+ const EventPacket& source_packet, |
+ const InputEventDispositions& expected_dispositions) { |
+ EXPECT_EQ(kTestRoutingID, packet_message->routing_id()); |
+ |
+ InputHostMsg_HandleEventPacket_ACK::Param param; |
+ ASSERT_TRUE( |
+ InputHostMsg_HandleEventPacket_ACK::Read(packet_message, ¶m)); |
+ const int64 packet_ack_id = param.a; |
+ const InputEventDispositions& dispositions = param.b; |
+ |
+ EXPECT_EQ(source_packet.id(), packet_ack_id); |
+ EXPECT_EQ(expected_dispositions, dispositions); |
+ } |
+ |
+ void CheckEventPacket(const IPC::Message* packet_message, |
+ const EventPacket& source_packet, |
+ InputEventDisposition expected_disposition) { |
+ CheckEventPacket( |
+ packet_message, |
+ source_packet, |
+ InputEventDispositions(source_packet.size(), expected_disposition)); |
+ } |
+ |
+ void CheckEventPacketAck(const IPC::Message* packet_message, |
+ const EventPacket& source_packet, |
+ InputEventDisposition expected_disposition) { |
+ CheckEventPacketAck( |
+ packet_message, |
+ source_packet, |
+ InputEventDispositions(source_packet.size(), expected_disposition)); |
+ } |
+ |
base::MessageLoop message_loop_; |
// Used to record IPCs sent by the filter to the RenderWidgetHost. |
@@ -188,10 +288,8 @@ TEST_F(InputEventFilterTest, Basic) { |
WebInputEvent::Type event_type = WebInputEvent::Undefined; |
InputEventAckState ack_result = INPUT_EVENT_ACK_STATE_NOT_CONSUMED; |
ui::LatencyInfo latency_info; |
- EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read(message, |
- &event_type, |
- &ack_result, |
- &latency_info)); |
+ EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read( |
+ message, &event_type, &ack_result, &latency_info)); |
EXPECT_EQ(kEvents[i].type, event_type); |
EXPECT_EQ(ack_result, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
@@ -267,9 +365,9 @@ TEST_F(InputEventFilterTest, PreserveRelativeOrder) { |
std::vector<IPC::Message> messages; |
messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID, |
- &mouse_down, |
- ui::LatencyInfo(), |
- false)); |
+ &mouse_down, |
+ ui::LatencyInfo(), |
+ false)); |
// Control where input events are delivered. |
messages.push_back(InputMsg_MouseCaptureLost(kTestRoutingID)); |
messages.push_back(InputMsg_SetFocus(kTestRoutingID, true)); |
@@ -296,9 +394,9 @@ TEST_F(InputEventFilterTest, PreserveRelativeOrder) { |
messages.push_back(InputMsg_MoveCaret(kTestRoutingID, gfx::Point())); |
messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID, |
- &mouse_up, |
- ui::LatencyInfo(), |
- false)); |
+ &mouse_up, |
+ ui::LatencyInfo(), |
+ false)); |
AddMessagesToFilter(filter_.get(), messages); |
// We should have sent all messages back to the main thread and preserved |
@@ -307,6 +405,188 @@ TEST_F(InputEventFilterTest, PreserveRelativeOrder) { |
for (size_t i = 0; i < messages.size(); ++i) { |
EXPECT_EQ(message_recorder_.message_at(i).type(), messages[i].type()) << i; |
} |
+ |
+ message_recorder_.Clear(); |
+ |
+ // Now test ordering for packets. |
+ EventPacket packet; |
+ packet.set_id(1); |
+ AddMessagesToPacket(&packet, messages); |
+ AddPacketToFilter(filter_.get(), packet); |
+ |
+ // We should have sent the packet sent back to the main thread, with message |
+ // order preserved. |
+ ASSERT_EQ(1U, message_recorder_.message_count()); |
+ |
+ // The first bounced WebInputEvent message will send the entire packet to |
+ // the main thread; all subsequents events that are only handled on the main |
+ // thread will be marked accordingly. |
+ InputEventDispositions expected_dispositions( |
+ messages.size(), INPUT_EVENT_IMPL_THREAD_BOUNCE_TO_MAIN); |
+ // The final WebInputEvent is marked undeliverable; WebInputEvents should |
+ // always be offered to the impl thread first, in order. |
+ expected_dispositions.back() = INPUT_EVENT_COULD_NOT_DELIVER; |
+ |
+ { |
+ SCOPED_TRACE("EventPacketBounceToMain"); |
+ CheckEventPacket(&message_recorder_.message_at(0), |
+ packet, |
+ expected_dispositions); |
+ } |
+ |
+ message_recorder_.Clear(); |
+ |
+ // Remove the first WebInputEvent; all main-thread bound messages will be |
+ // sent to main singly until a WebInputEvent is reached, which will finish |
+ // the packet processing. |
+ messages.erase(messages.begin()); |
+ event_recorder_.set_handle_events(true); |
+ |
+ EventPacket next_packet; |
+ packet.set_id(2); |
+ AddMessagesToPacket(&next_packet, messages); |
+ AddPacketToFilter(filter_.get(), next_packet); |
+ ASSERT_EQ(messages.size() - 1, message_recorder_.message_count()); |
+ ASSERT_EQ(1U, ipc_sink_.message_count()); |
+ |
+ expected_dispositions = |
+ InputEventDispositions(messages.size(), INPUT_EVENT_MAIN_THREAD_CONSUMED); |
+ expected_dispositions.back() = INPUT_EVENT_IMPL_THREAD_CONSUMED; |
+ |
+ { |
+ SCOPED_TRACE("EventPacketImplConsumed"); |
+ CheckEventPacketAck(ipc_sink_.GetMessageAt(0), |
+ next_packet, |
+ expected_dispositions); |
+ } |
+} |
+ |
+TEST_F(InputEventFilterTest, EventPacketBasic) { |
+ WebMouseEvent kEvents[3]; |
+ InitMouseEvent(&kEvents[0], WebInputEvent::MouseDown, 10, 10); |
+ InitMouseEvent(&kEvents[1], WebInputEvent::MouseMove, 20, 20); |
+ InitMouseEvent(&kEvents[2], WebInputEvent::MouseUp, 30, 30); |
+ |
+ EventPacket packet; |
+ packet.set_id(13); |
+ for (size_t i = 0; i < arraysize(kEvents); ++i) { |
+ ASSERT_TRUE( |
+ packet.Add(InputEvent::Create(i+1, WebInputEventPayload::Create( |
+ kEvents[i], ui::LatencyInfo(), false)))); |
+ } |
+ |
+ AddPacketToFilter(filter_.get(), packet); |
+ EXPECT_EQ(0U, ipc_sink_.message_count()); |
+ EXPECT_EQ(0U, event_recorder_.record_count()); |
+ EXPECT_EQ(0U, message_recorder_.message_count()); |
+ |
+ filter_->DidAddInputHandler(kTestRoutingID, NULL); |
+ |
+ AddPacketToFilter(filter_.get(), packet); |
+ // The packet is not forwarded to the the listener. |
+ ASSERT_EQ(1U, ipc_sink_.message_count()); |
+ ASSERT_EQ(arraysize(kEvents), event_recorder_.record_count()); |
+ EXPECT_EQ(0U, message_recorder_.message_count()); |
+ |
+ { |
+ SCOPED_TRACE("EventPacketImplNoConsumer"); |
+ CheckEventPacketAck(ipc_sink_.GetMessageAt(0), |
+ packet, |
+ INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS); |
+ } |
+ |
+ event_recorder_.set_send_to_widget(true); |
+ |
+ AddPacketToFilter(filter_.get(), packet); |
+ // The entire packet is forwarded to the the listener; only the first event is |
+ // offered to the event handler. |
+ EXPECT_EQ(1U, ipc_sink_.message_count()); |
+ EXPECT_EQ(arraysize(kEvents) + 1, event_recorder_.record_count()); |
+ EXPECT_EQ(1U, message_recorder_.message_count()); |
+ |
+ InputEventDispositions expected_dispositions; |
+ expected_dispositions.push_back(INPUT_EVENT_IMPL_THREAD_BOUNCE_TO_MAIN); |
+ expected_dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); |
+ expected_dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); |
+ { |
+ SCOPED_TRACE("EventPacketBounceToMain"); |
+ CheckEventPacket(&message_recorder_.message_at(0), |
+ packet, |
+ expected_dispositions); |
+ } |
+ |
+ ipc_sink_.ClearMessages(); |
+ event_recorder_.Clear(); |
+ message_recorder_.Clear(); |
+ |
+ event_recorder_.set_handle_events(true); |
+ |
+ AddPacketToFilter(filter_.get(), packet); |
+ // The packet is not forwarded to the listener; all events are offered to the |
+ // event handler. |
+ EXPECT_EQ(1U, ipc_sink_.message_count()); |
+ EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count()); |
+ EXPECT_EQ(0U, message_recorder_.message_count()); |
+ |
+ { |
+ SCOPED_TRACE("EventPacketImplConsumed"); |
+ CheckEventPacketAck(ipc_sink_.GetMessageAt(0), |
+ packet, |
+ INPUT_EVENT_IMPL_THREAD_CONSUMED); |
+ } |
+ |
+ filter_->OnFilterRemoved(); |
+} |
+ |
+TEST_F(InputEventFilterTest, EventPacketWithFollowup) { |
+ WebTouchEvent kTouchEvent; |
+ WebMouseEvent kMouseEvents[2]; |
+ InitTouchEvent(&kTouchEvent, WebInputEvent::TouchStart, 1); |
+ InitMouseEvent(&kMouseEvents[0], WebInputEvent::MouseDown, 10, 10); |
+ InitMouseEvent(&kMouseEvents[1], WebInputEvent::MouseMove, 20, 20); |
+ const WebInputEvent* kEvents[3] = { |
+ &kMouseEvents[0], |
+ &kTouchEvent, |
+ &kMouseEvents[1] |
+ }; |
+ |
+ EventPacket packet; |
+ packet.set_id(7); |
+ for (size_t i = 0; i < arraysize(kEvents); ++i) { |
+ ASSERT_TRUE(packet.Add(InputEvent::Create(i+1, |
+ WebInputEventPayload::Create(*kEvents[i], ui::LatencyInfo(), false)))); |
+ } |
+ ASSERT_TRUE(packet.Add( |
+ InputEvent::Create(arraysize(kEvents)+1, |
+ IPCInputEventPayload::Create( |
+ scoped_ptr<IPC::Message>(new InputMsg_Undo(kTestRoutingID)))))); |
+ |
+ event_recorder_.set_handle_events(false); |
+ event_recorder_.set_send_to_widget(false); |
+ |
+ filter_->DidAddInputHandler(kTestRoutingID, NULL); |
+ |
+ AddPacketToFilter(filter_.get(), packet); |
+ |
+ // The unhandled, injecting touch event prevents subsequent event handling. |
+ EXPECT_EQ(1U, ipc_sink_.message_count()); |
+ EXPECT_EQ(arraysize(kEvents) - 1, event_recorder_.record_count()); |
+ EXPECT_EQ(0U, message_recorder_.message_count()); |
+ |
+ InputEventDispositions expected_dispositions; |
+ expected_dispositions.push_back(INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS); |
+ expected_dispositions.push_back(INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS); |
+ expected_dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); |
+ expected_dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER); |
+ |
+ { |
+ SCOPED_TRACE("EventPacketImplWithFollowup"); |
+ CheckEventPacketAck(ipc_sink_.GetMessageAt(0), |
+ packet, |
+ expected_dispositions); |
+ } |
+ |
+ filter_->OnFilterRemoved(); |
} |
} // namespace content |