1// Copyright 2013 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/browser/renderer_host/input/tap_suppression_controller.h"
6
7#include "base/debug/trace_event.h"
8#include "base/logging.h"
9#include "content/browser/renderer_host/input/tap_suppression_controller_client.h"
10
11namespace content {
12
13TapSuppressionController::Config::Config()
14    : enabled(false),
15      max_cancel_to_down_time(base::TimeDelta::FromMilliseconds(180)),
16      max_tap_gap_time(base::TimeDelta::FromMilliseconds(500)) {
17}
18
19TapSuppressionController::TapSuppressionController(
20    TapSuppressionControllerClient* client,
21    const Config& config)
22    : client_(client),
23      state_(config.enabled ? NOTHING : DISABLED),
24      max_cancel_to_down_time_(config.max_cancel_to_down_time),
25      max_tap_gap_time_(config.max_tap_gap_time) {
26}
27
28TapSuppressionController::~TapSuppressionController() {}
29
30void TapSuppressionController::GestureFlingCancel() {
31  switch (state_) {
32    case DISABLED:
33      break;
34    case NOTHING:
35    case GFC_IN_PROGRESS:
36    case LAST_CANCEL_STOPPED_FLING:
37      state_ = GFC_IN_PROGRESS;
38      break;
39    case TAP_DOWN_STASHED:
40      break;
41  }
42}
43
44void TapSuppressionController::GestureFlingCancelAck(bool processed) {
45  base::TimeTicks event_time = Now();
46  switch (state_) {
47    case DISABLED:
48    case NOTHING:
49      break;
50    case GFC_IN_PROGRESS:
51      if (processed)
52        fling_cancel_time_ = event_time;
53      state_ = LAST_CANCEL_STOPPED_FLING;
54      break;
55    case TAP_DOWN_STASHED:
56      if (!processed) {
57        TRACE_EVENT0("browser",
58                     "TapSuppressionController::GestureFlingCancelAck");
59        StopTapDownTimer();
60        client_->ForwardStashedTapDown();
61        state_ = NOTHING;
62      }  // Else waiting for the timer to release the stashed tap down.
63      break;
64    case LAST_CANCEL_STOPPED_FLING:
65      break;
66  }
67}
68
69bool TapSuppressionController::ShouldDeferTapDown() {
70  base::TimeTicks event_time = Now();
71  switch (state_) {
72    case DISABLED:
73    case NOTHING:
74      return false;
75    case GFC_IN_PROGRESS:
76      state_ = TAP_DOWN_STASHED;
77      StartTapDownTimer(max_tap_gap_time_);
78      return true;
79    case TAP_DOWN_STASHED:
80      NOTREACHED() << "TapDown on TAP_DOWN_STASHED state";
81      state_ = NOTHING;
82      return false;
83    case LAST_CANCEL_STOPPED_FLING:
84      if ((event_time - fling_cancel_time_) < max_cancel_to_down_time_) {
85        state_ = TAP_DOWN_STASHED;
86        StartTapDownTimer(max_tap_gap_time_);
87        return true;
88      } else {
89        state_ = NOTHING;
90        return false;
91      }
92  }
93  NOTREACHED() << "Invalid state";
94  return false;
95}
96
97bool TapSuppressionController::ShouldSuppressTapEnd() {
98  switch (state_) {
99    case DISABLED:
100    case NOTHING:
101    case GFC_IN_PROGRESS:
102      return false;
103    case TAP_DOWN_STASHED:
104      state_ = NOTHING;
105      StopTapDownTimer();
106      client_->DropStashedTapDown();
107      return true;
108    case LAST_CANCEL_STOPPED_FLING:
109      NOTREACHED() << "Invalid tap end on LAST_CANCEL_STOPPED_FLING state";
110  }
111  return false;
112}
113
114base::TimeTicks TapSuppressionController::Now() {
115  return base::TimeTicks::Now();
116}
117
118void TapSuppressionController::StartTapDownTimer(const base::TimeDelta& delay) {
119  tap_down_timer_.Start(FROM_HERE, delay, this,
120                        &TapSuppressionController::TapDownTimerExpired);
121}
122
123void TapSuppressionController::StopTapDownTimer() {
124  tap_down_timer_.Stop();
125}
126
127void TapSuppressionController::TapDownTimerExpired() {
128  switch (state_) {
129    case DISABLED:
130    case NOTHING:
131      NOTREACHED() << "Timer fired on invalid state.";
132      break;
133    case GFC_IN_PROGRESS:
134    case LAST_CANCEL_STOPPED_FLING:
135      NOTREACHED() << "Timer fired on invalid state.";
136      state_ = NOTHING;
137      break;
138    case TAP_DOWN_STASHED:
139      TRACE_EVENT0("browser",
140                   "TapSuppressionController::TapDownTimerExpired");
141      client_->ForwardStashedTapDown();
142      state_ = NOTHING;
143      break;
144  }
145}
146
147}  // namespace content
148