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 ~NullInputAckHandler() override
{}
34 void OnKeyboardEventAck(const NativeWebKeyboardEvent
& event
,
35 InputEventAckState ack_result
) override
{
38 void OnWheelEventAck(const MouseWheelEventWithLatencyInfo
& event
,
39 InputEventAckState ack_result
) override
{
42 void OnTouchEventAck(const TouchEventWithLatencyInfo
& event
,
43 InputEventAckState ack_result
) override
{
46 void OnGestureEventAck(const GestureEventWithLatencyInfo
& event
,
47 InputEventAckState ack_result
) override
{
50 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 ~NullInputRouterClient() override
{}
72 InputEventAckState
FilterInputEvent(
73 const blink::WebInputEvent
& input_event
,
74 const ui::LatencyInfo
& latency_info
) override
{
75 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED
;
77 void IncrementInFlightEventCount() override
{}
78 void DecrementInFlightEventCount() override
{}
79 void OnHasTouchEventHandlers(bool has_handlers
) override
{}
80 void DidFlush() override
{}
81 void DidOverscroll(const DidOverscrollParams
& params
) override
{}
82 void DidStopFlinging() override
{}
85 class NullIPCSender
: public IPC::Sender
{
87 NullIPCSender() : sent_count_(0) {}
88 ~NullIPCSender() override
{}
90 bool Send(IPC::Message
* message
) override
{
96 size_t GetAndResetSentEventCount() {
97 size_t message_count
= sent_count_
;
102 bool HasMessages() const { return sent_count_
> 0; }
108 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
109 typedef std::vector
<WebGestureEvent
> Gestures
;
110 Gestures
BuildScrollSequence(size_t steps
,
111 gfx::Vector2dF origin
,
112 gfx::Vector2dF distance
) {
114 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
116 WebGestureEvent gesture
;
117 gesture
.type
= WebInputEvent::GestureScrollBegin
;
118 gesture
.x
= origin
.x();
119 gesture
.y
= origin
.y();
120 gestures
.push_back(gesture
);
122 gesture
.type
= WebInputEvent::GestureScrollUpdate
;
123 gesture
.data
.scrollUpdate
.deltaX
= delta
.x();
124 gesture
.data
.scrollUpdate
.deltaY
= delta
.y();
125 for (size_t i
= 0; i
< steps
; ++i
) {
126 gesture
.x
+= delta
.x();
127 gesture
.y
+= delta
.y();
128 gestures
.push_back(gesture
);
131 gesture
.type
= WebInputEvent::GestureScrollEnd
;
132 gestures
.push_back(gesture
);
136 typedef std::vector
<WebTouchEvent
> Touches
;
137 Touches
BuildTouchSequence(size_t steps
,
138 gfx::Vector2dF origin
,
139 gfx::Vector2dF distance
) {
141 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
144 touch
.touchesLength
= 1;
145 touch
.type
= WebInputEvent::TouchStart
;
146 touch
.touches
[0].id
= 0;
147 touch
.touches
[0].state
= WebTouchPoint::StatePressed
;
148 touch
.touches
[0].position
.x
= origin
.x();
149 touch
.touches
[0].position
.y
= origin
.y();
150 touch
.touches
[0].screenPosition
.x
= origin
.x();
151 touch
.touches
[0].screenPosition
.y
= origin
.y();
152 touches
.push_back(touch
);
154 touch
.type
= WebInputEvent::TouchMove
;
155 touch
.touches
[0].state
= WebTouchPoint::StateMoved
;
156 for (size_t i
= 0; i
< steps
; ++i
) {
157 touch
.touches
[0].position
.x
+= delta
.x();
158 touch
.touches
[0].position
.y
+= delta
.y();
159 touch
.touches
[0].screenPosition
.x
+= delta
.x();
160 touch
.touches
[0].screenPosition
.y
+= delta
.y();
161 touches
.push_back(touch
);
164 touch
.type
= WebInputEvent::TouchEnd
;
165 touch
.touches
[0].state
= WebTouchPoint::StateReleased
;
166 touches
.push_back(touch
);
170 class InputEventTimer
{
172 InputEventTimer(const char* test_name
, int64 event_count
)
173 : test_name_(test_name
),
174 event_count_(event_count
),
175 start_(base::TimeTicks::Now()) {}
178 perf_test::PrintResult(
179 "avg_time_per_event",
182 static_cast<size_t>(((base::TimeTicks::Now() - start_
) / event_count_
)
189 const char* test_name_
;
191 base::TimeTicks start_
;
192 DISALLOW_COPY_AND_ASSIGN(InputEventTimer
);
197 class InputRouterImplPerfTest
: public testing::Test
{
199 InputRouterImplPerfTest() : last_input_id_(0) {}
200 ~InputRouterImplPerfTest() override
{}
204 void SetUp() override
{
205 sender_
.reset(new NullIPCSender());
206 client_
.reset(new NullInputRouterClient());
207 ack_handler_
.reset(new NullInputAckHandler());
208 input_router_
.reset(new InputRouterImpl(sender_
.get(),
212 InputRouterImpl::Config()));
215 void TearDown() override
{
216 base::MessageLoop::current()->RunUntilIdle();
218 input_router_
.reset();
219 ack_handler_
.reset();
224 void SendEvent(const WebGestureEvent
& gesture
,
225 const ui::LatencyInfo
& latency
) {
226 input_router_
->SendGestureEvent(
227 GestureEventWithLatencyInfo(gesture
, latency
));
230 void SendEvent(const WebTouchEvent
& touch
, const ui::LatencyInfo
& latency
) {
231 input_router_
->SendTouchEvent(TouchEventWithLatencyInfo(touch
, latency
));
234 void SendEventAckIfNecessary(const blink::WebInputEvent
& event
,
235 InputEventAckState ack_result
) {
236 if (WebInputEventTraits::IgnoresAckDisposition(event
))
238 InputHostMsg_HandleInputEvent_ACK_Params ack
;
239 ack
.type
= event
.type
;
240 ack
.state
= ack_result
;
241 InputHostMsg_HandleInputEvent_ACK
response(0, ack
);
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(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT
, 1,
269 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
270 template <typename EventType
>
271 void SimulateEventSequence(const char* test_name
,
272 const std::vector
<EventType
>& events
,
275 OnHasTouchEventHandlers(true);
277 const size_t event_count
= events
.size();
278 const size_t total_event_count
= event_count
* iterations
;
280 InputEventTimer
timer(test_name
, total_event_count
);
281 while (iterations
--) {
282 size_t i
= 0, ack_i
= 0;
284 SendEvent(events
[i
++], CreateLatencyInfo());
286 for (; i
< event_count
; ++i
, ++ack_i
) {
287 SendEvent(events
[i
], CreateLatencyInfo());
288 SendEventAckIfNecessary(events
[ack_i
], INPUT_EVENT_ACK_STATE_CONSUMED
);
292 SendEventAckIfNecessary(events
.back(), INPUT_EVENT_ACK_STATE_CONSUMED
);
294 EXPECT_EQ(event_count
, GetAndResetSentEventCount());
295 EXPECT_EQ(event_count
, GetAndResetAckCount());
299 void SimulateTouchAndScrollEventSequence(const char* test_name
,
301 gfx::Vector2dF origin
,
302 gfx::Vector2dF distance
,
304 OnHasTouchEventHandlers(true);
306 Gestures gestures
= BuildScrollSequence(steps
, origin
, distance
);
307 Touches touches
= BuildTouchSequence(steps
, origin
, distance
);
308 ASSERT_EQ(touches
.size(), gestures
.size());
310 const size_t event_count
= gestures
.size();
311 const size_t total_event_count
= event_count
* iterations
* 2;
313 InputEventTimer
timer(test_name
, total_event_count
);
314 while (iterations
--) {
315 for (size_t i
= 0; i
< event_count
; ++i
) {
316 SendEvent(touches
[i
], CreateLatencyInfo());
317 // Touches may not be forwarded after the scroll sequence has begun, so
318 // only ack if necessary.
320 SendEventAckIfNecessary(touches
[i
],
321 INPUT_EVENT_ACK_STATE_NOT_CONSUMED
);
324 SendEvent(gestures
[i
], CreateLatencyInfo());
325 SendEventAckIfNecessary(gestures
[i
], INPUT_EVENT_ACK_STATE_CONSUMED
);
326 EXPECT_EQ(2U, GetAndResetAckCount());
332 int64 last_input_id_
;
333 scoped_ptr
<NullIPCSender
> sender_
;
334 scoped_ptr
<NullInputRouterClient
> client_
;
335 scoped_ptr
<NullInputAckHandler
> ack_handler_
;
336 scoped_ptr
<InputRouterImpl
> input_router_
;
337 base::MessageLoopForUI message_loop_
;
340 const size_t kDefaultSteps(100);
341 const size_t kDefaultIterations(100);
342 const gfx::Vector2dF
kDefaultOrigin(100, 100);
343 const gfx::Vector2dF
kDefaultDistance(500, 500);
345 TEST_F(InputRouterImplPerfTest
, TouchSwipe
) {
346 SimulateEventSequence(
348 BuildTouchSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
353 TEST_F(InputRouterImplPerfTest
, TouchSwipeDelayedAck
) {
354 SimulateEventSequence(
355 "TouchSwipeDelayedAck ",
356 BuildTouchSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
361 TEST_F(InputRouterImplPerfTest
, GestureScroll
) {
362 SimulateEventSequence(
364 BuildScrollSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
369 TEST_F(InputRouterImplPerfTest
, GestureScrollDelayedAck
) {
370 SimulateEventSequence(
371 "GestureScrollDelayedAck ",
372 BuildScrollSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
377 TEST_F(InputRouterImplPerfTest
, TouchSwipeToGestureScroll
) {
378 SimulateTouchAndScrollEventSequence("TouchSwipeToGestureScroll ",
385 } // namespace content