Commit 437c0bc7 authored by jdduke's avatar jdduke Committed by Commit bot

[Android] Enable input event stream validation

Enable the InputEventStreamValidator by default on Android. Also add
some basic unit tests, and clean up several corner cases that previously
went unnoticed.

BUG=345372

Review URL: https://codereview.chromium.org/537733003

Cr-Commit-Position: refs/heads/master@{#293437}
parent 7e6b4bb3
...@@ -58,6 +58,7 @@ void SetContentCommandLineFlags(bool single_process, ...@@ -58,6 +58,7 @@ void SetContentCommandLineFlags(bool single_process,
parsed_command_line->AppendSwitch(switches::kEnableOverlayFullscreenVideo); parsed_command_line->AppendSwitch(switches::kEnableOverlayFullscreenVideo);
parsed_command_line->AppendSwitch(switches::kEnableOverlayScrollbar); parsed_command_line->AppendSwitch(switches::kEnableOverlayScrollbar);
parsed_command_line->AppendSwitch(switches::kEnableOverscrollNotifications); parsed_command_line->AppendSwitch(switches::kEnableOverscrollNotifications);
parsed_command_line->AppendSwitch(switches::kValidateInputEventStream);
// Run the GPU service as a thread in the browser instead of as a // Run the GPU service as a thread in the browser instead of as a
// standalone process. // standalone process.
......
...@@ -24,8 +24,10 @@ bool GestureEventStreamValidator::Validate(const blink::WebGestureEvent& event, ...@@ -24,8 +24,10 @@ bool GestureEventStreamValidator::Validate(const blink::WebGestureEvent& event,
error_msg->clear(); error_msg->clear();
switch (event.type) { switch (event.type) {
case WebInputEvent::GestureScrollBegin: case WebInputEvent::GestureScrollBegin:
if (scrolling_ || pinching_) if (scrolling_)
error_msg->append("Scroll begin during scroll\n"); error_msg->append("Scroll begin during scroll\n");
if (pinching_)
error_msg->append("Scroll begin during pinch\n");
scrolling_ = true; scrolling_ = true;
break; break;
case WebInputEvent::GestureScrollUpdate: case WebInputEvent::GestureScrollUpdate:
...@@ -42,18 +44,16 @@ bool GestureEventStreamValidator::Validate(const blink::WebGestureEvent& event, ...@@ -42,18 +44,16 @@ bool GestureEventStreamValidator::Validate(const blink::WebGestureEvent& event,
scrolling_ = false; scrolling_ = false;
break; break;
case WebInputEvent::GesturePinchBegin: case WebInputEvent::GesturePinchBegin:
if (!scrolling_)
error_msg->append("Pinch begin outside of scroll\n");
if (pinching_) if (pinching_)
error_msg->append("Pinch begin during pinch\n"); error_msg->append("Pinch begin during pinch\n");
pinching_ = true; pinching_ = true;
break; break;
case WebInputEvent::GesturePinchUpdate: case WebInputEvent::GesturePinchUpdate:
if (!pinching_ || !scrolling_) if (!pinching_)
error_msg->append("Pinch update outside of pinch\n"); error_msg->append("Pinch update outside of pinch\n");
break; break;
case WebInputEvent::GesturePinchEnd: case WebInputEvent::GesturePinchEnd:
if (!pinching_ || !scrolling_) if (!pinching_)
error_msg->append("Pinch end outside of pinch\n"); error_msg->append("Pinch end outside of pinch\n");
pinching_ = false; pinching_ = false;
break; break;
...@@ -64,11 +64,13 @@ bool GestureEventStreamValidator::Validate(const blink::WebGestureEvent& event, ...@@ -64,11 +64,13 @@ bool GestureEventStreamValidator::Validate(const blink::WebGestureEvent& event,
break; break;
case WebInputEvent::GestureTap: case WebInputEvent::GestureTap:
case WebInputEvent::GestureTapCancel: case WebInputEvent::GestureTapCancel:
case WebInputEvent::GestureDoubleTap:
if (!waiting_for_tap_end_) if (!waiting_for_tap_end_)
error_msg->append("Missing GestureTapDown event\n"); error_msg->append("Missing GestureTapDown event\n");
waiting_for_tap_end_ = false; waiting_for_tap_end_ = false;
break; break;
case WebInputEvent::GestureDoubleTap:
waiting_for_tap_end_ = false;
break;
default: default:
break; break;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <string> #include <string>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "content/common/content_export.h"
namespace blink { namespace blink {
class WebGestureEvent; class WebGestureEvent;
...@@ -16,7 +17,7 @@ class WebGestureEvent; ...@@ -16,7 +17,7 @@ class WebGestureEvent;
namespace content { namespace content {
// Utility class for validating a stream of WebGestureEvents. // Utility class for validating a stream of WebGestureEvents.
class GestureEventStreamValidator { class CONTENT_EXPORT GestureEventStreamValidator {
public: public:
GestureEventStreamValidator(); GestureEventStreamValidator();
~GestureEventStreamValidator(); ~GestureEventStreamValidator();
......
// Copyright 2014 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/gesture_event_stream_validator.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
using blink::WebInputEvent;
using blink::WebGestureEvent;
namespace content {
namespace {
const blink::WebGestureDevice kDefaultGestureDevice =
blink::WebGestureDeviceTouchscreen;
blink::WebGestureEvent Build(WebInputEvent::Type type) {
return SyntheticWebGestureEventBuilder::Build(type, kDefaultGestureDevice);
}
} // namespace
TEST(GestureEventStreamValidator, ValidScroll) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
event = Build(WebInputEvent::GestureScrollBegin);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureScrollUpdate);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureScrollEnd);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
TEST(GestureEventStreamValidator, InvalidScroll) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
// No preceding ScrollBegin.
event = Build(WebInputEvent::GestureScrollUpdate);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
// No preceding ScrollBegin.
event = Build(WebInputEvent::GestureScrollEnd);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
event = Build(WebInputEvent::GestureScrollBegin);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
// Already scrolling.
event = Build(WebInputEvent::GestureScrollBegin);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
event = Build(WebInputEvent::GestureScrollEnd);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
// Scroll already ended.
event = Build(WebInputEvent::GestureScrollEnd);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
TEST(GestureEventStreamValidator, ValidFling) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
event = Build(WebInputEvent::GestureScrollBegin);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureFlingStart);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
TEST(GestureEventStreamValidator, InvalidFling) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
// No preceding ScrollBegin.
event = Build(WebInputEvent::GestureFlingStart);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
TEST(GestureEventStreamValidator, ValidPinch) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
event = Build(WebInputEvent::GesturePinchBegin);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GesturePinchUpdate);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GesturePinchEnd);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
TEST(GestureEventStreamValidator, InvalidPinch) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
// No preceding PinchBegin.
event = Build(WebInputEvent::GesturePinchUpdate);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
event = Build(WebInputEvent::GesturePinchBegin);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
// ScrollBegin while pinching.
event = Build(WebInputEvent::GestureScrollBegin);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
// ScrollEnd while pinching.
event = Build(WebInputEvent::GestureScrollEnd);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
// Pinch already begun.
event = Build(WebInputEvent::GesturePinchBegin);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
event = Build(WebInputEvent::GesturePinchEnd);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
// Pinch already ended.
event = Build(WebInputEvent::GesturePinchEnd);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
TEST(GestureEventStreamValidator, ValidTap) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
event = Build(WebInputEvent::GestureTapDown);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureTap);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureTapDown);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureTapCancel);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
// DoubleTap doesn't require a TapDown (unlike Tap and TapCancel).
event = Build(WebInputEvent::GestureDoubleTap);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
TEST(GestureEventStreamValidator, InvalidTap) {
GestureEventStreamValidator validator;
std::string error_msg;
WebGestureEvent event;
// No preceding TapDown.
event = Build(WebInputEvent::GestureTap);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
event = Build(WebInputEvent::GestureTapCancel);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
// TapDown already terminated.
event = Build(WebInputEvent::GestureTapDown);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureDoubleTap);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event = Build(WebInputEvent::GestureTapCancel);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
} // namespace content
...@@ -38,6 +38,14 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event, ...@@ -38,6 +38,14 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event,
WebTouchEvent previous_event = previous_event_; WebTouchEvent previous_event = previous_event_;
previous_event_ = event; previous_event_ = event;
if (!event.touchesLength) {
error_msg->append("Touch event is empty.\n");
return false;
}
if (!WebInputEvent::isTouchEventType(event.type))
error_msg->append("Touch event has invalid type.\n");
// Allow "hard" restarting of touch stream validation. This is necessary // Allow "hard" restarting of touch stream validation. This is necessary
// in cases where touch event forwarding ceases in response to the event ack // in cases where touch event forwarding ceases in response to the event ack
// or removal of touch handlers. // or removal of touch handlers.
...@@ -56,6 +64,7 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event, ...@@ -56,6 +64,7 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event,
error_msg->append("Previously active touch point not in new event.\n"); error_msg->append("Previously active touch point not in new event.\n");
} }
bool found_valid_state_for_type = false;
for (unsigned i = 0; i < event.touchesLength; ++i) { for (unsigned i = 0; i < event.touchesLength; ++i) {
const WebTouchPoint& point = event.touches[i]; const WebTouchPoint& point = event.touches[i];
const WebTouchPoint* previous_point = const WebTouchPoint* previous_point =
...@@ -81,16 +90,22 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event, ...@@ -81,16 +90,22 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event,
case WebTouchPoint::StateReleased: case WebTouchPoint::StateReleased:
if (event.type != WebInputEvent::TouchEnd) if (event.type != WebInputEvent::TouchEnd)
error_msg->append("Released touch point outside touchend.\n"); error_msg->append("Released touch point outside touchend.\n");
else
found_valid_state_for_type = true;
break; break;
case WebTouchPoint::StatePressed: case WebTouchPoint::StatePressed:
if (event.type != WebInputEvent::TouchStart) if (event.type != WebInputEvent::TouchStart)
error_msg->append("Pressed touch point outside touchstart.\n"); error_msg->append("Pressed touch point outside touchstart.\n");
else
found_valid_state_for_type = true;
break; break;
case WebTouchPoint::StateMoved: case WebTouchPoint::StateMoved:
if (event.type != WebInputEvent::TouchMove) if (event.type != WebInputEvent::TouchMove)
error_msg->append("Moved touch point outside touchmove.\n"); error_msg->append("Moved touch point outside touchmove.\n");
else
found_valid_state_for_type = true;
break; break;
case WebTouchPoint::StateStationary: case WebTouchPoint::StateStationary:
...@@ -99,9 +114,15 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event, ...@@ -99,9 +114,15 @@ bool TouchEventStreamValidator::Validate(const WebTouchEvent& event,
case WebTouchPoint::StateCancelled: case WebTouchPoint::StateCancelled:
if (event.type != WebInputEvent::TouchCancel) if (event.type != WebInputEvent::TouchCancel)
error_msg->append("Cancelled touch point outside touchcancel.\n"); error_msg->append("Cancelled touch point outside touchcancel.\n");
else
found_valid_state_for_type = true;
break; break;
} }
} }
if (!found_valid_state_for_type)
error_msg->append("No valid touch point corresponding to event type.");
return error_msg->empty(); return error_msg->empty();
} }
......
...@@ -8,12 +8,13 @@ ...@@ -8,12 +8,13 @@
#include <string> #include <string>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "content/common/content_export.h"
#include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebInputEvent.h"
namespace content { namespace content {
// Utility class for validating a stream of WebTouchEvents. // Utility class for validating a stream of WebTouchEvents.
class TouchEventStreamValidator { class CONTENT_EXPORT TouchEventStreamValidator {
public: public:
TouchEventStreamValidator(); TouchEventStreamValidator();
~TouchEventStreamValidator(); ~TouchEventStreamValidator();
......
// Copyright 2014 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/touch_event_stream_validator.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/common/input/web_touch_event_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
using blink::WebInputEvent;
using blink::WebTouchEvent;
using blink::WebTouchPoint;
namespace content {
TEST(TouchEventStreamValidator, ValidTouchStream) {
TouchEventStreamValidator validator;
SyntheticWebTouchEvent event;
std::string error_msg;
event.PressPoint(0, 1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.PressPoint(1, 0);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.MovePoint(1, 1, 1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.ReleasePoint(1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.MovePoint(0, -1, 0);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.CancelPoint(0);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.PressPoint(-1, -1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
TEST(TouchEventStreamValidator, ResetOnNewTouchStream) {
TouchEventStreamValidator validator;
SyntheticWebTouchEvent event;
std::string error_msg;
event.PressPoint(0, 1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.CancelPoint(0);
event.ResetPoints();
event.PressPoint(1, 0);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
TEST(TouchEventStreamValidator, MissedTouchStart) {
TouchEventStreamValidator validator;
SyntheticWebTouchEvent event;
std::string error_msg;
event.PressPoint(0, 1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.PressPoint(1, 0);
event.ResetPoints();
event.PressPoint(1, 1);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
TEST(TouchEventStreamValidator, MissedTouchEnd) {
TouchEventStreamValidator validator;
SyntheticWebTouchEvent event;
std::string error_msg;
event.PressPoint(0, 1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.PressPoint(0, 1);
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
event.ResetPoints();
event.ReleasePoint(1);
event.ResetPoints();
event.PressPoint(1, 1);
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
TEST(TouchEventStreamValidator, EmptyEvent) {
TouchEventStreamValidator validator;
WebTouchEvent event;
std::string error_msg;
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
TEST(TouchEventStreamValidator, InvalidEventType) {
TouchEventStreamValidator validator;
WebTouchEvent event;
std::string error_msg;
event.type = WebInputEvent::GestureScrollBegin;
event.touchesLength = 1;
event.touches[0].state = WebTouchPoint::StatePressed;
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
TEST(TouchEventStreamValidator, InvalidPointStates) {
TouchEventStreamValidator validator;
std::string error_msg;
WebInputEvent::Type kTouchTypes[4] = {
WebInputEvent::TouchStart, WebInputEvent::TouchMove,
WebInputEvent::TouchEnd, WebInputEvent::TouchCancel,
};
WebTouchPoint::State kValidTouchPointStatesForType[4] = {
WebTouchPoint::StatePressed, WebTouchPoint::StateMoved,
WebTouchPoint::StateReleased, WebTouchPoint::StateCancelled,
};
SyntheticWebTouchEvent start;
start.PressPoint(0, 0);
for (size_t i = 0; i < 4; ++i) {
// Always start with a touchstart to reset the stream validation.
EXPECT_TRUE(validator.Validate(start, &error_msg));
EXPECT_TRUE(error_msg.empty());
WebTouchEvent event;
event.touchesLength = 1;
event.type = kTouchTypes[i];
for (size_t j = WebTouchPoint::StateUndefined;
j <= WebTouchPoint::StateCancelled;
++j) {
event.touches[0].state = static_cast<WebTouchPoint::State>(j);
if (event.touches[0].state == kValidTouchPointStatesForType[i]) {
EXPECT_TRUE(validator.Validate(event, &error_msg));
EXPECT_TRUE(error_msg.empty());
} else {
EXPECT_FALSE(validator.Validate(event, &error_msg));
EXPECT_FALSE(error_msg.empty());
}
}
}
}
} // namespace content
...@@ -666,7 +666,9 @@ ...@@ -666,7 +666,9 @@
'common/gpu/gpu_memory_manager_unittest.cc', 'common/gpu/gpu_memory_manager_unittest.cc',
'common/host_shared_bitmap_manager_unittest.cc', 'common/host_shared_bitmap_manager_unittest.cc',
'common/indexed_db/indexed_db_key_unittest.cc', 'common/indexed_db/indexed_db_key_unittest.cc',
'common/input/gesture_event_stream_validator_unittest.cc',
'common/input/input_param_traits_unittest.cc', 'common/input/input_param_traits_unittest.cc',
'common/input/touch_event_stream_validator_unittest.cc',
'common/input/web_input_event_traits_unittest.cc', 'common/input/web_input_event_traits_unittest.cc',
'common/inter_process_time_ticks_converter_unittest.cc', 'common/inter_process_time_ticks_converter_unittest.cc',
'common/mac/attributed_string_coder_unittest.mm', 'common/mac/attributed_string_coder_unittest.mm',
......
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