Commit 46d03267 authored by Mustaq Ahmed's avatar Mustaq Ahmed Committed by Commit Bot

Suppress firing JS events for pointercancel events with non-existent

ids.

We encountered a device that reserves three-finger touches for a
system-defined gesture, so it cancels all touch points at the moment the
third finger touches the screen.  But the device wrongly sends a
pointercancel for the third finger without sending a corresponding
pointerdown first.  This caused Blink to fire a pointercancel with a new
id, which is not a valid event sequence!

This CL suppresses such pointercancel events.

Bug: 1007164
Change-Id: I0af68ac229e56344ee013a8e65fe18b3cc4e9fd7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2488181
Commit-Queue: Mustaq Ahmed <mustaq@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#821329}
parent 152bdaf4
......@@ -222,6 +222,7 @@ PointerEventInit* PointerEventFactory::ConvertIdTypeButtonsEvent(
const WebPointerEvent& web_pointer_event) {
WebPointerProperties::PointerType pointer_type =
web_pointer_event.pointer_type;
unsigned buttons;
if (web_pointer_event.hovering) {
buttons = MouseEvent::WebInputEventModifiersToButtons(
......@@ -248,12 +249,16 @@ PointerEventInit* PointerEventFactory::ConvertIdTypeButtonsEvent(
}
pointer_type = WebPointerProperties::PointerType::kPen;
}
PointerEventInit* pointer_event_init = PointerEventInit::Create();
pointer_event_init->setButtons(buttons);
const IncomingId incoming_id(pointer_type, web_pointer_event.id);
PointerId pointer_id = AddIdAndActiveButtons(incoming_id, buttons != 0,
web_pointer_event.hovering);
web_pointer_event.hovering,
web_pointer_event.GetType());
if (pointer_id == kInvalidId)
return nullptr;
PointerEventInit* pointer_event_init = PointerEventInit::Create();
pointer_event_init->setButtons(buttons);
pointer_event_init->setPointerId(pointer_id);
pointer_event_init->setPointerType(
PointerTypeNameForWebPointPointerType(pointer_type));
......@@ -293,6 +298,8 @@ PointerEvent* PointerEventFactory::Create(
PointerEventInit* pointer_event_init =
ConvertIdTypeButtonsEvent(web_pointer_event);
if (!pointer_event_init)
return nullptr;
AtomicString type = PointerEventNameForEventType(event_type);
if (event_type == WebInputEvent::Type::kPointerDown ||
......@@ -491,7 +498,7 @@ PointerEventFactory::~PointerEventFactory() {
void PointerEventFactory::Clear() {
for (int type = 0;
type <= ToInt(WebPointerProperties::PointerType::kMaxValue); type++) {
primary_id_[type] = PointerEventFactory::kInvalidId;
primary_id_[type] = kInvalidId;
id_count_[type] = 0;
}
pointer_incoming_id_mapping_.clear();
......@@ -510,9 +517,11 @@ void PointerEventFactory::Clear() {
current_id_ = PointerEventFactory::kMouseId + 1;
}
PointerId PointerEventFactory::AddIdAndActiveButtons(const IncomingId p,
PointerId PointerEventFactory::AddIdAndActiveButtons(
const IncomingId p,
bool is_active_buttons,
bool hovering) {
bool hovering,
WebInputEvent::Type event_type) {
// Do not add extra mouse pointer as it was added in initialization.
if (p.GetPointerType() == WebPointerProperties::PointerType::kMouse) {
pointer_id_mapping_.Set(kMouseId,
......@@ -526,6 +535,12 @@ PointerId PointerEventFactory::AddIdAndActiveButtons(const IncomingId p,
PointerAttributes(p, is_active_buttons, hovering));
return mapped_id;
}
// TODO(crbug.com/1141595): We should filter out bad pointercancel events
// further upstream.
if (event_type == WebInputEvent::Type::kPointerCancel)
return kInvalidId;
int type_int = p.PointerTypeInt();
// We do not handle the overflow of |current_id_| as it should be very rare.
PointerId mapped_id = current_id_++;
......@@ -549,7 +564,7 @@ bool PointerEventFactory::Remove(const PointerId mapped_id) {
pointer_incoming_id_mapping_.erase(p);
RemoveLastPosition(mapped_id);
if (primary_id_[type_int] == mapped_id)
primary_id_[type_int] = PointerEventFactory::kInvalidId;
primary_id_[type_int] = kInvalidId;
id_count_[type_int]--;
return true;
}
......@@ -594,8 +609,7 @@ bool PointerEventFactory::IsPrimary(
return true;
PointerId pointer_id = GetPointerEventId(properties);
return (pointer_id != PointerEventFactory::kInvalidId &&
IsPrimary(pointer_id));
return (pointer_id != kInvalidId && IsPrimary(pointer_id));
}
bool PointerEventFactory::IsActiveButtonsState(
......@@ -618,7 +632,7 @@ PointerId PointerEventFactory::GetPointerEventId(
IncomingId id(properties.pointer_type, properties.id);
if (pointer_incoming_id_mapping_.Contains(id))
return pointer_incoming_id_mapping_.at(id);
return PointerEventFactory::kInvalidId;
return kInvalidId;
}
} // namespace blink
......@@ -30,6 +30,8 @@ class CORE_EXPORT PointerEventFactory {
PointerEventFactory();
~PointerEventFactory();
// Returns nullptr if the |web_pointer_event| is invalid from event stream
// perspective (e.g. it is a pointercancel for a non-existent id).
PointerEvent* Create(const WebPointerEvent& web_pointer_event,
const Vector<WebPointerEvent>& coalesced_events,
const Vector<WebPointerEvent>& predicted_events,
......@@ -128,9 +130,14 @@ class CORE_EXPORT PointerEventFactory {
PointerId AddIdAndActiveButtons(const IncomingId,
bool is_active_buttons,
bool hovering);
bool hovering,
WebInputEvent::Type event_type);
bool IsPrimary(const PointerId) const;
// Returns nullptr when the event is unexpected. E.g. pointercancel for a
// non-existent id (see crbug.com/1007164).
PointerEventInit* ConvertIdTypeButtonsEvent(const WebPointerEvent&);
void SetEventSpecificFields(PointerEventInit*, const AtomicString& type);
// Creates pointerevents like boundary and capture events from another
......
......@@ -463,8 +463,12 @@ WebInputEventResult PointerEventManager::DispatchTouchPointerEvent(
? pointer_event_target.target_element->GetDocument().domWindow()
: nullptr);
if (pointer_event) {
result = SendTouchPointerEvent(pointer_event_target.target_element,
pointer_event, web_pointer_event.hovering);
} else {
result = WebInputEventResult::kNotHandled;
}
// If a pointerdown has been canceled, queue the unique id to allow
// suppressing mouse events from gesture events. For mouse events
......
......@@ -22,23 +22,29 @@ namespace {
class CheckPointerEventListenerCallback final : public NativeEventListener {
public:
void Invoke(ExecutionContext*, Event* event) override {
const String pointer_type = ((PointerEvent*)event)->pointerType();
num_events_received_++;
const String pointer_type =
static_cast<PointerEvent*>(event)->pointerType();
if (pointer_type == "mouse")
mouse_event_received_count_++;
num_type_mouse_received_++;
else if (pointer_type == "touch")
touch_event_received_count_++;
num_type_touch_received_++;
else if (pointer_type == "pen")
pen_event_received_count_++;
num_type_pen_received_++;
}
int mouseEventCount() const { return mouse_event_received_count_; }
int touchEventCount() const { return touch_event_received_count_; }
int penEventCount() const { return pen_event_received_count_; }
int numEventsReceived() const { return num_events_received_; }
int numTypeMouseReceived() const { return num_type_mouse_received_; }
int numTypeTouchReceived() const { return num_type_touch_received_; }
int numTypePenReceived() const { return num_type_pen_received_; }
private:
int mouse_event_received_count_ = 0;
int touch_event_received_count_ = 0;
int pen_event_received_count_ = 0;
int num_events_received_ = 0;
int num_type_mouse_received_ = 0;
int num_type_touch_received_ = 0;
int num_type_pen_received_ = 0;
};
class PointerEventCoordinateListenerCallback final
......@@ -49,7 +55,7 @@ class PointerEventCoordinateListenerCallback final
}
void Invoke(ExecutionContext*, Event* event) override {
const PointerEvent* pointer_event = (PointerEvent*)event;
const PointerEvent* pointer_event = static_cast<PointerEvent*>(event);
last_client_x_ = pointer_event->clientX();
last_client_y_ = pointer_event->clientY();
last_page_x_ = pointer_event->pageX();
......@@ -84,17 +90,21 @@ class PointerEventManagerTest : public SimTest {
std::unique_ptr<WebPointerEvent> CreateTestPointerEvent(
WebInputEvent::Type type,
WebPointerProperties::PointerType pointer_type,
gfx::PointF position_in_widget = gfx::PointF(100, 100),
gfx::PointF position_in_screen = gfx::PointF(100, 100),
int movement_x = 0,
int movement_y = 0,
PointerId id = 1) {
return CreateTestPointerEvent(type, pointer_type, id, gfx::PointF(100, 100),
gfx::PointF(100, 100), 0, 0, 1, 1);
}
std::unique_ptr<WebPointerEvent> CreateTestPointerEvent(
WebInputEvent::Type type,
WebPointerProperties::PointerType pointer_type,
gfx::PointF position_in_widget,
gfx::PointF position_in_screen,
int movement_x,
int movement_y,
float width = 1,
float height = 1) {
return std::make_unique<WebPointerEvent>(
type,
WebPointerProperties(
1, pointer_type, WebPointerProperties::Button::kLeft,
position_in_widget, position_in_screen, movement_x, movement_y),
return CreateTestPointerEvent(type, pointer_type, 1, position_in_widget,
position_in_screen, movement_x, movement_y,
width, height);
}
WebMouseEvent CreateTestMouseEvent(WebInputEvent::Type type,
......@@ -106,6 +116,25 @@ class PointerEventManagerTest : public SimTest {
event.SetFrameScale(1);
return event;
}
private:
std::unique_ptr<WebPointerEvent> CreateTestPointerEvent(
WebInputEvent::Type type,
WebPointerProperties::PointerType pointer_type,
PointerId id,
gfx::PointF position_in_widget,
gfx::PointF position_in_screen,
int movement_x,
int movement_y,
float width,
float height) {
return std::make_unique<WebPointerEvent>(
type,
WebPointerProperties(
id, pointer_type, WebPointerProperties::Button::kLeft,
position_in_widget, position_in_screen, movement_x, movement_y),
width, height);
}
};
TEST_F(PointerEventManagerTest, HasPointerCapture) {
......@@ -175,34 +204,75 @@ TEST_F(PointerEventManagerTest, PointerCancelsOfAllTypes) {
GetEventHandler().HandleMousePressEvent(CreateTestMouseEvent(
WebInputEvent::Type::kMouseDown, gfx::PointF(100, 100)));
ASSERT_EQ(callback->mouseEventCount(), 0);
ASSERT_EQ(callback->touchEventCount(), 0);
ASSERT_EQ(callback->penEventCount(), 0);
ASSERT_EQ(callback->numTypeMouseReceived(), 0);
ASSERT_EQ(callback->numTypeTouchReceived(), 0);
ASSERT_EQ(callback->numTypePenReceived(), 0);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerCausedUaAction,
WebPointerProperties::PointerType::kPen),
{}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->mouseEventCount(), 0);
ASSERT_EQ(callback->touchEventCount(), 1);
ASSERT_EQ(callback->penEventCount(), 1);
ASSERT_EQ(callback->numTypeMouseReceived(), 0);
ASSERT_EQ(callback->numTypeTouchReceived(), 1);
ASSERT_EQ(callback->numTypePenReceived(), 1);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerCausedUaAction,
WebPointerProperties::PointerType::kTouch),
{}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->mouseEventCount(), 0);
ASSERT_EQ(callback->touchEventCount(), 1);
ASSERT_EQ(callback->penEventCount(), 1);
ASSERT_EQ(callback->numTypeMouseReceived(), 0);
ASSERT_EQ(callback->numTypeTouchReceived(), 1);
ASSERT_EQ(callback->numTypePenReceived(), 1);
GetEventHandler().HandleMouseMoveEvent(
CreateTestMouseEvent(WebInputEvent::Type::kMouseMove,
gfx::PointF(200, 200)),
Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
ASSERT_EQ(callback->mouseEventCount(), 1);
ASSERT_EQ(callback->touchEventCount(), 1);
ASSERT_EQ(callback->penEventCount(), 1);
ASSERT_EQ(callback->numTypeMouseReceived(), 1);
ASSERT_EQ(callback->numTypeTouchReceived(), 1);
ASSERT_EQ(callback->numTypePenReceived(), 1);
}
TEST_F(PointerEventManagerTest, PointerCancelForNonExistentid) {
WebView().MainFrameViewWidget()->Resize(gfx::Size(400, 400));
SimRequest request("https://example.com/test.html", "text/html");
LoadURL("https://example.com/test.html");
request.Complete(
"<body style='padding: 0px; width: 400px; height: 400px;'>"
"<div draggable='true' style='width: 150px; height: 150px;'></div>"
"</body>");
auto* callback = MakeGarbageCollected<CheckPointerEventListenerCallback>();
GetDocument().body()->addEventListener(event_type_names::kPointercancel,
callback);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerCancel,
WebPointerProperties::PointerType::kTouch, 100),
{}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->numEventsReceived(), 0);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerDown,
WebPointerProperties::PointerType::kTouch, 100),
{}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->numEventsReceived(), 0);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerCancel,
WebPointerProperties::PointerType::kTouch, 100),
{}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->numEventsReceived(), 1);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerCancel,
WebPointerProperties::PointerType::kTouch, 200),
{}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->numEventsReceived(), 1);
}
TEST_F(PointerEventManagerTest, PointerEventCoordinates) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment