blob: 49e5d9ea7d8eb44b5b506c9855c7961c0f5620d2 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/input/touchpad_pinch_event_queue.h"
#include <string>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "components/input/event_with_latency_info.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/latency/latency_info.h"
namespace input {
class MockTouchpadPinchEventQueueClient {
public:
MockTouchpadPinchEventQueueClient() = default;
~MockTouchpadPinchEventQueueClient() = default;
// TouchpadPinchEventQueueClient
MOCK_METHOD1(SendMouseWheelEventForPinchImmediately,
void(const MouseWheelEventWithLatencyInfo& event));
MOCK_METHOD3(OnGestureEventForPinchAck,
void(const GestureEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result));
};
class TouchpadPinchEventQueueTest : public testing::TestWithParam<bool>,
public TouchpadPinchEventQueueClient {
protected:
TouchpadPinchEventQueueTest() {
queue_ = std::make_unique<TouchpadPinchEventQueue>(this);
}
~TouchpadPinchEventQueueTest() = default;
void QueueEvent(const blink::WebGestureEvent& event) {
ScopedDispatchToRendererCallback dispatch_callback(base::DoNothing());
queue_->QueueEvent(GestureEventWithLatencyInfo(event),
dispatch_callback.callback);
}
void QueuePinchBegin() {
blink::WebGestureEvent event(
blink::WebInputEvent::Type::kGesturePinchBegin,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests(),
blink::WebGestureDevice::kTouchpad);
event.SetPositionInWidget(gfx::PointF(1, 1));
event.SetPositionInScreen(gfx::PointF(1, 1));
event.SetNeedsWheelEvent(true);
QueueEvent(event);
}
void QueuePinchEnd() {
blink::WebGestureEvent event(
blink::WebInputEvent::Type::kGesturePinchEnd,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests(),
blink::WebGestureDevice::kTouchpad);
event.SetPositionInWidget(gfx::PointF(1, 1));
event.SetPositionInScreen(gfx::PointF(1, 1));
event.SetNeedsWheelEvent(true);
QueueEvent(event);
}
void QueuePinchUpdate(float scale, bool zoom_disabled) {
blink::WebGestureEvent event(
blink::WebInputEvent::Type::kGesturePinchUpdate,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests(),
blink::WebGestureDevice::kTouchpad);
event.SetPositionInWidget(gfx::PointF(1, 1));
event.SetPositionInScreen(gfx::PointF(1, 1));
event.SetNeedsWheelEvent(true);
event.data.pinch_update.zoom_disabled = zoom_disabled;
event.data.pinch_update.scale = scale;
QueueEvent(event);
}
void QueueDoubleTap() {
blink::WebGestureEvent event(
blink::WebInputEvent::Type::kGestureDoubleTap,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests(),
blink::WebGestureDevice::kTouchpad);
event.SetPositionInWidget(gfx::PointF(1, 1));
event.SetPositionInScreen(gfx::PointF(1, 1));
event.data.tap.tap_count = 1;
event.SetNeedsWheelEvent(true);
QueueEvent(event);
}
using HandleEventCallback =
base::OnceCallback<void(blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result)>;
void SendWheelEventAck(blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
std::move(callbacks_.front()).Run(ack_source, ack_result);
callbacks_.pop_front();
}
void SendMouseWheelEventForPinchImmediately(
const blink::WebGestureEvent& pinch_event,
const MouseWheelEventWithLatencyInfo& event,
MouseWheelEventHandledCallback callback,
DispatchToRendererCallback& dispatch_callback) override {
mock_client_.SendMouseWheelEventForPinchImmediately(event);
callbacks_.emplace_back(base::BindOnce(
[](MouseWheelEventHandledCallback callback,
const MouseWheelEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
std::move(callback).Run(event, ack_source, ack_result);
},
std::move(callback), event));
}
void OnGestureEventForPinchAck(
const GestureEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) override {
mock_client_.OnGestureEventForPinchAck(event, ack_source, ack_result);
}
testing::StrictMock<MockTouchpadPinchEventQueueClient> mock_client_;
std::unique_ptr<TouchpadPinchEventQueue> queue_;
base::circular_deque<HandleEventCallback> callbacks_;
};
MATCHER_P(EventHasType,
type,
std::string(negation ? "does not have" : "has") + " type " +
::testing::PrintToString(type)) {
return arg.event.GetType() == type;
}
MATCHER_P(EventHasPhase,
phase,
std::string(negation ? "does not have" : "has") + " phase " +
::testing::PrintToString(phase)) {
return arg.event.phase == phase;
}
MATCHER_P(EventHasScale,
expected_scale,
std::string(negation ? "does not have" : "has") + " scale " +
::testing::PrintToString(expected_scale)) {
const float actual_scale = exp(arg.event.delta_y / 100.0f);
return ::testing::Matches(::testing::FloatEq(expected_scale))(actual_scale);
}
MATCHER(EventHasCtrlModifier,
std::string(negation ? "does not have" : "has") + " control modifier") {
return (arg.event.GetModifiers() & blink::WebInputEvent::kControlKey) != 0;
}
MATCHER(EventIsBlocking,
std::string(negation ? "is not" : "is") + " blocking") {
return arg.event.dispatch_type ==
blink::WebInputEvent::DispatchType::kBlocking;
}
// Ensure that when the queue receives a touchpad pinch sequence, it sends a
// synthetic mouse wheel event and acks the pinch events back to the client.
TEST_F(TouchpadPinchEventQueueTest, Basic) {
::testing::InSequence sequence;
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
::testing::AllOf(EventHasCtrlModifier(), EventIsBlocking())));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), ::testing::Not(EventIsBlocking()))));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
// Ensure the queue sends the wheel events with phase information.
TEST_F(TouchpadPinchEventQueueTest, MouseWheelPhase) {
::testing::InSequence sequence;
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
EventHasPhase(blink::WebMouseWheelEvent::kPhaseBegan)));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
EventHasPhase(blink::WebMouseWheelEvent::kPhaseChanged)));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
testing::_, testing::_));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
EventHasPhase(blink::WebMouseWheelEvent::kPhaseEnded)));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
// Ensure that if the renderer consumes the synthetic wheel event, the ack of
// the GesturePinchUpdate reflects this.
TEST_F(TouchpadPinchEventQueueTest, Consumed) {
::testing::InSequence sequence;
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
::testing::AllOf(EventHasCtrlModifier(), EventIsBlocking())));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), ::testing::Not(EventIsBlocking()))));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
// Ensure that the queue sends wheel events for updates with |zoom_disabled| as
// well.
TEST_F(TouchpadPinchEventQueueTest, ZoomDisabled) {
::testing::InSequence sequence;
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
::testing::AllOf(EventHasCtrlModifier(), EventIsBlocking())));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), ::testing::Not(EventIsBlocking()))));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
QueuePinchUpdate(1.23, true);
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
TEST_F(TouchpadPinchEventQueueTest, MultipleSequences) {
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored))
.Times(2);
EXPECT_CALL(mock_client_, SendMouseWheelEventForPinchImmediately(testing::_))
.Times(4);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists))
.Times(2);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored))
.Times(2);
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
// Ensure we can queue additional pinch event sequences while the queue is
// waiting for a wheel event ack.
TEST_F(TouchpadPinchEventQueueTest, MultipleQueuedSequences) {
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_, SendMouseWheelEventForPinchImmediately(testing::_));
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
testing::Mock::VerifyAndClearExpectations(&mock_client_);
QueuePinchEnd();
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
QueuePinchEnd();
// No calls since we're still waiting on the ack for the first wheel event.
testing::Mock::VerifyAndClearExpectations(&mock_client_);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_, SendMouseWheelEventForPinchImmediately(testing::_))
.Times(2);
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
// ACK for end event.
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
// After acking the first wheel event, the queue continues.
testing::Mock::VerifyAndClearExpectations(&mock_client_);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_, SendMouseWheelEventForPinchImmediately(testing::_));
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
// ACK for end event.
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
// Ensure the queue handles pinch event sequences with multiple updates.
TEST_F(TouchpadPinchEventQueueTest, MultipleUpdatesInSequence) {
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
// Only first wheel event is cancelable.
// Here the second and the third wheel events are not blocking because we
// ack the first wheel event as unconsumed.
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
::testing::AllOf(EventHasCtrlModifier(), EventIsBlocking())));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), ::testing::Not(EventIsBlocking()))))
.Times(3);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored))
.Times(2);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
// Ensure the queue coalesces pinch update events.
TEST_F(TouchpadPinchEventQueueTest, MultipleUpdatesCoalesced) {
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
// Only the first wheel event is cancelable.
// Here the second wheel is not blocking because we ack the first wheel
// event as unconsumed.
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
::testing::AllOf(EventHasCtrlModifier(), EventIsBlocking())));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), ::testing::Not(EventIsBlocking()))))
.Times(2);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
// The queue will send the first wheel event for this first update.
QueuePinchUpdate(1.23, false);
// Before the first wheel event is acked, queue another update.
QueuePinchUpdate(1.23, false);
// Queue a third update. This will be coalesced with the second update which
// is currently in the queue.
QueuePinchUpdate(1.23, false);
QueuePinchEnd();
// Ack for the wheel event corresponding to the first update.
SendWheelEventAck(blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNoConsumerExists);
// Ack for the wheel event corresponding to the second and third updates.
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
// ACK for end event.
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
EXPECT_FALSE(queue_->has_pending());
}
// Ensure the queue handles pinch event sequences with multiple canceled
// updates.
TEST_F(TouchpadPinchEventQueueTest, MultipleCanceledUpdatesInSequence) {
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
::testing::AllOf(EventHasCtrlModifier(), EventIsBlocking())))
.Times(3);
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed))
.Times(3);
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), ::testing::Not(EventIsBlocking()))));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed);
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed);
QueuePinchUpdate(1.23, false);
SendWheelEventAck(blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
// Ensure that when the queue receives a touchpad double tap, it sends a
// synthetic mouse wheel event and acks the double tap back to the client.
TEST_F(TouchpadPinchEventQueueTest, DoubleTap) {
::testing::InSequence sequence;
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), EventIsBlocking(),
EventHasPhase(blink::WebMouseWheelEvent::kPhaseNone),
EventHasScale(1.0f))));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGestureDoubleTap),
blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed));
QueueDoubleTap();
SendWheelEventAck(blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed);
}
// Ensure that ACKs are only processed when they match the event that is
// currently awaiting an ACK.
TEST_F(TouchpadPinchEventQueueTest, IgnoreNonMatchingEvents) {
::testing::InSequence sequence;
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchBegin),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(
::testing::AllOf(EventHasCtrlModifier(), EventIsBlocking())));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchUpdate),
blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed));
EXPECT_CALL(mock_client_,
SendMouseWheelEventForPinchImmediately(::testing::AllOf(
EventHasCtrlModifier(), ::testing::Not(EventIsBlocking()))));
EXPECT_CALL(mock_client_,
OnGestureEventForPinchAck(
EventHasType(blink::WebInputEvent::Type::kGesturePinchEnd),
blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored));
QueuePinchBegin();
QueuePinchUpdate(1.23, false);
QueuePinchEnd();
SendWheelEventAck(blink::mojom::InputEventResultSource::kMainThread,
blink::mojom::InputEventResultState::kConsumed);
SendWheelEventAck(blink::mojom::InputEventResultSource::kBrowser,
blink::mojom::InputEventResultState::kIgnored);
}
} // namespace input