1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "content/browser/renderer_host/input/input_ack_handler.h"
8 #include "content/browser/renderer_host/input/input_router_client.h"
9 #include "content/browser/renderer_host/input/input_router_impl.h"
10 #include "content/common/input/web_input_event_traits.h"
11 #include "content/common/input_messages.h"
12 #include "content/common/view_messages.h"
13 #include "ipc/ipc_sender.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "testing/perf/perf_test.h"
16 #include "ui/gfx/geometry/vector2d_f.h"
18 using base::TimeDelta
;
19 using blink::WebGestureEvent
;
20 using blink::WebInputEvent
;
21 using blink::WebTouchEvent
;
22 using blink::WebTouchPoint
;
28 class NullInputAckHandler
: public InputAckHandler
{
30 NullInputAckHandler() : ack_count_(0) {}
31 virtual ~NullInputAckHandler() {}
34 virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent
& event
,
35 InputEventAckState ack_result
) OVERRIDE
{
38 virtual void OnWheelEventAck(const MouseWheelEventWithLatencyInfo
& event
,
39 InputEventAckState ack_result
) OVERRIDE
{
42 virtual void OnTouchEventAck(const TouchEventWithLatencyInfo
& event
,
43 InputEventAckState ack_result
) OVERRIDE
{
46 virtual void OnGestureEventAck(const GestureEventWithLatencyInfo
& event
,
47 InputEventAckState ack_result
) OVERRIDE
{
50 virtual void OnUnexpectedEventAck(UnexpectedEventAckType type
) OVERRIDE
{
54 size_t GetAndResetAckCount() {
55 size_t ack_count
= ack_count_
;
60 size_t ack_count() const { return ack_count_
; }
66 class NullInputRouterClient
: public InputRouterClient
{
68 NullInputRouterClient() {}
69 virtual ~NullInputRouterClient() {}
72 virtual InputEventAckState
FilterInputEvent(
73 const blink::WebInputEvent
& input_event
,
74 const ui::LatencyInfo
& latency_info
) OVERRIDE
{
75 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED
;
77 virtual void IncrementInFlightEventCount() OVERRIDE
{}
78 virtual void DecrementInFlightEventCount() OVERRIDE
{}
79 virtual void OnHasTouchEventHandlers(bool has_handlers
) OVERRIDE
{}
80 virtual OverscrollController
* GetOverscrollController() const OVERRIDE
{
83 virtual void DidFlush() OVERRIDE
{}
84 virtual void SetNeedsFlush() OVERRIDE
{}
87 class NullIPCSender
: public IPC::Sender
{
89 NullIPCSender() : sent_count_(0) {}
90 virtual ~NullIPCSender() {}
92 virtual bool Send(IPC::Message
* message
) OVERRIDE
{
98 size_t GetAndResetSentEventCount() {
99 size_t message_count
= sent_count_
;
101 return message_count
;
104 bool HasMessages() const { return sent_count_
> 0; }
110 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
111 typedef std::vector
<WebGestureEvent
> Gestures
;
112 Gestures
BuildScrollSequence(size_t steps
,
113 gfx::Vector2dF origin
,
114 gfx::Vector2dF distance
) {
116 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
118 WebGestureEvent gesture
;
119 gesture
.type
= WebInputEvent::GestureScrollBegin
;
120 gesture
.x
= origin
.x();
121 gesture
.y
= origin
.y();
122 gestures
.push_back(gesture
);
124 gesture
.type
= WebInputEvent::GestureScrollUpdate
;
125 gesture
.data
.scrollUpdate
.deltaX
= delta
.x();
126 gesture
.data
.scrollUpdate
.deltaY
= delta
.y();
127 for (size_t i
= 0; i
< steps
; ++i
) {
128 gesture
.x
+= delta
.x();
129 gesture
.y
+= delta
.y();
130 gestures
.push_back(gesture
);
133 gesture
.type
= WebInputEvent::GestureScrollEnd
;
134 gestures
.push_back(gesture
);
138 typedef std::vector
<WebTouchEvent
> Touches
;
139 Touches
BuildTouchSequence(size_t steps
,
140 gfx::Vector2dF origin
,
141 gfx::Vector2dF distance
) {
143 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
146 touch
.touchesLength
= 1;
147 touch
.type
= WebInputEvent::TouchStart
;
148 touch
.touches
[0].id
= 0;
149 touch
.touches
[0].state
= WebTouchPoint::StatePressed
;
150 touch
.touches
[0].position
.x
= origin
.x();
151 touch
.touches
[0].position
.y
= origin
.y();
152 touch
.touches
[0].screenPosition
.x
= origin
.x();
153 touch
.touches
[0].screenPosition
.y
= origin
.y();
154 touches
.push_back(touch
);
156 touch
.type
= WebInputEvent::TouchMove
;
157 touch
.touches
[0].state
= WebTouchPoint::StateMoved
;
158 for (size_t i
= 0; i
< steps
; ++i
) {
159 touch
.touches
[0].position
.x
+= delta
.x();
160 touch
.touches
[0].position
.y
+= delta
.y();
161 touch
.touches
[0].screenPosition
.x
+= delta
.x();
162 touch
.touches
[0].screenPosition
.y
+= delta
.y();
163 touches
.push_back(touch
);
166 touch
.type
= WebInputEvent::TouchEnd
;
167 touch
.touches
[0].state
= WebTouchPoint::StateReleased
;
168 touches
.push_back(touch
);
172 class InputEventTimer
{
174 InputEventTimer(const char* test_name
, int64 event_count
)
175 : test_name_(test_name
),
176 event_count_(event_count
),
177 start_(base::TimeTicks::Now()) {}
180 perf_test::PrintResult(
181 "avg_time_per_event",
184 static_cast<size_t>(((base::TimeTicks::Now() - start_
) / event_count_
)
191 const char* test_name_
;
193 base::TimeTicks start_
;
194 DISALLOW_COPY_AND_ASSIGN(InputEventTimer
);
199 class InputRouterImplPerfTest
: public testing::Test
{
201 InputRouterImplPerfTest() : last_input_id_(0) {}
202 virtual ~InputRouterImplPerfTest() {}
206 virtual void SetUp() OVERRIDE
{
207 sender_
.reset(new NullIPCSender());
208 client_
.reset(new NullInputRouterClient());
209 ack_handler_
.reset(new NullInputAckHandler());
210 input_router_
.reset(new InputRouterImpl(sender_
.get(),
214 InputRouterImpl::Config()));
217 virtual void TearDown() OVERRIDE
{
218 base::MessageLoop::current()->RunUntilIdle();
220 input_router_
.reset();
221 ack_handler_
.reset();
226 void SendEvent(const WebGestureEvent
& gesture
,
227 const ui::LatencyInfo
& latency
) {
228 input_router_
->SendGestureEvent(
229 GestureEventWithLatencyInfo(gesture
, latency
));
232 void SendEvent(const WebTouchEvent
& touch
, const ui::LatencyInfo
& latency
) {
233 input_router_
->SendTouchEvent(TouchEventWithLatencyInfo(touch
, latency
));
236 void SendEventAckIfNecessary(const blink::WebInputEvent
& event
,
237 InputEventAckState ack_result
) {
238 if (WebInputEventTraits::IgnoresAckDisposition(event
))
240 InputHostMsg_HandleInputEvent_ACK
response(
241 0, event
.type
, ack_result
, ui::LatencyInfo());
242 input_router_
->OnMessageReceived(response
);
245 void OnHasTouchEventHandlers(bool has_handlers
) {
246 input_router_
->OnMessageReceived(
247 ViewHostMsg_HasTouchEventHandlers(0, has_handlers
));
250 size_t GetAndResetSentEventCount() {
251 return sender_
->GetAndResetSentEventCount();
254 size_t GetAndResetAckCount() { return ack_handler_
->GetAndResetAckCount(); }
256 size_t AckCount() const { return ack_handler_
->ack_count(); }
258 int64
NextLatencyID() { return ++last_input_id_
; }
260 ui::LatencyInfo
CreateLatencyInfo() {
261 ui::LatencyInfo latency
;
262 latency
.AddLatencyNumber(
263 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT
, 1, 0);
264 latency
.AddLatencyNumber(
265 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT
,
271 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
272 template <typename EventType
>
273 void SimulateEventSequence(const char* test_name
,
274 const std::vector
<EventType
>& events
,
277 OnHasTouchEventHandlers(true);
279 const size_t event_count
= events
.size();
280 const size_t total_event_count
= event_count
* iterations
;
282 InputEventTimer
timer(test_name
, total_event_count
);
283 while (iterations
--) {
284 size_t i
= 0, ack_i
= 0;
286 SendEvent(events
[i
++], CreateLatencyInfo());
288 for (; i
< event_count
; ++i
, ++ack_i
) {
289 SendEvent(events
[i
], CreateLatencyInfo());
290 SendEventAckIfNecessary(events
[ack_i
], INPUT_EVENT_ACK_STATE_CONSUMED
);
294 SendEventAckIfNecessary(events
.back(), INPUT_EVENT_ACK_STATE_CONSUMED
);
296 EXPECT_EQ(event_count
, GetAndResetSentEventCount());
297 EXPECT_EQ(event_count
, GetAndResetAckCount());
301 void SimulateTouchAndScrollEventSequence(const char* test_name
,
303 gfx::Vector2dF origin
,
304 gfx::Vector2dF distance
,
306 OnHasTouchEventHandlers(true);
308 Gestures gestures
= BuildScrollSequence(steps
, origin
, distance
);
309 Touches touches
= BuildTouchSequence(steps
, origin
, distance
);
310 ASSERT_EQ(touches
.size(), gestures
.size());
312 const size_t event_count
= gestures
.size();
313 const size_t total_event_count
= event_count
* iterations
* 2;
315 InputEventTimer
timer(test_name
, total_event_count
);
316 while (iterations
--) {
317 for (size_t i
= 0; i
< event_count
; ++i
) {
318 SendEvent(touches
[i
], CreateLatencyInfo());
319 // Touches may not be forwarded after the scroll sequence has begun, so
320 // only ack if necessary.
322 SendEventAckIfNecessary(touches
[i
],
323 INPUT_EVENT_ACK_STATE_NOT_CONSUMED
);
326 SendEvent(gestures
[i
], CreateLatencyInfo());
327 SendEventAckIfNecessary(gestures
[i
], INPUT_EVENT_ACK_STATE_CONSUMED
);
328 EXPECT_EQ(2U, GetAndResetAckCount());
334 int64 last_input_id_
;
335 scoped_ptr
<NullIPCSender
> sender_
;
336 scoped_ptr
<NullInputRouterClient
> client_
;
337 scoped_ptr
<NullInputAckHandler
> ack_handler_
;
338 scoped_ptr
<InputRouterImpl
> input_router_
;
339 base::MessageLoopForUI message_loop_
;
342 const size_t kDefaultSteps(100);
343 const size_t kDefaultIterations(100);
344 const gfx::Vector2dF
kDefaultOrigin(100, 100);
345 const gfx::Vector2dF
kDefaultDistance(500, 500);
347 TEST_F(InputRouterImplPerfTest
, TouchSwipe
) {
348 SimulateEventSequence(
350 BuildTouchSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
355 TEST_F(InputRouterImplPerfTest
, TouchSwipeDelayedAck
) {
356 SimulateEventSequence(
357 "TouchSwipeDelayedAck ",
358 BuildTouchSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
363 TEST_F(InputRouterImplPerfTest
, GestureScroll
) {
364 SimulateEventSequence(
366 BuildScrollSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
371 TEST_F(InputRouterImplPerfTest
, GestureScrollDelayedAck
) {
372 SimulateEventSequence(
373 "GestureScrollDelayedAck ",
374 BuildScrollSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
379 TEST_F(InputRouterImplPerfTest
, TouchSwipeToGestureScroll
) {
380 SimulateTouchAndScrollEventSequence("TouchSwipeToGestureScroll ",
387 } // namespace content