1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/common/input/gesture_event_stream_validator.h"
6 
7 #include "base/logging.h"
8 #include "base/strings/stringprintf.h"
9 #include "third_party/blink/public/common/input/web_gesture_event.h"
10 #include "third_party/blink/public/common/input/web_input_event.h"
11 #include "ui/events/blink/web_input_event_traits.h"
12 
13 using blink::WebInputEvent;
14 
15 namespace content {
16 
GestureEventStreamValidator()17 GestureEventStreamValidator::GestureEventStreamValidator()
18     : scrolling_(false), pinching_(false), waiting_for_tap_end_(false) {
19 }
20 
~GestureEventStreamValidator()21 GestureEventStreamValidator::~GestureEventStreamValidator() {
22 }
23 
Validate(const blink::WebGestureEvent & event,std::string * error_msg)24 bool GestureEventStreamValidator::Validate(
25     const blink::WebGestureEvent& event,
26     std::string* error_msg) {
27   DCHECK(error_msg);
28   error_msg->clear();
29   if (!WebInputEvent::IsGestureEventType(event.GetType())) {
30     error_msg->append(base::StringPrintf(
31         "Invalid gesture type: %s", WebInputEvent::GetName(event.GetType())));
32   }
33   switch (event.GetType()) {
34     case WebInputEvent::kGestureScrollBegin:
35       if (scrolling_)
36         error_msg->append("Scroll begin during scroll\n");
37       if (pinching_)
38         error_msg->append("Scroll begin during pinch\n");
39       scrolling_ = true;
40       break;
41     case WebInputEvent::kGestureScrollUpdate:
42       if (!scrolling_)
43         error_msg->append("Scroll update outside of scroll\n");
44       break;
45     case WebInputEvent::kGestureFlingStart:
46       if (event.SourceDevice() == blink::WebGestureDevice::kTouchscreen &&
47           !event.data.fling_start.velocity_x &&
48           !event.data.fling_start.velocity_y) {
49         error_msg->append("Zero velocity touchscreen fling\n");
50       }
51       if (!scrolling_)
52         error_msg->append("Fling start outside of scroll\n");
53       if (pinching_)
54         error_msg->append("Flinging while pinching\n");
55       // Don't reset scrolling_ since the GSE sent by the fling_controller_ at
56       // the end of the fling resets it.
57       break;
58     case WebInputEvent::kGestureScrollEnd:
59       if (!scrolling_)
60         error_msg->append("Scroll end outside of scroll\n");
61       if (pinching_)
62         error_msg->append("Ending scroll while pinching\n");
63       scrolling_ = false;
64       break;
65     case WebInputEvent::kGesturePinchBegin:
66       if (pinching_)
67         error_msg->append("Pinch begin during pinch\n");
68       pinching_ = true;
69       break;
70     case WebInputEvent::kGesturePinchUpdate:
71       if (!pinching_)
72         error_msg->append("Pinch update outside of pinch\n");
73       break;
74     case WebInputEvent::kGesturePinchEnd:
75       if (!pinching_)
76         error_msg->append("Pinch end outside of pinch\n");
77       pinching_ = false;
78       break;
79     case WebInputEvent::kGestureTapDown:
80       if (waiting_for_tap_end_)
81         error_msg->append("Missing tap ending event before TapDown\n");
82       waiting_for_tap_end_ = true;
83       break;
84     case WebInputEvent::kGestureTapUnconfirmed:
85       if (!waiting_for_tap_end_)
86         error_msg->append("Missing TapDown event before TapUnconfirmed\n");
87       break;
88     case WebInputEvent::kGestureTapCancel:
89       if (!waiting_for_tap_end_)
90         error_msg->append("Missing TapDown event before TapCancel\n");
91       waiting_for_tap_end_ = false;
92       break;
93     case WebInputEvent::kGestureTap:
94       if (!waiting_for_tap_end_)
95         error_msg->append("Missing TapDown event before Tap\n");
96       waiting_for_tap_end_ = false;
97       break;
98     case WebInputEvent::kGestureDoubleTap:
99       // DoubleTap gestures may be synthetically inserted, and do not require a
100       // preceding TapDown.
101       waiting_for_tap_end_ = false;
102       break;
103     default:
104       break;
105   }
106   // TODO(wjmaclean): At some future point we may wish to consider adding a
107   // 'continuity check', requiring that all events between an initial tap-down
108   // and whatever terminates the sequence to have the same source device type,
109   // and that touchpad gestures are only found on ScrollEvents.
110   if (event.SourceDevice() == blink::WebGestureDevice::kUninitialized)
111     error_msg->append("Gesture event source is uninitialized.\n");
112 
113   return error_msg->empty();
114 }
115 
116 }  // namespace content
117