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 NativeWebKeyboardEventWithLatencyInfo
& event
,
35 InputEventAckState ack_result
) override
{
38 void OnMouseEventAck(const MouseEventWithLatencyInfo
& event
,
39 InputEventAckState ack_result
) override
{
42 void OnWheelEventAck(const MouseWheelEventWithLatencyInfo
& event
,
43 InputEventAckState ack_result
) override
{
46 void OnTouchEventAck(const TouchEventWithLatencyInfo
& event
,
47 InputEventAckState ack_result
) override
{
50 void OnGestureEventAck(const GestureEventWithLatencyInfo
& event
,
51 InputEventAckState ack_result
) override
{
54 void OnUnexpectedEventAck(UnexpectedEventAckType type
) override
{
58 size_t GetAndResetAckCount() {
59 size_t ack_count
= ack_count_
;
64 size_t ack_count() const { return ack_count_
; }
70 class NullInputRouterClient
: public InputRouterClient
{
72 NullInputRouterClient() {}
73 ~NullInputRouterClient() override
{}
76 InputEventAckState
FilterInputEvent(
77 const blink::WebInputEvent
& input_event
,
78 const ui::LatencyInfo
& latency_info
) override
{
79 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED
;
81 void IncrementInFlightEventCount() override
{}
82 void DecrementInFlightEventCount() override
{}
83 void OnHasTouchEventHandlers(bool has_handlers
) override
{}
84 void DidFlush() override
{}
85 void DidOverscroll(const DidOverscrollParams
& params
) override
{}
86 void DidStopFlinging() override
{}
89 class NullIPCSender
: public IPC::Sender
{
91 NullIPCSender() : sent_count_(0) {}
92 ~NullIPCSender() override
{}
94 bool Send(IPC::Message
* message
) override
{
100 size_t GetAndResetSentEventCount() {
101 size_t message_count
= sent_count_
;
103 return message_count
;
106 bool HasMessages() const { return sent_count_
> 0; }
112 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
113 typedef std::vector
<WebGestureEvent
> Gestures
;
114 Gestures
BuildScrollSequence(size_t steps
,
115 const gfx::Vector2dF
& origin
,
116 const gfx::Vector2dF
& distance
) {
118 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
120 WebGestureEvent gesture
;
121 gesture
.type
= WebInputEvent::GestureScrollBegin
;
122 gesture
.x
= origin
.x();
123 gesture
.y
= origin
.y();
124 gestures
.push_back(gesture
);
126 gesture
.type
= WebInputEvent::GestureScrollUpdate
;
127 gesture
.data
.scrollUpdate
.deltaX
= delta
.x();
128 gesture
.data
.scrollUpdate
.deltaY
= delta
.y();
129 for (size_t i
= 0; i
< steps
; ++i
) {
130 gesture
.x
+= delta
.x();
131 gesture
.y
+= delta
.y();
132 gestures
.push_back(gesture
);
135 gesture
.type
= WebInputEvent::GestureScrollEnd
;
136 gestures
.push_back(gesture
);
140 typedef std::vector
<WebTouchEvent
> Touches
;
141 Touches
BuildTouchSequence(size_t steps
,
142 const gfx::Vector2dF
& origin
,
143 const gfx::Vector2dF
& distance
) {
145 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
148 touch
.touchesLength
= 1;
149 touch
.type
= WebInputEvent::TouchStart
;
150 touch
.touches
[0].id
= 0;
151 touch
.touches
[0].state
= WebTouchPoint::StatePressed
;
152 touch
.touches
[0].position
.x
= origin
.x();
153 touch
.touches
[0].position
.y
= origin
.y();
154 touch
.touches
[0].screenPosition
.x
= origin
.x();
155 touch
.touches
[0].screenPosition
.y
= origin
.y();
156 touches
.push_back(touch
);
158 touch
.type
= WebInputEvent::TouchMove
;
159 touch
.touches
[0].state
= WebTouchPoint::StateMoved
;
160 for (size_t i
= 0; i
< steps
; ++i
) {
161 touch
.touches
[0].position
.x
+= delta
.x();
162 touch
.touches
[0].position
.y
+= delta
.y();
163 touch
.touches
[0].screenPosition
.x
+= delta
.x();
164 touch
.touches
[0].screenPosition
.y
+= delta
.y();
165 touches
.push_back(touch
);
168 touch
.type
= WebInputEvent::TouchEnd
;
169 touch
.touches
[0].state
= WebTouchPoint::StateReleased
;
170 touches
.push_back(touch
);
174 class InputEventTimer
{
176 InputEventTimer(const char* test_name
, int64 event_count
)
177 : test_name_(test_name
),
178 event_count_(event_count
),
179 start_(base::TimeTicks::Now()) {}
182 perf_test::PrintResult(
183 "avg_time_per_event",
186 static_cast<size_t>(((base::TimeTicks::Now() - start_
) / event_count_
)
193 const char* test_name_
;
195 base::TimeTicks start_
;
196 DISALLOW_COPY_AND_ASSIGN(InputEventTimer
);
201 class InputRouterImplPerfTest
: public testing::Test
{
203 InputRouterImplPerfTest() : last_input_id_(0) {}
204 ~InputRouterImplPerfTest() override
{}
208 void SetUp() override
{
209 sender_
.reset(new NullIPCSender());
210 client_
.reset(new NullInputRouterClient());
211 ack_handler_
.reset(new NullInputAckHandler());
212 input_router_
.reset(new InputRouterImpl(sender_
.get(),
216 InputRouterImpl::Config()));
219 void TearDown() override
{
220 base::MessageLoop::current()->RunUntilIdle();
222 input_router_
.reset();
223 ack_handler_
.reset();
228 void SendEvent(const WebGestureEvent
& gesture
,
229 const ui::LatencyInfo
& latency
) {
230 input_router_
->SendGestureEvent(
231 GestureEventWithLatencyInfo(gesture
, latency
));
234 void SendEvent(const WebTouchEvent
& touch
, const ui::LatencyInfo
& latency
) {
235 input_router_
->SendTouchEvent(TouchEventWithLatencyInfo(touch
, latency
));
238 void SendEventAckIfNecessary(const blink::WebInputEvent
& event
,
239 InputEventAckState ack_result
) {
240 if (!WebInputEventTraits::WillReceiveAckFromRenderer(event
))
242 InputEventAck
ack(event
.type
, ack_result
);
243 InputHostMsg_HandleInputEvent_ACK
response(0, ack
);
244 input_router_
->OnMessageReceived(response
);
247 void OnHasTouchEventHandlers(bool has_handlers
) {
248 input_router_
->OnMessageReceived(
249 ViewHostMsg_HasTouchEventHandlers(0, has_handlers
));
252 size_t GetAndResetSentEventCount() {
253 return sender_
->GetAndResetSentEventCount();
256 size_t GetAndResetAckCount() { return ack_handler_
->GetAndResetAckCount(); }
258 size_t AckCount() const { return ack_handler_
->ack_count(); }
260 int64
NextLatencyID() { return ++last_input_id_
; }
262 ui::LatencyInfo
CreateLatencyInfo() {
263 ui::LatencyInfo latency
;
264 latency
.AddLatencyNumber(
265 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT
, 1, 0);
266 latency
.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT
, 1,
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 const gfx::Vector2dF
& origin
,
304 const 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