blob: 0e2f6ff5a6ed09d94f956ad5f01bf09cccc82e7f [file] [log] [blame]
// Copyright 2015 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 "content/common/input/event_with_latency_info.h"
#include <limits>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebInputEvent.h"
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
using blink::WebTouchEvent;
using blink::WebTouchPoint;
using std::numeric_limits;
namespace content {
namespace {
using EventWithLatencyInfoTest = testing::Test;
TouchEventWithLatencyInfo CreateTouchEvent(WebInputEvent::Type type,
double timestamp,
unsigned touch_count = 1) {
TouchEventWithLatencyInfo touch(type, WebInputEvent::kNoModifiers, timestamp,
ui::LatencyInfo());
touch.event.touches_length = touch_count;
return touch;
}
MouseEventWithLatencyInfo CreateMouseEvent(WebInputEvent::Type type,
double timestamp) {
return MouseEventWithLatencyInfo(type, WebInputEvent::kNoModifiers, timestamp,
ui::LatencyInfo());
}
MouseWheelEventWithLatencyInfo CreateMouseWheelEvent(
double timestamp,
float deltaX = 0.0f,
float deltaY = 0.0f,
int modifiers = WebInputEvent::kNoModifiers) {
MouseWheelEventWithLatencyInfo mouse_wheel(
WebInputEvent::kMouseWheel, modifiers, timestamp, ui::LatencyInfo());
mouse_wheel.event.delta_x = deltaX;
mouse_wheel.event.delta_y = deltaY;
return mouse_wheel;
}
GestureEventWithLatencyInfo CreateGestureEvent(WebInputEvent::Type type,
double timestamp,
float x = 0.0f,
float y = 0.0f) {
GestureEventWithLatencyInfo gesture(type, WebInputEvent::kNoModifiers,
timestamp, ui::LatencyInfo());
gesture.event.x = x;
gesture.event.y = y;
return gesture;
}
TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForMouseEvent) {
MouseEventWithLatencyInfo mouse_0 =
CreateMouseEvent(WebInputEvent::kMouseMove, 5.0);
MouseEventWithLatencyInfo mouse_1 =
CreateMouseEvent(WebInputEvent::kMouseMove, 10.0);
ASSERT_TRUE(mouse_0.CanCoalesceWith(mouse_1));
mouse_0.CoalesceWith(mouse_1);
// Coalescing WebMouseEvent preserves newer timestamp.
EXPECT_EQ(10.0, mouse_0.event.TimeStampSeconds());
}
TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForMouseWheelEvent) {
MouseWheelEventWithLatencyInfo mouse_wheel_0 = CreateMouseWheelEvent(5.0);
MouseWheelEventWithLatencyInfo mouse_wheel_1 = CreateMouseWheelEvent(10.0);
ASSERT_TRUE(mouse_wheel_0.CanCoalesceWith(mouse_wheel_1));
mouse_wheel_0.CoalesceWith(mouse_wheel_1);
// Coalescing WebMouseWheelEvent preserves newer timestamp.
EXPECT_EQ(10.0, mouse_wheel_0.event.TimeStampSeconds());
}
TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForTouchEvent) {
TouchEventWithLatencyInfo touch_0 =
CreateTouchEvent(WebInputEvent::kTouchMove, 5.0);
TouchEventWithLatencyInfo touch_1 =
CreateTouchEvent(WebInputEvent::kTouchMove, 10.0);
ASSERT_TRUE(touch_0.CanCoalesceWith(touch_1));
touch_0.CoalesceWith(touch_1);
// Coalescing WebTouchEvent preserves newer timestamp.
EXPECT_EQ(10.0, touch_0.event.TimeStampSeconds());
}
TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForGestureEvent) {
GestureEventWithLatencyInfo scroll_0 =
CreateGestureEvent(WebInputEvent::kGestureScrollUpdate, 5.0);
GestureEventWithLatencyInfo scroll_1 =
CreateGestureEvent(WebInputEvent::kGestureScrollUpdate, 10.0);
ASSERT_TRUE(scroll_0.CanCoalesceWith(scroll_1));
scroll_0.CoalesceWith(scroll_1);
// Coalescing WebGestureEvent preserves newer timestamp.
EXPECT_EQ(10.0, scroll_0.event.TimeStampSeconds());
}
TEST_F(EventWithLatencyInfoTest, LatencyInfoCoalescing) {
MouseEventWithLatencyInfo mouse_0 =
CreateMouseEvent(WebInputEvent::kMouseMove, 5.0);
mouse_0.latency.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, base::TimeTicks(), 1);
MouseEventWithLatencyInfo mouse_1 =
CreateMouseEvent(WebInputEvent::kMouseMove, 10.0);
ASSERT_TRUE(mouse_0.CanCoalesceWith(mouse_1));
ui::LatencyInfo::LatencyComponent component;
EXPECT_FALSE(mouse_1.latency.FindLatency(
ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, &component));
mouse_0.CoalesceWith(mouse_1);
// Coalescing WebMouseEvent preservers older LatencyInfo.
EXPECT_TRUE(mouse_1.latency.FindLatency(
ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, &component));
}
WebTouchPoint CreateTouchPoint(WebTouchPoint::State state, int id) {
WebTouchPoint touch;
touch.state = state;
touch.id = id;
return touch;
}
TouchEventWithLatencyInfo CreateTouch(WebInputEvent::Type type,
unsigned touch_count = 1) {
return CreateTouchEvent(type, 0.0, touch_count);
}
GestureEventWithLatencyInfo CreateGesture(WebInputEvent::Type type,
float x,
float y) {
return CreateGestureEvent(type, 0.0, x, y);
}
MouseWheelEventWithLatencyInfo CreateMouseWheel(float deltaX, float deltaY) {
return CreateMouseWheelEvent(0.0, deltaX, deltaY);
}
template <class T>
bool CanCoalesce(const T& event_to_coalesce, const T& event) {
return event.CanCoalesceWith(event_to_coalesce);
}
template <class T>
void Coalesce(const T& event_to_coalesce, T* event) {
return event->CoalesceWith(event_to_coalesce);
}
TEST_F(EventWithLatencyInfoTest, TouchEventCoalescing) {
TouchEventWithLatencyInfo touch0 = CreateTouch(WebInputEvent::kTouchStart);
TouchEventWithLatencyInfo touch1 = CreateTouch(WebInputEvent::kTouchMove);
// Non touch-moves won't coalesce.
EXPECT_FALSE(CanCoalesce(touch0, touch0));
// Touches of different types won't coalesce.
EXPECT_FALSE(CanCoalesce(touch0, touch1));
// Touch moves with idential touch lengths and touch ids should coalesce.
EXPECT_TRUE(CanCoalesce(touch1, touch1));
// Touch moves with different touch ids should not coalesce.
touch0 = CreateTouch(WebInputEvent::kTouchMove);
touch1 = CreateTouch(WebInputEvent::kTouchMove);
touch0.event.touches[0].id = 7;
EXPECT_FALSE(CanCoalesce(touch0, touch1));
touch0 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch1 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch0.event.touches[0].id = 1;
touch1.event.touches[0].id = 0;
EXPECT_FALSE(CanCoalesce(touch0, touch1));
// Touch moves with different touch lengths should not coalesce.
touch0 = CreateTouch(WebInputEvent::kTouchMove, 1);
touch1 = CreateTouch(WebInputEvent::kTouchMove, 2);
EXPECT_FALSE(CanCoalesce(touch0, touch1));
// Touch moves with identical touch ids in different orders should coalesce.
touch0 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch1 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch0.event.touches[0] = touch1.event.touches[1] =
CreateTouchPoint(WebTouchPoint::kStateMoved, 1);
touch0.event.touches[1] = touch1.event.touches[0] =
CreateTouchPoint(WebTouchPoint::kStateMoved, 0);
EXPECT_TRUE(CanCoalesce(touch0, touch1));
// Pointers with the same ID's should coalesce.
touch0 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch1 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch0.event.touches[0] = touch1.event.touches[1] =
CreateTouchPoint(WebTouchPoint::kStateMoved, 1);
Coalesce(touch0, &touch1);
ASSERT_EQ(1, touch1.event.touches[0].id);
ASSERT_EQ(0, touch1.event.touches[1].id);
EXPECT_EQ(WebTouchPoint::kStateUndefined, touch1.event.touches[1].state);
EXPECT_EQ(WebTouchPoint::kStateMoved, touch1.event.touches[0].state);
// Movement from now-stationary pointers should be preserved.
touch0 = touch1 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch0.event.touches[0] = CreateTouchPoint(WebTouchPoint::kStateMoved, 1);
touch1.event.touches[1] =
CreateTouchPoint(WebTouchPoint::kStateStationary, 1);
touch0.event.touches[1] =
CreateTouchPoint(WebTouchPoint::kStateStationary, 0);
touch1.event.touches[0] = CreateTouchPoint(WebTouchPoint::kStateMoved, 0);
Coalesce(touch0, &touch1);
ASSERT_EQ(1, touch1.event.touches[0].id);
ASSERT_EQ(0, touch1.event.touches[1].id);
EXPECT_EQ(WebTouchPoint::kStateMoved, touch1.event.touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateMoved, touch1.event.touches[1].state);
// Touch moves with different dispatchTypes coalesce.
touch0 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch0.event.dispatch_type = WebInputEvent::DispatchType::kBlocking;
touch1 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch1.event.dispatch_type = WebInputEvent::DispatchType::kEventNonBlocking;
touch0.event.touches[0] = touch1.event.touches[1] =
CreateTouchPoint(WebTouchPoint::kStateMoved, 1);
touch0.event.touches[1] = touch1.event.touches[0] =
CreateTouchPoint(WebTouchPoint::kStateMoved, 0);
EXPECT_TRUE(CanCoalesce(touch0, touch1));
Coalesce(touch0, &touch1);
ASSERT_EQ(WebInputEvent::DispatchType::kBlocking, touch1.event.dispatch_type);
touch0 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch0.event.dispatch_type =
WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling;
touch1 = CreateTouch(WebInputEvent::kTouchMove, 2);
touch1.event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
touch0.event.touches[0] = touch1.event.touches[1] =
CreateTouchPoint(WebTouchPoint::kStateMoved, 1);
touch0.event.touches[1] = touch1.event.touches[0] =
CreateTouchPoint(WebTouchPoint::kStateMoved, 0);
EXPECT_TRUE(CanCoalesce(touch0, touch1));
Coalesce(touch0, &touch1);
ASSERT_EQ(WebInputEvent::DispatchType::kListenersNonBlockingPassive,
touch1.event.dispatch_type);
}
TEST_F(EventWithLatencyInfoTest, PinchEventCoalescing) {
GestureEventWithLatencyInfo pinch0 =
CreateGesture(WebInputEvent::kGesturePinchBegin, 1, 1);
GestureEventWithLatencyInfo pinch1 =
CreateGesture(WebInputEvent::kGesturePinchUpdate, 2, 2);
// Only GesturePinchUpdate's coalesce.
EXPECT_FALSE(CanCoalesce(pinch0, pinch0));
// Pinch gestures of different types should not coalesce.
EXPECT_FALSE(CanCoalesce(pinch0, pinch1));
// Pinches with different focal points should not coalesce.
pinch0 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 1, 1);
pinch1 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 2, 2);
EXPECT_FALSE(CanCoalesce(pinch0, pinch1));
EXPECT_TRUE(CanCoalesce(pinch0, pinch0));
// Coalesced scales are multiplicative.
pinch0 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 1, 1);
pinch0.event.data.pinch_update.scale = 2.f;
pinch1 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 1, 1);
pinch1.event.data.pinch_update.scale = 3.f;
EXPECT_TRUE(CanCoalesce(pinch0, pinch0));
Coalesce(pinch0, &pinch1);
EXPECT_EQ(2.f * 3.f, pinch1.event.data.pinch_update.scale);
// Scales have a minimum value and can never reach 0.
ASSERT_GT(numeric_limits<float>::min(), 0);
pinch0 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 1, 1);
pinch0.event.data.pinch_update.scale = numeric_limits<float>::min() * 2.0f;
pinch1 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 1, 1);
pinch1.event.data.pinch_update.scale = numeric_limits<float>::min() * 5.0f;
EXPECT_TRUE(CanCoalesce(pinch0, pinch1));
Coalesce(pinch0, &pinch1);
EXPECT_EQ(numeric_limits<float>::min(), pinch1.event.data.pinch_update.scale);
// Scales have a maximum value and can never reach Infinity.
pinch0 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 1, 1);
pinch0.event.data.pinch_update.scale = numeric_limits<float>::max() / 2.0f;
pinch1 = CreateGesture(WebInputEvent::kGesturePinchUpdate, 1, 1);
pinch1.event.data.pinch_update.scale = 10.0f;
EXPECT_TRUE(CanCoalesce(pinch0, pinch1));
Coalesce(pinch0, &pinch1);
EXPECT_EQ(numeric_limits<float>::max(), pinch1.event.data.pinch_update.scale);
}
TEST_F(EventWithLatencyInfoTest, WebMouseWheelEventCoalescing) {
MouseWheelEventWithLatencyInfo mouse_wheel_0 = CreateMouseWheel(1, 1);
MouseWheelEventWithLatencyInfo mouse_wheel_1 = CreateMouseWheel(2, 2);
// WebMouseWheelEvent objects with same values except different deltaX and
// deltaY should coalesce.
EXPECT_TRUE(CanCoalesce(mouse_wheel_0, mouse_wheel_1));
// WebMouseWheelEvent objects with different modifiers should not coalesce.
mouse_wheel_0 = CreateMouseWheelEvent(2.0, 1, 1, WebInputEvent::kControlKey);
mouse_wheel_1 = CreateMouseWheelEvent(2.0, 1, 1, WebInputEvent::kShiftKey);
EXPECT_FALSE(CanCoalesce(mouse_wheel_0, mouse_wheel_1));
// Coalesce old and new events.
mouse_wheel_0 = CreateMouseWheel(1, 1);
mouse_wheel_0.event.SetPositionInWidget(1, 1);
mouse_wheel_1 = CreateMouseWheel(2, 2);
mouse_wheel_1.event.SetPositionInWidget(2, 2);
MouseWheelEventWithLatencyInfo mouse_wheel_1_copy = mouse_wheel_1;
EXPECT_TRUE(CanCoalesce(mouse_wheel_0, mouse_wheel_1));
EXPECT_EQ(mouse_wheel_0.event.GetModifiers(),
mouse_wheel_1.event.GetModifiers());
EXPECT_EQ(mouse_wheel_0.event.scroll_by_page,
mouse_wheel_1.event.scroll_by_page);
EXPECT_EQ(mouse_wheel_0.event.phase, mouse_wheel_1.event.phase);
EXPECT_EQ(mouse_wheel_0.event.momentum_phase,
mouse_wheel_1.event.momentum_phase);
EXPECT_EQ(mouse_wheel_0.event.has_precise_scrolling_deltas,
mouse_wheel_1.event.has_precise_scrolling_deltas);
Coalesce(mouse_wheel_0, &mouse_wheel_1);
// Coalesced event has the position of the most recent event.
EXPECT_EQ(1, mouse_wheel_1.event.PositionInWidget().x);
EXPECT_EQ(1, mouse_wheel_1.event.PositionInWidget().y);
// deltaX/Y, wheelTicksX/Y, and movementX/Y of the coalesced event are
// calculated properly.
EXPECT_EQ(mouse_wheel_1_copy.event.delta_x + mouse_wheel_0.event.delta_x,
mouse_wheel_1.event.delta_x);
EXPECT_EQ(mouse_wheel_1_copy.event.delta_y + mouse_wheel_0.event.delta_y,
mouse_wheel_1.event.delta_y);
EXPECT_EQ(mouse_wheel_1_copy.event.wheel_ticks_x +
mouse_wheel_0.event.wheel_ticks_x,
mouse_wheel_1.event.wheel_ticks_x);
EXPECT_EQ(mouse_wheel_1_copy.event.wheel_ticks_y +
mouse_wheel_0.event.wheel_ticks_y,
mouse_wheel_1.event.wheel_ticks_y);
EXPECT_EQ(
mouse_wheel_1_copy.event.movement_x + mouse_wheel_0.event.movement_x,
mouse_wheel_1.event.movement_x);
EXPECT_EQ(
mouse_wheel_1_copy.event.movement_y + mouse_wheel_0.event.movement_y,
mouse_wheel_1.event.movement_y);
}
// Coalescing preserves the newer timestamp.
TEST_F(EventWithLatencyInfoTest, TimestampCoalescing) {
MouseWheelEventWithLatencyInfo mouse_wheel_0 =
CreateMouseWheelEvent(5.0, 1, 1);
MouseWheelEventWithLatencyInfo mouse_wheel_1 =
CreateMouseWheelEvent(10.0, 2, 2);
EXPECT_TRUE(CanCoalesce(mouse_wheel_0, mouse_wheel_1));
Coalesce(mouse_wheel_1, &mouse_wheel_0);
EXPECT_EQ(10.0, mouse_wheel_0.event.TimeStampSeconds());
}
} // namespace
} // namespace content