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( ...@@ -222,6 +222,7 @@ PointerEventInit* PointerEventFactory::ConvertIdTypeButtonsEvent(
const WebPointerEvent& web_pointer_event) { const WebPointerEvent& web_pointer_event) {
WebPointerProperties::PointerType pointer_type = WebPointerProperties::PointerType pointer_type =
web_pointer_event.pointer_type; web_pointer_event.pointer_type;
unsigned buttons; unsigned buttons;
if (web_pointer_event.hovering) { if (web_pointer_event.hovering) {
buttons = MouseEvent::WebInputEventModifiersToButtons( buttons = MouseEvent::WebInputEventModifiersToButtons(
...@@ -248,12 +249,16 @@ PointerEventInit* PointerEventFactory::ConvertIdTypeButtonsEvent( ...@@ -248,12 +249,16 @@ PointerEventInit* PointerEventFactory::ConvertIdTypeButtonsEvent(
} }
pointer_type = WebPointerProperties::PointerType::kPen; 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); const IncomingId incoming_id(pointer_type, web_pointer_event.id);
PointerId pointer_id = AddIdAndActiveButtons(incoming_id, buttons != 0, 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->setPointerId(pointer_id);
pointer_event_init->setPointerType( pointer_event_init->setPointerType(
PointerTypeNameForWebPointPointerType(pointer_type)); PointerTypeNameForWebPointPointerType(pointer_type));
...@@ -293,6 +298,8 @@ PointerEvent* PointerEventFactory::Create( ...@@ -293,6 +298,8 @@ PointerEvent* PointerEventFactory::Create(
PointerEventInit* pointer_event_init = PointerEventInit* pointer_event_init =
ConvertIdTypeButtonsEvent(web_pointer_event); ConvertIdTypeButtonsEvent(web_pointer_event);
if (!pointer_event_init)
return nullptr;
AtomicString type = PointerEventNameForEventType(event_type); AtomicString type = PointerEventNameForEventType(event_type);
if (event_type == WebInputEvent::Type::kPointerDown || if (event_type == WebInputEvent::Type::kPointerDown ||
...@@ -491,7 +498,7 @@ PointerEventFactory::~PointerEventFactory() { ...@@ -491,7 +498,7 @@ PointerEventFactory::~PointerEventFactory() {
void PointerEventFactory::Clear() { void PointerEventFactory::Clear() {
for (int type = 0; for (int type = 0;
type <= ToInt(WebPointerProperties::PointerType::kMaxValue); type++) { type <= ToInt(WebPointerProperties::PointerType::kMaxValue); type++) {
primary_id_[type] = PointerEventFactory::kInvalidId; primary_id_[type] = kInvalidId;
id_count_[type] = 0; id_count_[type] = 0;
} }
pointer_incoming_id_mapping_.clear(); pointer_incoming_id_mapping_.clear();
...@@ -510,9 +517,11 @@ void PointerEventFactory::Clear() { ...@@ -510,9 +517,11 @@ void PointerEventFactory::Clear() {
current_id_ = PointerEventFactory::kMouseId + 1; current_id_ = PointerEventFactory::kMouseId + 1;
} }
PointerId PointerEventFactory::AddIdAndActiveButtons(const IncomingId p, PointerId PointerEventFactory::AddIdAndActiveButtons(
bool is_active_buttons, const IncomingId p,
bool hovering) { bool is_active_buttons,
bool hovering,
WebInputEvent::Type event_type) {
// Do not add extra mouse pointer as it was added in initialization. // Do not add extra mouse pointer as it was added in initialization.
if (p.GetPointerType() == WebPointerProperties::PointerType::kMouse) { if (p.GetPointerType() == WebPointerProperties::PointerType::kMouse) {
pointer_id_mapping_.Set(kMouseId, pointer_id_mapping_.Set(kMouseId,
...@@ -526,6 +535,12 @@ PointerId PointerEventFactory::AddIdAndActiveButtons(const IncomingId p, ...@@ -526,6 +535,12 @@ PointerId PointerEventFactory::AddIdAndActiveButtons(const IncomingId p,
PointerAttributes(p, is_active_buttons, hovering)); PointerAttributes(p, is_active_buttons, hovering));
return mapped_id; 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(); int type_int = p.PointerTypeInt();
// We do not handle the overflow of |current_id_| as it should be very rare. // We do not handle the overflow of |current_id_| as it should be very rare.
PointerId mapped_id = current_id_++; PointerId mapped_id = current_id_++;
...@@ -549,7 +564,7 @@ bool PointerEventFactory::Remove(const PointerId mapped_id) { ...@@ -549,7 +564,7 @@ bool PointerEventFactory::Remove(const PointerId mapped_id) {
pointer_incoming_id_mapping_.erase(p); pointer_incoming_id_mapping_.erase(p);
RemoveLastPosition(mapped_id); RemoveLastPosition(mapped_id);
if (primary_id_[type_int] == mapped_id) if (primary_id_[type_int] == mapped_id)
primary_id_[type_int] = PointerEventFactory::kInvalidId; primary_id_[type_int] = kInvalidId;
id_count_[type_int]--; id_count_[type_int]--;
return true; return true;
} }
...@@ -594,8 +609,7 @@ bool PointerEventFactory::IsPrimary( ...@@ -594,8 +609,7 @@ bool PointerEventFactory::IsPrimary(
return true; return true;
PointerId pointer_id = GetPointerEventId(properties); PointerId pointer_id = GetPointerEventId(properties);
return (pointer_id != PointerEventFactory::kInvalidId && return (pointer_id != kInvalidId && IsPrimary(pointer_id));
IsPrimary(pointer_id));
} }
bool PointerEventFactory::IsActiveButtonsState( bool PointerEventFactory::IsActiveButtonsState(
...@@ -618,7 +632,7 @@ PointerId PointerEventFactory::GetPointerEventId( ...@@ -618,7 +632,7 @@ PointerId PointerEventFactory::GetPointerEventId(
IncomingId id(properties.pointer_type, properties.id); IncomingId id(properties.pointer_type, properties.id);
if (pointer_incoming_id_mapping_.Contains(id)) if (pointer_incoming_id_mapping_.Contains(id))
return pointer_incoming_id_mapping_.at(id); return pointer_incoming_id_mapping_.at(id);
return PointerEventFactory::kInvalidId; return kInvalidId;
} }
} // namespace blink } // namespace blink
...@@ -30,6 +30,8 @@ class CORE_EXPORT PointerEventFactory { ...@@ -30,6 +30,8 @@ class CORE_EXPORT PointerEventFactory {
PointerEventFactory(); 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, PointerEvent* Create(const WebPointerEvent& web_pointer_event,
const Vector<WebPointerEvent>& coalesced_events, const Vector<WebPointerEvent>& coalesced_events,
const Vector<WebPointerEvent>& predicted_events, const Vector<WebPointerEvent>& predicted_events,
...@@ -128,9 +130,14 @@ class CORE_EXPORT PointerEventFactory { ...@@ -128,9 +130,14 @@ class CORE_EXPORT PointerEventFactory {
PointerId AddIdAndActiveButtons(const IncomingId, PointerId AddIdAndActiveButtons(const IncomingId,
bool is_active_buttons, bool is_active_buttons,
bool hovering); bool hovering,
WebInputEvent::Type event_type);
bool IsPrimary(const PointerId) const; 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&); PointerEventInit* ConvertIdTypeButtonsEvent(const WebPointerEvent&);
void SetEventSpecificFields(PointerEventInit*, const AtomicString& type); void SetEventSpecificFields(PointerEventInit*, const AtomicString& type);
// Creates pointerevents like boundary and capture events from another // Creates pointerevents like boundary and capture events from another
......
...@@ -463,8 +463,12 @@ WebInputEventResult PointerEventManager::DispatchTouchPointerEvent( ...@@ -463,8 +463,12 @@ WebInputEventResult PointerEventManager::DispatchTouchPointerEvent(
? pointer_event_target.target_element->GetDocument().domWindow() ? pointer_event_target.target_element->GetDocument().domWindow()
: nullptr); : nullptr);
result = SendTouchPointerEvent(pointer_event_target.target_element, if (pointer_event) {
pointer_event, web_pointer_event.hovering); 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 // If a pointerdown has been canceled, queue the unique id to allow
// suppressing mouse events from gesture events. For mouse events // suppressing mouse events from gesture events. For mouse events
......
...@@ -22,23 +22,29 @@ namespace { ...@@ -22,23 +22,29 @@ namespace {
class CheckPointerEventListenerCallback final : public NativeEventListener { class CheckPointerEventListenerCallback final : public NativeEventListener {
public: public:
void Invoke(ExecutionContext*, Event* event) override { 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") if (pointer_type == "mouse")
mouse_event_received_count_++; num_type_mouse_received_++;
else if (pointer_type == "touch") else if (pointer_type == "touch")
touch_event_received_count_++; num_type_touch_received_++;
else if (pointer_type == "pen") else if (pointer_type == "pen")
pen_event_received_count_++; num_type_pen_received_++;
} }
int mouseEventCount() const { return mouse_event_received_count_; } int numEventsReceived() const { return num_events_received_; }
int touchEventCount() const { return touch_event_received_count_; }
int penEventCount() const { return pen_event_received_count_; } 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: private:
int mouse_event_received_count_ = 0; int num_events_received_ = 0;
int touch_event_received_count_ = 0; int num_type_mouse_received_ = 0;
int pen_event_received_count_ = 0; int num_type_touch_received_ = 0;
int num_type_pen_received_ = 0;
}; };
class PointerEventCoordinateListenerCallback final class PointerEventCoordinateListenerCallback final
...@@ -49,7 +55,7 @@ class PointerEventCoordinateListenerCallback final ...@@ -49,7 +55,7 @@ class PointerEventCoordinateListenerCallback final
} }
void Invoke(ExecutionContext*, Event* event) override { 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_x_ = pointer_event->clientX();
last_client_y_ = pointer_event->clientY(); last_client_y_ = pointer_event->clientY();
last_page_x_ = pointer_event->pageX(); last_page_x_ = pointer_event->pageX();
...@@ -84,18 +90,22 @@ class PointerEventManagerTest : public SimTest { ...@@ -84,18 +90,22 @@ class PointerEventManagerTest : public SimTest {
std::unique_ptr<WebPointerEvent> CreateTestPointerEvent( std::unique_ptr<WebPointerEvent> CreateTestPointerEvent(
WebInputEvent::Type type, WebInputEvent::Type type,
WebPointerProperties::PointerType pointer_type, WebPointerProperties::PointerType pointer_type,
gfx::PointF position_in_widget = gfx::PointF(100, 100), PointerId id = 1) {
gfx::PointF position_in_screen = gfx::PointF(100, 100), return CreateTestPointerEvent(type, pointer_type, id, gfx::PointF(100, 100),
int movement_x = 0, gfx::PointF(100, 100), 0, 0, 1, 1);
int movement_y = 0, }
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 width = 1,
float height = 1) { float height = 1) {
return std::make_unique<WebPointerEvent>( return CreateTestPointerEvent(type, pointer_type, 1, position_in_widget,
type, position_in_screen, movement_x, movement_y,
WebPointerProperties( width, height);
1, pointer_type, WebPointerProperties::Button::kLeft,
position_in_widget, position_in_screen, movement_x, movement_y),
width, height);
} }
WebMouseEvent CreateTestMouseEvent(WebInputEvent::Type type, WebMouseEvent CreateTestMouseEvent(WebInputEvent::Type type,
const gfx::PointF& coordinates) { const gfx::PointF& coordinates) {
...@@ -106,6 +116,25 @@ class PointerEventManagerTest : public SimTest { ...@@ -106,6 +116,25 @@ class PointerEventManagerTest : public SimTest {
event.SetFrameScale(1); event.SetFrameScale(1);
return event; 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) { TEST_F(PointerEventManagerTest, HasPointerCapture) {
...@@ -175,34 +204,75 @@ TEST_F(PointerEventManagerTest, PointerCancelsOfAllTypes) { ...@@ -175,34 +204,75 @@ TEST_F(PointerEventManagerTest, PointerCancelsOfAllTypes) {
GetEventHandler().HandleMousePressEvent(CreateTestMouseEvent( GetEventHandler().HandleMousePressEvent(CreateTestMouseEvent(
WebInputEvent::Type::kMouseDown, gfx::PointF(100, 100))); WebInputEvent::Type::kMouseDown, gfx::PointF(100, 100)));
ASSERT_EQ(callback->mouseEventCount(), 0); ASSERT_EQ(callback->numTypeMouseReceived(), 0);
ASSERT_EQ(callback->touchEventCount(), 0); ASSERT_EQ(callback->numTypeTouchReceived(), 0);
ASSERT_EQ(callback->penEventCount(), 0); ASSERT_EQ(callback->numTypePenReceived(), 0);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent( WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerCausedUaAction, CreateTestPointerEvent(WebInputEvent::Type::kPointerCausedUaAction,
WebPointerProperties::PointerType::kPen), WebPointerProperties::PointerType::kPen),
{}, {}, ui::LatencyInfo())); {}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->mouseEventCount(), 0); ASSERT_EQ(callback->numTypeMouseReceived(), 0);
ASSERT_EQ(callback->touchEventCount(), 1); ASSERT_EQ(callback->numTypeTouchReceived(), 1);
ASSERT_EQ(callback->penEventCount(), 1); ASSERT_EQ(callback->numTypePenReceived(), 1);
WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent( WebView().MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(
CreateTestPointerEvent(WebInputEvent::Type::kPointerCausedUaAction, CreateTestPointerEvent(WebInputEvent::Type::kPointerCausedUaAction,
WebPointerProperties::PointerType::kTouch), WebPointerProperties::PointerType::kTouch),
{}, {}, ui::LatencyInfo())); {}, {}, ui::LatencyInfo()));
ASSERT_EQ(callback->mouseEventCount(), 0); ASSERT_EQ(callback->numTypeMouseReceived(), 0);
ASSERT_EQ(callback->touchEventCount(), 1); ASSERT_EQ(callback->numTypeTouchReceived(), 1);
ASSERT_EQ(callback->penEventCount(), 1); ASSERT_EQ(callback->numTypePenReceived(), 1);
GetEventHandler().HandleMouseMoveEvent( GetEventHandler().HandleMouseMoveEvent(
CreateTestMouseEvent(WebInputEvent::Type::kMouseMove, CreateTestMouseEvent(WebInputEvent::Type::kMouseMove,
gfx::PointF(200, 200)), gfx::PointF(200, 200)),
Vector<WebMouseEvent>(), Vector<WebMouseEvent>()); Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
ASSERT_EQ(callback->mouseEventCount(), 1); ASSERT_EQ(callback->numTypeMouseReceived(), 1);
ASSERT_EQ(callback->touchEventCount(), 1); ASSERT_EQ(callback->numTypeTouchReceived(), 1);
ASSERT_EQ(callback->penEventCount(), 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) { 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