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/touch_event_stream_validator.h"
6
7#include "base/logging.h"
8#include "content/common/input/web_touch_event_traits.h"
9
10using blink::WebInputEvent;
11using blink::WebTouchEvent;
12using blink::WebTouchPoint;
13
14namespace content {
15namespace {
16
17const WebTouchPoint* FindTouchPoint(const WebTouchEvent& event, int id) {
18  for (unsigned i = 0; i < event.touchesLength; ++i) {
19    if (event.touches[i].id == id)
20      return &event.touches[i];
21  }
22  return NULL;
23}
24
25}  // namespace
26
27TouchEventStreamValidator::TouchEventStreamValidator() {
28}
29
30TouchEventStreamValidator::~TouchEventStreamValidator() {
31}
32
33bool TouchEventStreamValidator::Validate(const WebTouchEvent& event,
34                                         std::string* error_msg) {
35  DCHECK(error_msg);
36  error_msg->clear();
37
38  WebTouchEvent previous_event = previous_event_;
39  previous_event_ = event;
40
41  if (!event.touchesLength) {
42    error_msg->append("Touch event is empty.\n");
43    return false;
44  }
45
46  if (!WebInputEvent::isTouchEventType(event.type))
47    error_msg->append("Touch event has invalid type.\n");
48
49  // Allow "hard" restarting of touch stream validation. This is necessary
50  // in cases where touch event forwarding ceases in response to the event ack
51  // or removal of touch handlers.
52  if (WebTouchEventTraits::IsTouchSequenceStart(event))
53    previous_event = WebTouchEvent();
54
55  // Unreleased points from the previous event should exist in the latest event.
56  for (unsigned i = 0; i < previous_event.touchesLength; ++i) {
57    const WebTouchPoint& previous_point = previous_event.touches[i];
58    if (previous_point.state == WebTouchPoint::StateCancelled ||
59        previous_point.state == WebTouchPoint::StateReleased)
60      continue;
61
62    const WebTouchPoint* point = FindTouchPoint(event, previous_point.id);
63    if (!point)
64      error_msg->append("Previously active touch point not in new event.\n");
65  }
66
67  bool found_valid_state_for_type = false;
68  for (unsigned i = 0; i < event.touchesLength; ++i) {
69    const WebTouchPoint& point = event.touches[i];
70    const WebTouchPoint* previous_point =
71        FindTouchPoint(previous_event, point.id);
72
73    // The point should exist in the previous event if it is not a new point.
74    if (!previous_point) {
75      if (point.state != WebTouchPoint::StatePressed)
76        error_msg->append("Active touch point not found in previous event.\n");
77    } else {
78      if (point.state == WebTouchPoint::StatePressed &&
79          previous_point->state != WebTouchPoint::StateCancelled &&
80          previous_point->state != WebTouchPoint::StateReleased) {
81        error_msg->append("Pressed touch point id already exists.\n");
82      }
83    }
84
85    switch (point.state) {
86      case WebTouchPoint::StateUndefined:
87        error_msg->append("Undefined WebTouchPoint state.\n");
88        break;
89
90      case WebTouchPoint::StateReleased:
91        if (event.type != WebInputEvent::TouchEnd)
92          error_msg->append("Released touch point outside touchend.\n");
93        else
94          found_valid_state_for_type = true;
95        break;
96
97      case WebTouchPoint::StatePressed:
98        if (event.type != WebInputEvent::TouchStart)
99          error_msg->append("Pressed touch point outside touchstart.\n");
100        else
101          found_valid_state_for_type = true;
102        break;
103
104      case WebTouchPoint::StateMoved:
105        if (event.type != WebInputEvent::TouchMove)
106          error_msg->append("Moved touch point outside touchmove.\n");
107        else
108          found_valid_state_for_type = true;
109        break;
110
111      case WebTouchPoint::StateStationary:
112        break;
113
114      case WebTouchPoint::StateCancelled:
115        if (event.type != WebInputEvent::TouchCancel)
116          error_msg->append("Cancelled touch point outside touchcancel.\n");
117        else
118          found_valid_state_for_type = true;
119        break;
120    }
121  }
122
123  if (!found_valid_state_for_type)
124    error_msg->append("No valid touch point corresponding to event type.");
125
126  return error_msg->empty();
127}
128
129}  // namespace content
130