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  // Allow "hard" restarting of touch stream validation. This is necessary
42  // in cases where touch event forwarding ceases in response to the event ack
43  // or removal of touch handlers.
44  if (WebTouchEventTraits::IsTouchSequenceStart(event))
45    previous_event = WebTouchEvent();
46
47  // Unreleased points from the previous event should exist in the latest event.
48  for (unsigned i = 0; i < previous_event.touchesLength; ++i) {
49    const WebTouchPoint& previous_point = previous_event.touches[i];
50    if (previous_point.state == WebTouchPoint::StateCancelled ||
51        previous_point.state == WebTouchPoint::StateReleased)
52      continue;
53
54    const WebTouchPoint* point = FindTouchPoint(event, previous_point.id);
55    if (!point)
56      error_msg->append("Previously active touch point not in new event.\n");
57  }
58
59  for (unsigned i = 0; i < event.touchesLength; ++i) {
60    const WebTouchPoint& point = event.touches[i];
61    const WebTouchPoint* previous_point =
62        FindTouchPoint(previous_event, point.id);
63
64    // The point should exist in the previous event if it is not a new point.
65    if (!previous_point) {
66      if (point.state != WebTouchPoint::StatePressed)
67        error_msg->append("Active touch point not found in previous event.\n");
68    } else {
69      if (point.state == WebTouchPoint::StatePressed &&
70          previous_point->state != WebTouchPoint::StateCancelled &&
71          previous_point->state != WebTouchPoint::StateReleased) {
72        error_msg->append("Pressed touch point id already exists.\n");
73      }
74    }
75
76    switch (point.state) {
77      case WebTouchPoint::StateUndefined:
78        error_msg->append("Undefined WebTouchPoint state.\n");
79        break;
80
81      case WebTouchPoint::StateReleased:
82        if (event.type != WebInputEvent::TouchEnd)
83          error_msg->append("Released touch point outside touchend.\n");
84        break;
85
86      case WebTouchPoint::StatePressed:
87        if (event.type != WebInputEvent::TouchStart)
88          error_msg->append("Pressed touch point outside touchstart.\n");
89        break;
90
91      case WebTouchPoint::StateMoved:
92        if (event.type != WebInputEvent::TouchMove)
93          error_msg->append("Moved touch point outside touchmove.\n");
94        break;
95
96      case WebTouchPoint::StateStationary:
97        break;
98
99      case WebTouchPoint::StateCancelled:
100        if (event.type != WebInputEvent::TouchCancel)
101          error_msg->append("Cancelled touch point outside touchcancel.\n");
102        break;
103    }
104  }
105  return error_msg->empty();
106}
107
108}  // namespace content
109