1// Copyright (c) 2012 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 "ui/events/gestures/gesture_sequence.h"
6
7#include <stdlib.h>
8#include <cmath>
9
10#include "base/command_line.h"
11#include "base/logging.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/time/time.h"
15#include "ui/events/event.h"
16#include "ui/events/event_constants.h"
17#include "ui/events/event_switches.h"
18#include "ui/events/gestures/gesture_configuration.h"
19#include "ui/events/gestures/gesture_util.h"
20#include "ui/gfx/rect.h"
21
22namespace ui {
23
24namespace {
25
26// ui::EventType is mapped to TouchState so it can fit into 3 bits of
27// Signature.
28enum TouchState {
29  TS_RELEASED,
30  TS_PRESSED,
31  TS_MOVED,
32  TS_STATIONARY,
33  TS_CANCELLED,
34  TS_UNKNOWN,
35};
36
37// ui::EventResult is mapped to TouchStatusInternal to simply indicate whether a
38// processed touch-event should affect gesture-recognition or not.
39enum TouchStatusInternal {
40  TSI_NOT_PROCESSED,  // The touch-event should take-part into
41                      // gesture-recognition only if the touch-event has not
42                      // been processed.
43
44  TSI_PROCESSED,      // The touch-event should affect gesture-recognition only
45                      // if the touch-event has been processed. For example,,
46                      // this means that a JavaScript touch handler called
47                      // |preventDefault| on the associated touch event
48                      // or was processed by an aura-window or views-view.
49
50  TSI_ALWAYS          // The touch-event should always affect gesture
51                      // recognition.
52};
53
54// Get equivalent TouchState from EventType |type|.
55TouchState TouchEventTypeToTouchState(ui::EventType type) {
56  switch (type) {
57    case ui::ET_TOUCH_RELEASED:
58      return TS_RELEASED;
59    case ui::ET_TOUCH_PRESSED:
60      return TS_PRESSED;
61    case ui::ET_TOUCH_MOVED:
62      return TS_MOVED;
63    case ui::ET_TOUCH_STATIONARY:
64      return TS_STATIONARY;
65    case ui::ET_TOUCH_CANCELLED:
66      return TS_CANCELLED;
67    default:
68      DVLOG(1) << "Unknown Touch Event type";
69  }
70  return TS_UNKNOWN;
71}
72
73// Gesture signature types for different values of combination (GestureState,
74// touch_id, ui::EventType, touch_handled), see Signature for more info.
75//
76// Note: New addition of types should be placed as per their Signature value.
77#define G(gesture_state, id, touch_state, handled) 1 + ( \
78  (((touch_state) & 0x7) << 1) |                         \
79  ((handled & 0x3) << 4) |                               \
80  (((id) & 0xfff) << 6) |                                \
81  ((gesture_state) << 18))
82
83enum EdgeStateSignatureType {
84  GST_INVALID = -1,
85
86  GST_NO_GESTURE_FIRST_PRESSED =
87      G(GS_NO_GESTURE, 0, TS_PRESSED, TSI_NOT_PROCESSED),
88
89  GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED =
90      G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_NOT_PROCESSED),
91
92  GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED =
93      G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_PROCESSED),
94
95  // Ignore processed touch-move events until gesture-scroll starts.
96  GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED =
97      G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_NOT_PROCESSED),
98
99  GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED =
100      G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_PROCESSED),
101
102  GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY =
103      G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_STATIONARY, TSI_NOT_PROCESSED),
104
105  GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED =
106      G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_CANCELLED, TSI_ALWAYS),
107
108  GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED =
109      G(GS_PENDING_SYNTHETIC_CLICK, 1, TS_PRESSED, TSI_NOT_PROCESSED),
110
111  GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED =
112      G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL,
113        0,
114        TS_RELEASED,
115        TSI_NOT_PROCESSED),
116
117  GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED =
118      G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_RELEASED, TSI_PROCESSED),
119
120  GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED =
121      G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_MOVED, TSI_ALWAYS),
122
123  GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY =
124      G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_STATIONARY, TSI_ALWAYS),
125
126  GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED =
127      G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
128
129  GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED =
130      G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
131
132  GST_SCROLL_FIRST_RELEASED =
133      G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS),
134
135  // Once scroll has started, process all touch-move events.
136  GST_SCROLL_FIRST_MOVED =
137      G(GS_SCROLL, 0, TS_MOVED, TSI_ALWAYS),
138
139  GST_SCROLL_FIRST_CANCELLED =
140      G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
141
142  GST_SCROLL_SECOND_PRESSED =
143      G(GS_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
144
145  GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED =
146      G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_NOT_PROCESSED),
147
148  GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED =
149      G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_PROCESSED),
150
151  GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED =
152      G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_NOT_PROCESSED),
153
154  GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED =
155      G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_PROCESSED),
156
157  GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED =
158      G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_NOT_PROCESSED),
159
160  GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED =
161      G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_NOT_PROCESSED),
162
163  GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED =
164      G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_PROCESSED),
165
166  GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED =
167      G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_PROCESSED),
168
169  GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED =
170      G(GS_PENDING_TWO_FINGER_TAP, 0, TS_CANCELLED, TSI_ALWAYS),
171
172  GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED =
173      G(GS_PENDING_TWO_FINGER_TAP, 1, TS_CANCELLED, TSI_ALWAYS),
174
175  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED =
176      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_NOT_PROCESSED),
177
178  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED =
179      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_PROCESSED),
180
181  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED =
182      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_NOT_PROCESSED),
183
184  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED =
185      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_PROCESSED),
186
187  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED =
188      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS),
189
190  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED =
191      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS),
192
193  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED =
194      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
195
196  GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED =
197      G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
198
199  GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED =
200      G(GS_PENDING_TWO_FINGER_TAP, 2, TS_PRESSED, TSI_NOT_PROCESSED),
201
202  GST_PENDING_PINCH_FIRST_MOVED =
203      G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED),
204
205  GST_PENDING_PINCH_SECOND_MOVED =
206      G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED),
207
208  GST_PENDING_PINCH_FIRST_MOVED_HANDLED =
209      G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_PROCESSED),
210
211  GST_PENDING_PINCH_SECOND_MOVED_HANDLED =
212      G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_PROCESSED),
213
214  GST_PENDING_PINCH_FIRST_CANCELLED =
215      G(GS_PENDING_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
216
217  GST_PENDING_PINCH_SECOND_CANCELLED =
218      G(GS_PENDING_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
219
220  GST_PENDING_PINCH_FIRST_RELEASED =
221      G(GS_PENDING_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
222
223  GST_PENDING_PINCH_SECOND_RELEASED =
224      G(GS_PENDING_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
225
226  GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED =
227      G(GS_PENDING_PINCH_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS),
228
229  GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED =
230      G(GS_PENDING_PINCH_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS),
231
232  GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED =
233      G(GS_PENDING_PINCH_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
234
235  GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED =
236      G(GS_PENDING_PINCH_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
237
238  GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED =
239      G(GS_PENDING_PINCH_NO_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
240
241  GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED =
242      G(GS_PENDING_PINCH_NO_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
243
244  GST_PINCH_FIRST_MOVED =
245      G(GS_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED),
246
247  GST_PINCH_FIRST_MOVED_HANDLED =
248      G(GS_PINCH, 0, TS_MOVED, TSI_PROCESSED),
249
250  GST_PINCH_SECOND_MOVED =
251      G(GS_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED),
252
253  GST_PINCH_SECOND_MOVED_HANDLED =
254      G(GS_PINCH, 1, TS_MOVED, TSI_PROCESSED),
255
256  GST_PINCH_FIRST_RELEASED =
257      G(GS_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
258
259  GST_PINCH_SECOND_RELEASED =
260      G(GS_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
261
262  GST_PINCH_FIRST_CANCELLED =
263      G(GS_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
264
265  GST_PINCH_SECOND_CANCELLED =
266      G(GS_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
267
268  GST_PINCH_THIRD_PRESSED =
269      G(GS_PINCH, 2, TS_PRESSED, TSI_NOT_PROCESSED),
270
271  GST_PINCH_THIRD_MOVED =
272      G(GS_PINCH, 2, TS_MOVED, TSI_NOT_PROCESSED),
273
274  GST_PINCH_THIRD_MOVED_HANDLED =
275      G(GS_PINCH, 2, TS_MOVED, TSI_PROCESSED),
276
277  GST_PINCH_THIRD_RELEASED =
278      G(GS_PINCH, 2, TS_RELEASED, TSI_ALWAYS),
279
280  GST_PINCH_THIRD_CANCELLED =
281      G(GS_PINCH, 2, TS_CANCELLED, TSI_ALWAYS),
282
283  GST_PINCH_FOURTH_PRESSED =
284      G(GS_PINCH, 3, TS_PRESSED, TSI_NOT_PROCESSED),
285
286  GST_PINCH_FOURTH_MOVED =
287      G(GS_PINCH, 3, TS_MOVED, TSI_NOT_PROCESSED),
288
289  GST_PINCH_FOURTH_MOVED_HANDLED =
290      G(GS_PINCH, 3, TS_MOVED, TSI_PROCESSED),
291
292  GST_PINCH_FOURTH_RELEASED =
293      G(GS_PINCH, 3, TS_RELEASED, TSI_ALWAYS),
294
295  GST_PINCH_FOURTH_CANCELLED =
296      G(GS_PINCH, 3, TS_CANCELLED, TSI_ALWAYS),
297
298  GST_PINCH_FIFTH_PRESSED =
299      G(GS_PINCH, 4, TS_PRESSED, TSI_NOT_PROCESSED),
300
301  GST_PINCH_FIFTH_MOVED =
302      G(GS_PINCH, 4, TS_MOVED, TSI_NOT_PROCESSED),
303
304  GST_PINCH_FIFTH_MOVED_HANDLED =
305      G(GS_PINCH, 4, TS_MOVED, TSI_PROCESSED),
306
307  GST_PINCH_FIFTH_RELEASED =
308      G(GS_PINCH, 4, TS_RELEASED, TSI_ALWAYS),
309
310  GST_PINCH_FIFTH_CANCELLED =
311      G(GS_PINCH, 4, TS_CANCELLED, TSI_ALWAYS),
312};
313
314// Builds a signature. Signatures are assembled by joining together
315// multiple bits.
316// 1 LSB bit so that the computed signature is always greater than 0
317// 3 bits for the |type|.
318// 2 bit for |touch_status|
319// 12 bits for |touch_id|
320// 14 bits for the |gesture_state|.
321EdgeStateSignatureType Signature(GestureState gesture_state,
322                                 unsigned int touch_id,
323                                 ui::EventType type,
324                                 TouchStatusInternal touch_status) {
325  CHECK((touch_id & 0xfff) == touch_id);
326  TouchState touch_state = TouchEventTypeToTouchState(type);
327  EdgeStateSignatureType signature = static_cast<EdgeStateSignatureType>
328      (G(gesture_state, touch_id, touch_state, touch_status));
329
330  switch (signature) {
331    case GST_NO_GESTURE_FIRST_PRESSED:
332    case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
333    case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
334    case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
335    case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
336    case GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY:
337    case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
338    case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
339    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
340    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
341    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED:
342    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY:
343    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
344    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
345    case GST_SCROLL_FIRST_RELEASED:
346    case GST_SCROLL_FIRST_MOVED:
347    case GST_SCROLL_FIRST_CANCELLED:
348    case GST_SCROLL_SECOND_PRESSED:
349    case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
350    case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED:
351    case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED:
352    case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED:
353    case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED:
354    case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED:
355    case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED:
356    case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED:
357    case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED:
358    case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED:
359    case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED:
360    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED:
361    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED:
362    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED:
363    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED:
364    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED:
365    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED:
366    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED:
367    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED:
368    case GST_PENDING_PINCH_FIRST_MOVED:
369    case GST_PENDING_PINCH_SECOND_MOVED:
370    case GST_PENDING_PINCH_FIRST_MOVED_HANDLED:
371    case GST_PENDING_PINCH_SECOND_MOVED_HANDLED:
372    case GST_PENDING_PINCH_FIRST_RELEASED:
373    case GST_PENDING_PINCH_SECOND_RELEASED:
374    case GST_PENDING_PINCH_FIRST_CANCELLED:
375    case GST_PENDING_PINCH_SECOND_CANCELLED:
376    case GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED:
377    case GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED:
378    case GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED:
379    case GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED:
380    case GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED:
381    case GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED:
382    case GST_PINCH_FIRST_MOVED:
383    case GST_PINCH_FIRST_MOVED_HANDLED:
384    case GST_PINCH_SECOND_MOVED:
385    case GST_PINCH_SECOND_MOVED_HANDLED:
386    case GST_PINCH_FIRST_RELEASED:
387    case GST_PINCH_SECOND_RELEASED:
388    case GST_PINCH_FIRST_CANCELLED:
389    case GST_PINCH_SECOND_CANCELLED:
390    case GST_PINCH_THIRD_PRESSED:
391    case GST_PINCH_THIRD_MOVED:
392    case GST_PINCH_THIRD_MOVED_HANDLED:
393    case GST_PINCH_THIRD_RELEASED:
394    case GST_PINCH_THIRD_CANCELLED:
395    case GST_PINCH_FOURTH_PRESSED:
396    case GST_PINCH_FOURTH_MOVED:
397    case GST_PINCH_FOURTH_MOVED_HANDLED:
398    case GST_PINCH_FOURTH_RELEASED:
399    case GST_PINCH_FOURTH_CANCELLED:
400    case GST_PINCH_FIFTH_PRESSED:
401    case GST_PINCH_FIFTH_MOVED:
402    case GST_PINCH_FIFTH_MOVED_HANDLED:
403    case GST_PINCH_FIFTH_RELEASED:
404    case GST_PINCH_FIFTH_CANCELLED:
405      break;
406    default:
407      signature = GST_INVALID;
408      break;
409  }
410
411  return signature;
412}
413#undef G
414
415float BoundingBoxDiagonal(const gfx::Rect& rect) {
416  float width = rect.width() * rect.width();
417  float height = rect.height() * rect.height();
418  return sqrt(width + height);
419}
420
421unsigned int ComputeTouchBitmask(const GesturePoint* points) {
422  unsigned int touch_bitmask = 0;
423  for (int i = 0; i < GestureSequence::kMaxGesturePoints; ++i) {
424    if (points[i].in_use())
425      touch_bitmask |= 1 << points[i].touch_id();
426  }
427  return touch_bitmask;
428}
429
430const float kFlingCurveNormalization = 1.0f / 1875.f;
431
432float CalibrateFlingVelocity(float velocity) {
433  const unsigned last_coefficient =
434      GestureConfiguration::NumAccelParams - 1;
435  float normalized_velocity = fabs(velocity * kFlingCurveNormalization);
436  float nu = 0.0f, x = 1.f;
437
438  for (int i = last_coefficient ; i >= 0; i--) {
439    float a = GestureConfiguration::fling_acceleration_curve_coefficients(i);
440    nu += x * a;
441    x *= normalized_velocity;
442  }
443  if (velocity < 0.f)
444    return std::max(nu * velocity, -GestureConfiguration::fling_velocity_cap());
445  else
446    return std::min(nu * velocity, GestureConfiguration::fling_velocity_cap());
447}
448
449
450void UpdateGestureEventLatencyInfo(const TouchEvent& event,
451                                   GestureSequence::Gestures* gestures) {
452  // If the touch event does not cause any rendering scheduled, we first
453  // end the touch event's LatencyInfo. Then we copy the touch event's
454  // LatencyInfo into the generated gesture's LatencyInfo. Since one touch
455  // event can generate multiple gesture events, we have to clear the gesture
456  // event's trace_id, remove its ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT,
457  // so when the gesture event passes through RWHI, a new trace_id will be
458  // assigned and new ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT will be added.
459  if (!event.latency()->FindLatency(
460          ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL)) {
461    ui::LatencyInfo* touch_latency =
462        const_cast<ui::LatencyInfo*>(event.latency());
463    touch_latency->AddLatencyNumber(
464        ui::INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, 0, 0);
465    GestureSequence::Gestures::iterator it = gestures->begin();
466    for (; it != gestures->end(); it++) {
467      ui::LatencyInfo* gesture_latency = (*it)->latency();
468      *gesture_latency = *touch_latency;
469      gesture_latency->trace_id = -1;
470      gesture_latency->terminated = false;
471      gesture_latency->RemoveLatency(
472          ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
473    }
474  }
475}
476
477}  // namespace
478
479////////////////////////////////////////////////////////////////////////////////
480// GestureSequence Public:
481
482GestureSequence::GestureSequence(GestureSequenceDelegate* delegate)
483    : state_(GS_NO_GESTURE),
484      flags_(0),
485      pinch_distance_start_(0.f),
486      pinch_distance_current_(0.f),
487      scroll_type_(ST_FREE),
488      point_count_(0),
489      delegate_(delegate) {
490  CHECK(delegate_);
491}
492
493GestureSequence::~GestureSequence() {
494}
495
496GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
497    const TouchEvent& event,
498    EventResult result) {
499  StopTimersIfRequired(event);
500  last_touch_location_ = event.location();
501  if (result & ER_CONSUMED)
502    return NULL;
503
504  // Set a limit on the number of simultaneous touches in a gesture.
505  if (event.touch_id() >= kMaxGesturePoints)
506    return NULL;
507
508  if (event.type() == ui::ET_TOUCH_PRESSED) {
509    if (point_count_ == kMaxGesturePoints)
510      return NULL;
511    GesturePoint* new_point = &points_[event.touch_id()];
512    // We shouldn't be able to get two PRESSED events from the same
513    // finger without either a RELEASE or CANCEL in between. But let's not crash
514    // in a release build.
515    if (new_point->in_use()) {
516      LOG(ERROR) << "Received a second press for a point: " << event.touch_id();
517      new_point->ResetVelocity();
518      new_point->UpdateValues(event);
519      return NULL;
520    }
521    new_point->set_point_id(point_count_++);
522    new_point->set_touch_id(event.touch_id());
523  }
524
525  GestureState last_state = state_;
526
527  // NOTE: when modifying these state transitions, also update gestures.dot
528  scoped_ptr<Gestures> gestures(new Gestures());
529  GesturePoint& point = GesturePointForEvent(event);
530  point.UpdateValues(event);
531  RecreateBoundingBox();
532  flags_ = event.flags();
533  const int point_id = point.point_id();
534  if (point_id < 0)
535    return NULL;
536
537  // Send GESTURE_BEGIN for any touch pressed.
538  if (event.type() == ui::ET_TOUCH_PRESSED)
539    AppendBeginGestureEvent(point, gestures.get());
540
541  TouchStatusInternal status_internal = (result == ER_UNHANDLED) ?
542      TSI_NOT_PROCESSED : TSI_PROCESSED;
543
544  EdgeStateSignatureType signature = Signature(state_, point_id,
545      event.type(), status_internal);
546
547  if (signature == GST_INVALID)
548    signature = Signature(state_, point_id, event.type(), TSI_ALWAYS);
549
550  switch (signature) {
551    case GST_INVALID:
552      break;
553
554    case GST_NO_GESTURE_FIRST_PRESSED:
555      TouchDown(event, point, gestures.get());
556      set_state(GS_PENDING_SYNTHETIC_CLICK);
557      break;
558    case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
559      if (Click(event, point, gestures.get()))
560        point.UpdateForTap();
561      else
562        PrependTapCancelGestureEvent(point, gestures.get());
563      set_state(GS_NO_GESTURE);
564      break;
565    case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
566    case GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY:
567      if (ScrollStart(event, point, gestures.get())) {
568        PrependTapCancelGestureEvent(point, gestures.get());
569        set_state(GS_SCROLL);
570        if (ScrollUpdate(event, point, gestures.get()))
571          point.UpdateForScroll();
572      }
573      break;
574    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED:
575    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY:
576      // No scrolling allowed, so nothing happens.
577      break;
578    case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
579      if (point.IsInScrollWindow(event)) {
580        PrependTapCancelGestureEvent(point, gestures.get());
581        set_state(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL);
582      }
583      break;
584    case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
585    case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
586      PrependTapCancelGestureEvent(point, gestures.get());
587      set_state(GS_NO_GESTURE);
588      break;
589    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
590    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
591    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
592      set_state(GS_NO_GESTURE);
593      break;
594    case GST_SCROLL_FIRST_MOVED:
595      if (scroll_type_ == ST_VERTICAL ||
596          scroll_type_ == ST_HORIZONTAL)
597        BreakRailScroll(event, point, gestures.get());
598      if (ScrollUpdate(event, point, gestures.get()))
599        point.UpdateForScroll();
600      break;
601    case GST_SCROLL_FIRST_RELEASED:
602    case GST_SCROLL_FIRST_CANCELLED:
603      ScrollEnd(event, point, gestures.get());
604      set_state(GS_NO_GESTURE);
605      break;
606    case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
607      PrependTapCancelGestureEvent(point, gestures.get());
608      TwoFingerTapOrPinch(event, point, gestures.get());
609      break;
610    case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
611      TwoFingerTapOrPinch(event, point, gestures.get());
612      break;
613    case GST_SCROLL_SECOND_PRESSED:
614      PinchStart(event, point, gestures.get());
615      set_state(GS_PINCH);
616      break;
617    case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
618    case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED:
619      TwoFingerTouchReleased(event, point, gestures.get());
620      set_state(GS_SCROLL);
621      break;
622    case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED:
623    case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED:
624      if (TwoFingerTouchMove(event, point, gestures.get()))
625        set_state(GS_PINCH);
626      break;
627    case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED:
628    case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED:
629      set_state(GS_PENDING_TWO_FINGER_TAP_NO_PINCH);
630      break;
631    case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED:
632    case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED:
633    case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED:
634    case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED:
635      scroll_type_ = ST_FREE;
636      set_state(GS_SCROLL);
637      break;
638    case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED:
639      set_state(GS_PENDING_PINCH);
640      break;
641    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED:
642    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED:
643      // No pinch allowed, so nothing happens.
644      break;
645    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED:
646    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED:
647      TwoFingerTouchReleased(event, point, gestures.get());
648      // We transit into GS_SCROLL even though the touch move can be
649      // consumed and no scroll should happen. crbug.com/240399.
650      set_state(GS_SCROLL);
651      break;
652    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED:
653    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED:
654    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED:
655    case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED:
656      // We transit into GS_SCROLL even though the touch move can be
657      // consumed and no scroll should happen. crbug.com/240399.
658      scroll_type_ = ST_FREE;
659      set_state(GS_SCROLL);
660      break;
661    case GST_PENDING_PINCH_FIRST_MOVED:
662    case GST_PENDING_PINCH_SECOND_MOVED:
663      if (TwoFingerTouchMove(event, point, gestures.get()))
664        set_state(GS_PINCH);
665      break;
666    case GST_PENDING_PINCH_FIRST_MOVED_HANDLED:
667    case GST_PENDING_PINCH_SECOND_MOVED_HANDLED:
668      set_state(GS_PENDING_PINCH_NO_PINCH);
669      break;
670    case GST_PENDING_PINCH_FIRST_RELEASED:
671    case GST_PENDING_PINCH_SECOND_RELEASED:
672    case GST_PENDING_PINCH_FIRST_CANCELLED:
673    case GST_PENDING_PINCH_SECOND_CANCELLED:
674      // We transit into GS_SCROLL even though the touch move can be
675      // consumed and no scroll should happen. crbug.com/240399.
676      scroll_type_ = ST_FREE;
677      set_state(GS_SCROLL);
678      break;
679    case GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED:
680    case GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED:
681      // No pinch allowed, so nothing happens.
682      break;
683    case GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED:
684    case GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED:
685    case GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED:
686    case GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED:
687      // We transit into GS_SCROLL even though the touch move can be
688      // consumed and no scroll should happen. crbug.com/240399.
689      scroll_type_ = ST_FREE;
690      set_state(GS_SCROLL);
691      break;
692    case GST_PINCH_FIRST_MOVED_HANDLED:
693    case GST_PINCH_SECOND_MOVED_HANDLED:
694    case GST_PINCH_THIRD_MOVED_HANDLED:
695    case GST_PINCH_FOURTH_MOVED_HANDLED:
696    case GST_PINCH_FIFTH_MOVED_HANDLED:
697      break;
698    case GST_PINCH_FIRST_MOVED:
699    case GST_PINCH_SECOND_MOVED:
700    case GST_PINCH_THIRD_MOVED:
701    case GST_PINCH_FOURTH_MOVED:
702    case GST_PINCH_FIFTH_MOVED:
703      if (PinchUpdate(event, point, gestures.get())) {
704        for (int i = 0; i < point_count_; ++i)
705          GetPointByPointId(i)->UpdateForScroll();
706      }
707      break;
708    case GST_PINCH_FIRST_RELEASED:
709    case GST_PINCH_SECOND_RELEASED:
710    case GST_PINCH_THIRD_RELEASED:
711    case GST_PINCH_FOURTH_RELEASED:
712    case GST_PINCH_FIFTH_RELEASED:
713    case GST_PINCH_FIRST_CANCELLED:
714    case GST_PINCH_SECOND_CANCELLED:
715    case GST_PINCH_THIRD_CANCELLED:
716    case GST_PINCH_FOURTH_CANCELLED:
717    case GST_PINCH_FIFTH_CANCELLED:
718      // Was it a swipe? i.e. were all the fingers moving in the same
719      // direction?
720      MaybeSwipe(event, point, gestures.get());
721
722      if (point_count_ == 2) {
723        PinchEnd(event, point, gestures.get());
724
725        // Once pinch ends, it should still be possible to scroll with the
726        // remaining finger on the screen.
727        set_state(GS_SCROLL);
728      } else {
729        // Nothing else to do if we have more than 2 fingers active, since after
730        // the release/cancel, there are still enough fingers to do pinch.
731        // pinch_distance_current_ and pinch_distance_start_ will be updated
732        // when the bounding-box is updated.
733      }
734      ResetVelocities();
735      break;
736    case GST_PINCH_THIRD_PRESSED:
737    case GST_PINCH_FOURTH_PRESSED:
738    case GST_PINCH_FIFTH_PRESSED:
739      pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
740      pinch_distance_start_ = pinch_distance_current_;
741      break;
742  }
743
744  if (event.type() == ui::ET_TOUCH_RELEASED ||
745      event.type() == ui::ET_TOUCH_CANCELLED)
746    AppendEndGestureEvent(point, gestures.get());
747
748  if (state_ != last_state)
749    DVLOG(4) << "Gesture Sequence"
750             << " State: " << state_
751             << " touch id: " << event.touch_id();
752
753  if (last_state == GS_PENDING_SYNTHETIC_CLICK && state_ != last_state) {
754    GetLongPressTimer()->Stop();
755    GetShowPressTimer()->Stop();
756  }
757
758  // The set of point_ids must be contiguous and include 0.
759  // When a touch point is released, all points with ids greater than the
760  // released point must have their ids decremented, or the set of point_ids
761  // could end up with gaps.
762  if (event.type() == ui::ET_TOUCH_RELEASED ||
763      event.type() == ui::ET_TOUCH_CANCELLED) {
764    for (int i = 0; i < kMaxGesturePoints; ++i) {
765      GesturePoint& iter_point = points_[i];
766      if (iter_point.point_id() > point.point_id())
767        iter_point.set_point_id(iter_point.point_id() - 1);
768    }
769
770    point.Reset();
771    --point_count_;
772    CHECK_GE(point_count_, 0);
773    RecreateBoundingBox();
774    if (state_ == GS_PINCH) {
775      pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
776      pinch_distance_start_ = pinch_distance_current_;
777    }
778  }
779
780  UpdateGestureEventLatencyInfo(event, gestures.get());
781  return gestures.release();
782}
783
784void GestureSequence::RecreateBoundingBox() {
785  // TODO(sad): Recreating the bounding box at every touch-event is not very
786  // efficient. This should be made better.
787  if (point_count_ == 0) {
788    bounding_box_.SetRect(0, 0, 0, 0);
789  } else if (point_count_ == 1) {
790    bounding_box_ = GetPointByPointId(0)->enclosing_rectangle();
791  } else {
792    int left = INT_MAX / 20, top = INT_MAX / 20;
793    int right = INT_MIN / 20, bottom = INT_MIN / 20;
794    for (int i = 0; i < kMaxGesturePoints; ++i) {
795      if (!points_[i].in_use())
796        continue;
797      // Using the |enclosing_rectangle()| for the touch-points would be ideal.
798      // However, this becomes brittle especially when a finger is in motion
799      // because the change in radius can overshadow the actual change in
800      // position. So the actual position of the point is used instead.
801      const gfx::Point& point = points_[i].last_touch_position();
802      left = std::min(left, point.x());
803      right = std::max(right, point.x());
804      top = std::min(top, point.y());
805      bottom = std::max(bottom, point.y());
806    }
807    bounding_box_.SetRect(left, top, right - left, bottom - top);
808  }
809}
810
811void GestureSequence::ResetVelocities() {
812  for (int i = 0; i < kMaxGesturePoints; ++i) {
813    if (points_[i].in_use())
814      points_[i].ResetVelocity();
815  }
816}
817
818////////////////////////////////////////////////////////////////////////////////
819// GestureSequence Protected:
820
821base::OneShotTimer<GestureSequence>* GestureSequence::CreateTimer() {
822  return new base::OneShotTimer<GestureSequence>();
823}
824
825base::OneShotTimer<GestureSequence>* GestureSequence::GetLongPressTimer() {
826  if (!long_press_timer_.get())
827    long_press_timer_.reset(CreateTimer());
828  return long_press_timer_.get();
829}
830
831base::OneShotTimer<GestureSequence>* GestureSequence::GetShowPressTimer() {
832  if (!show_press_timer_.get())
833    show_press_timer_.reset(CreateTimer());
834  return show_press_timer_.get();
835}
836
837////////////////////////////////////////////////////////////////////////////////
838// GestureSequence Private:
839
840GesturePoint& GestureSequence::GesturePointForEvent(
841    const TouchEvent& event) {
842  return points_[event.touch_id()];
843}
844
845GesturePoint* GestureSequence::GetPointByPointId(int point_id) {
846  DCHECK(0 <= point_id && point_id < kMaxGesturePoints);
847  for (int i = 0; i < kMaxGesturePoints; ++i) {
848    GesturePoint& point = points_[i];
849    if (point.in_use() && point.point_id() == point_id)
850      return &point;
851  }
852  NOTREACHED();
853  return NULL;
854}
855
856bool GestureSequence::IsSecondTouchDownCloseEnoughForTwoFingerTap() {
857  gfx::Point p1 = GetPointByPointId(0)->last_touch_position();
858  gfx::Point p2 = GetPointByPointId(1)->last_touch_position();
859  double max_distance =
860      GestureConfiguration::max_distance_for_two_finger_tap_in_pixels();
861  double distance = (p1.x() - p2.x()) * (p1.x() - p2.x()) +
862      (p1.y() - p2.y()) * (p1.y() - p2.y());
863  if (distance < max_distance * max_distance)
864    return true;
865  return false;
866}
867
868GestureEvent* GestureSequence::CreateGestureEvent(
869    const GestureEventDetails& details,
870    const gfx::Point& location,
871    int flags,
872    base::Time timestamp,
873    unsigned int touch_id_bitmask) {
874  GestureEventDetails gesture_details(details);
875  gesture_details.set_touch_points(point_count_);
876  gesture_details.set_bounding_box(bounding_box_);
877  base::TimeDelta time_stamp =
878      base::TimeDelta::FromMicroseconds(timestamp.ToDoubleT() * 1000000);
879  return new GestureEvent(gesture_details.type(), location.x(), location.y(),
880                          flags, time_stamp, gesture_details,
881                          touch_id_bitmask);
882}
883
884void GestureSequence::AppendTapDownGestureEvent(const GesturePoint& point,
885                                                Gestures* gestures) {
886  gestures->push_back(CreateGestureEvent(
887      GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0),
888      point.first_touch_position(),
889      flags_,
890      base::Time::FromDoubleT(point.last_touch_time()),
891      1 << point.touch_id()));
892}
893
894void GestureSequence::PrependTapCancelGestureEvent(const GesturePoint& point,
895                                            Gestures* gestures) {
896  gestures->insert(gestures->begin(), CreateGestureEvent(
897    GestureEventDetails(ui::ET_GESTURE_TAP_CANCEL, 0, 0),
898    point.first_touch_position(),
899    flags_,
900    base::Time::FromDoubleT(point.last_touch_time()),
901    1 << point.touch_id()));
902}
903
904void GestureSequence::AppendBeginGestureEvent(const GesturePoint& point,
905                                              Gestures* gestures) {
906  gestures->push_back(CreateGestureEvent(
907      GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0),
908      point.first_touch_position(),
909      flags_,
910      base::Time::FromDoubleT(point.last_touch_time()),
911      1 << point.touch_id()));
912}
913
914void GestureSequence::AppendEndGestureEvent(const GesturePoint& point,
915                                              Gestures* gestures) {
916  gestures->push_back(CreateGestureEvent(
917      GestureEventDetails(ui::ET_GESTURE_END, 0, 0),
918      point.first_touch_position(),
919      flags_,
920      base::Time::FromDoubleT(point.last_touch_time()),
921      1 << point.touch_id()));
922}
923
924void GestureSequence::AppendClickGestureEvent(const GesturePoint& point,
925                                              int tap_count,
926                                              Gestures* gestures) {
927  gfx::Rect er = point.enclosing_rectangle();
928  gfx::Point center = er.CenterPoint();
929  gestures->push_back(CreateGestureEvent(
930      GestureEventDetails(ui::ET_GESTURE_TAP, tap_count, 0),
931      center,
932      flags_,
933      base::Time::FromDoubleT(point.last_touch_time()),
934      1 << point.touch_id()));
935}
936
937void GestureSequence::AppendScrollGestureBegin(const GesturePoint& point,
938                                               const gfx::Point& location,
939                                               Gestures* gestures) {
940  gestures->push_back(CreateGestureEvent(
941      GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
942      location,
943      flags_,
944      base::Time::FromDoubleT(point.last_touch_time()),
945      1 << point.touch_id()));
946}
947
948void GestureSequence::AppendScrollGestureEnd(const GesturePoint& point,
949                                             const gfx::Point& location,
950                                             Gestures* gestures,
951                                             float x_velocity,
952                                             float y_velocity) {
953  float railed_x_velocity = x_velocity;
954  float railed_y_velocity = y_velocity;
955  last_scroll_prediction_offset_.set_x(0);
956  last_scroll_prediction_offset_.set_y(0);
957
958  if (scroll_type_ == ST_HORIZONTAL)
959    railed_y_velocity = 0;
960  else if (scroll_type_ == ST_VERTICAL)
961    railed_x_velocity = 0;
962
963  if (railed_x_velocity != 0 || railed_y_velocity != 0) {
964
965    gestures->push_back(CreateGestureEvent(
966        GestureEventDetails(ui::ET_SCROLL_FLING_START,
967            CalibrateFlingVelocity(railed_x_velocity),
968            CalibrateFlingVelocity(railed_y_velocity),
969            CalibrateFlingVelocity(x_velocity),
970            CalibrateFlingVelocity(y_velocity)),
971        location,
972        flags_,
973        base::Time::FromDoubleT(point.last_touch_time()),
974        1 << point.touch_id()));
975  } else {
976    gestures->push_back(CreateGestureEvent(
977        GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0),
978        location,
979        flags_,
980        base::Time::FromDoubleT(point.last_touch_time()),
981        1 << point.touch_id()));
982  }
983}
984
985void GestureSequence::AppendScrollGestureUpdate(GesturePoint& point,
986                                                Gestures* gestures) {
987  static bool use_scroll_prediction = CommandLine::ForCurrentProcess()->
988      HasSwitch(switches::kEnableScrollPrediction);
989  gfx::Vector2dF d;
990  gfx::Point location;
991  if (point_count_ == 1) {
992    d = point.ScrollDelta();
993    location = point.last_touch_position();
994  } else {
995    location = bounding_box_.CenterPoint();
996    d = location - latest_multi_scroll_update_location_;
997    latest_multi_scroll_update_location_ = location;
998  }
999
1000  if (use_scroll_prediction) {
1001    // Remove the extra distance added by the last scroll prediction and add
1002    // the new prediction offset.
1003    d -= last_scroll_prediction_offset_;
1004    last_scroll_prediction_offset_.set_x(
1005        GestureConfiguration::scroll_prediction_seconds() * point.XVelocity());
1006    last_scroll_prediction_offset_.set_y(
1007        GestureConfiguration::scroll_prediction_seconds() * point.YVelocity());
1008    d += last_scroll_prediction_offset_;
1009    location += gfx::Vector2d(last_scroll_prediction_offset_.x(),
1010                              last_scroll_prediction_offset_.y());
1011  }
1012
1013  gfx::Vector2dF o = d;
1014
1015  if (scroll_type_ == ST_HORIZONTAL)
1016    d.set_y(0);
1017  else if (scroll_type_ == ST_VERTICAL)
1018    d.set_x(0);
1019  if (d.IsZero())
1020    return;
1021
1022  GestureEventDetails details(ui::ET_GESTURE_SCROLL_UPDATE,
1023                              d.x(), d.y(), o.x(), o.y());
1024  details.SetScrollVelocity(
1025      scroll_type_ == ST_VERTICAL ? 0 : point.XVelocity(),
1026      scroll_type_ == ST_HORIZONTAL ? 0 : point.YVelocity(),
1027      point.XVelocity(),
1028      point.YVelocity());
1029  gestures->push_back(CreateGestureEvent(
1030      details,
1031      location,
1032      flags_,
1033      base::Time::FromDoubleT(point.last_touch_time()),
1034      ComputeTouchBitmask(points_)));
1035}
1036
1037void GestureSequence::AppendPinchGestureBegin(const GesturePoint& p1,
1038                                              const GesturePoint& p2,
1039                                              Gestures* gestures) {
1040  gfx::Point center = bounding_box_.CenterPoint();
1041  gestures->push_back(CreateGestureEvent(
1042      GestureEventDetails(ui::ET_GESTURE_PINCH_BEGIN, 0, 0),
1043      center,
1044      flags_,
1045      base::Time::FromDoubleT(p1.last_touch_time()),
1046      1 << p1.touch_id() | 1 << p2.touch_id()));
1047}
1048
1049void GestureSequence::AppendPinchGestureEnd(const GesturePoint& p1,
1050                                            const GesturePoint& p2,
1051                                            float scale,
1052                                            Gestures* gestures) {
1053  gfx::Point center = bounding_box_.CenterPoint();
1054  gestures->push_back(CreateGestureEvent(
1055      GestureEventDetails(ui::ET_GESTURE_PINCH_END, 0, 0),
1056      center,
1057      flags_,
1058      base::Time::FromDoubleT(p1.last_touch_time()),
1059      1 << p1.touch_id() | 1 << p2.touch_id()));
1060}
1061
1062void GestureSequence::AppendPinchGestureUpdate(const GesturePoint& point,
1063                                               float scale,
1064                                               Gestures* gestures) {
1065  // TODO(sad): Compute rotation and include it in delta_y.
1066  // http://crbug.com/113145
1067  gestures->push_back(CreateGestureEvent(
1068      GestureEventDetails(ui::ET_GESTURE_PINCH_UPDATE, scale, 0),
1069      bounding_box_.CenterPoint(),
1070      flags_,
1071      base::Time::FromDoubleT(point.last_touch_time()),
1072      ComputeTouchBitmask(points_)));
1073}
1074
1075void GestureSequence::AppendSwipeGesture(const GesturePoint& point,
1076                                         int swipe_x,
1077                                         int swipe_y,
1078                                         Gestures* gestures) {
1079  gestures->push_back(CreateGestureEvent(
1080      GestureEventDetails(ui::ET_GESTURE_MULTIFINGER_SWIPE, swipe_x, swipe_y),
1081      bounding_box_.CenterPoint(),
1082      flags_,
1083      base::Time::FromDoubleT(point.last_touch_time()),
1084      ComputeTouchBitmask(points_)));
1085}
1086
1087void GestureSequence::AppendTwoFingerTapGestureEvent(Gestures* gestures) {
1088  const GesturePoint* point = GetPointByPointId(0);
1089  const gfx::Rect rect = point->enclosing_rectangle();
1090  gestures->push_back(CreateGestureEvent(
1091      GestureEventDetails(ui::ET_GESTURE_TWO_FINGER_TAP,
1092                          rect.width(),
1093                          rect.height()),
1094      point->enclosing_rectangle().CenterPoint(),
1095      flags_,
1096      base::Time::FromDoubleT(point->last_touch_time()),
1097      1 << point->touch_id()));
1098}
1099
1100bool GestureSequence::Click(const TouchEvent& event,
1101                            const GesturePoint& point,
1102                            Gestures* gestures) {
1103  DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
1104  if (point.IsInClickWindow(event)) {
1105    int tap_count = 1;
1106    if (point.IsInTripleClickWindow(event))
1107      tap_count = 3;
1108    else if (point.IsInDoubleClickWindow(event))
1109      tap_count = 2;
1110    if (tap_count == 1 && GetShowPressTimer()->IsRunning()) {
1111      GetShowPressTimer()->Stop();
1112      AppendShowPressGestureEvent();
1113    }
1114    AppendClickGestureEvent(point, tap_count, gestures);
1115    return true;
1116  } else if (point.IsInsideManhattanSquare(event) &&
1117      !GetLongPressTimer()->IsRunning()) {
1118    AppendLongTapGestureEvent(point, gestures);
1119  }
1120  return false;
1121}
1122
1123bool GestureSequence::ScrollStart(const TouchEvent& event,
1124                                  GesturePoint& point,
1125                                  Gestures* gestures) {
1126  DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
1127  if (!point.IsInScrollWindow(event))
1128    return false;
1129  AppendScrollGestureBegin(point, point.first_touch_position(), gestures);
1130  if (point.IsInHorizontalRailWindow())
1131    scroll_type_ = ST_HORIZONTAL;
1132  else if (point.IsInVerticalRailWindow())
1133    scroll_type_ = ST_VERTICAL;
1134  else
1135    scroll_type_ = ST_FREE;
1136  return true;
1137}
1138
1139void GestureSequence::BreakRailScroll(const TouchEvent& event,
1140                                      GesturePoint& point,
1141                                      Gestures* gestures) {
1142  DCHECK(state_ == GS_SCROLL);
1143  if (scroll_type_ == ST_HORIZONTAL &&
1144      point.BreaksHorizontalRail())
1145    scroll_type_ = ST_FREE;
1146  else if (scroll_type_ == ST_VERTICAL &&
1147           point.BreaksVerticalRail())
1148    scroll_type_ = ST_FREE;
1149}
1150
1151bool GestureSequence::ScrollUpdate(const TouchEvent& event,
1152                                   GesturePoint& point,
1153                                   Gestures* gestures) {
1154  DCHECK(state_ == GS_SCROLL);
1155  if (!point.DidScroll(event, 0))
1156    return false;
1157  AppendScrollGestureUpdate(point, gestures);
1158  return true;
1159}
1160
1161bool GestureSequence::TouchDown(const TouchEvent& event,
1162                                const GesturePoint& point,
1163                                Gestures* gestures) {
1164  DCHECK(state_ == GS_NO_GESTURE);
1165  AppendTapDownGestureEvent(point, gestures);
1166  GetLongPressTimer()->Start(
1167      FROM_HERE,
1168      base::TimeDelta::FromMilliseconds(
1169          GestureConfiguration::long_press_time_in_seconds() * 1000),
1170      this,
1171      &GestureSequence::AppendLongPressGestureEvent);
1172
1173  GetShowPressTimer()->Start(
1174      FROM_HERE,
1175      base::TimeDelta::FromMilliseconds(
1176          GestureConfiguration::show_press_delay_in_ms()),
1177      this,
1178      &GestureSequence::AppendShowPressGestureEvent);
1179
1180  return true;
1181}
1182
1183bool GestureSequence::TwoFingerTouchDown(const TouchEvent& event,
1184                                         const GesturePoint& point,
1185                                         Gestures* gestures) {
1186  DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK ||
1187         state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL ||
1188         state_ == GS_SCROLL);
1189
1190  if (state_ == GS_SCROLL) {
1191    AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
1192        0.f, 0.f);
1193  }
1194  second_touch_time_ = event.time_stamp();
1195  return true;
1196}
1197
1198bool GestureSequence::TwoFingerTouchMove(const TouchEvent& event,
1199                                         const GesturePoint& point,
1200                                         Gestures* gestures) {
1201  DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP ||
1202         state_ == GS_PENDING_PINCH);
1203
1204  base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
1205  base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
1206      ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
1207  if (time_delta > max_delta || !point.IsInsideManhattanSquare(event)) {
1208    PinchStart(event, point, gestures);
1209    return true;
1210  }
1211  return false;
1212}
1213
1214bool GestureSequence::TwoFingerTouchReleased(const TouchEvent& event,
1215                                             const GesturePoint& point,
1216                                             Gestures* gestures) {
1217  DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP ||
1218         state_ == GS_PENDING_TWO_FINGER_TAP_NO_PINCH);
1219  base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
1220  base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
1221      ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
1222  if (time_delta < max_delta && point.IsInsideManhattanSquare(event))
1223    AppendTwoFingerTapGestureEvent(gestures);
1224  return true;
1225}
1226
1227void GestureSequence::AppendLongPressGestureEvent() {
1228  const GesturePoint* point = GetPointByPointId(0);
1229  scoped_ptr<GestureEvent> gesture(CreateGestureEvent(
1230      GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0),
1231      point->first_touch_position(),
1232      flags_,
1233      base::Time::FromDoubleT(point->last_touch_time()),
1234      1 << point->touch_id()));
1235  delegate_->DispatchPostponedGestureEvent(gesture.get());
1236}
1237
1238void GestureSequence::AppendShowPressGestureEvent() {
1239  const GesturePoint* point = GetPointByPointId(0);
1240  scoped_ptr<GestureEvent> gesture(CreateGestureEvent(
1241      GestureEventDetails(ui::ET_GESTURE_SHOW_PRESS, 0, 0),
1242      point->first_touch_position(),
1243      flags_,
1244      base::Time::FromDoubleT(point->last_touch_time()),
1245      1 << point->touch_id()));
1246  delegate_->DispatchPostponedGestureEvent(gesture.get());
1247}
1248
1249void GestureSequence::AppendLongTapGestureEvent(const GesturePoint& point,
1250                                                Gestures* gestures) {
1251  gfx::Rect er = point.enclosing_rectangle();
1252  gfx::Point center = er.CenterPoint();
1253  gestures->push_back(CreateGestureEvent(
1254      GestureEventDetails(ui::ET_GESTURE_LONG_TAP, 0, 0),
1255      center,
1256      flags_,
1257      base::Time::FromDoubleT(point.last_touch_time()),
1258      1 << point.touch_id()));
1259}
1260
1261bool GestureSequence::ScrollEnd(const TouchEvent& event,
1262                                GesturePoint& point,
1263                                Gestures* gestures) {
1264  DCHECK(state_ == GS_SCROLL);
1265  if (point.IsInFlickWindow(event)) {
1266    AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
1267        point.XVelocity(), point.YVelocity());
1268  } else {
1269    AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
1270        0.f, 0.f);
1271  }
1272  return true;
1273}
1274
1275bool GestureSequence::PinchStart(const TouchEvent& event,
1276                                 const GesturePoint& point,
1277                                 Gestures* gestures) {
1278  DCHECK(state_ == GS_SCROLL ||
1279         state_ == GS_PENDING_TWO_FINGER_TAP ||
1280         state_ == GS_PENDING_PINCH);
1281
1282  // Once pinch starts, we immediately break rail scroll.
1283  scroll_type_ = ST_FREE;
1284
1285  const GesturePoint* point1 = GetPointByPointId(0);
1286  const GesturePoint* point2 = GetPointByPointId(1);
1287
1288  pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
1289  pinch_distance_start_ = pinch_distance_current_;
1290  latest_multi_scroll_update_location_ = bounding_box_.CenterPoint();
1291  AppendPinchGestureBegin(*point1, *point2, gestures);
1292
1293  if (state_ == GS_PENDING_TWO_FINGER_TAP ||
1294      state_ == GS_PENDING_PINCH) {
1295    gfx::Point center = bounding_box_.CenterPoint();
1296    AppendScrollGestureBegin(point, center, gestures);
1297  }
1298
1299  return true;
1300}
1301
1302bool GestureSequence::PinchUpdate(const TouchEvent& event,
1303                                  GesturePoint& point,
1304                                  Gestures* gestures) {
1305  DCHECK(state_ == GS_PINCH);
1306
1307  // It is possible that the none of the touch-points changed their position,
1308  // but their radii changed, and that caused the bounding box to also change.
1309  // But in such cases, we do not want to either pinch or scroll.
1310  // To avoid small jiggles, it is also necessary to make sure that at least one
1311  // of the fingers moved enough before a pinch or scroll update is created.
1312  bool did_scroll = false;
1313  for (int i = 0; i < kMaxGesturePoints; ++i) {
1314    if (!points_[i].in_use() || !points_[i].DidScroll(event, 0))
1315      continue;
1316    did_scroll = true;
1317    break;
1318  }
1319
1320  if (!did_scroll)
1321    return false;
1322
1323  float distance = BoundingBoxDiagonal(bounding_box_);
1324
1325  if (abs(distance - pinch_distance_current_) >=
1326      GestureConfiguration::min_pinch_update_distance_in_pixels()) {
1327    AppendPinchGestureUpdate(point,
1328        distance / pinch_distance_current_, gestures);
1329    pinch_distance_current_ = distance;
1330  }
1331  AppendScrollGestureUpdate(point, gestures);
1332
1333  return true;
1334}
1335
1336bool GestureSequence::PinchEnd(const TouchEvent& event,
1337                               const GesturePoint& point,
1338                               Gestures* gestures) {
1339  DCHECK(state_ == GS_PINCH);
1340
1341  GesturePoint* point1 = GetPointByPointId(0);
1342  GesturePoint* point2 = GetPointByPointId(1);
1343
1344  float distance = BoundingBoxDiagonal(bounding_box_);
1345  AppendPinchGestureEnd(*point1, *point2,
1346      distance / pinch_distance_start_, gestures);
1347
1348  pinch_distance_start_ = 0;
1349  pinch_distance_current_ = 0;
1350  return true;
1351}
1352
1353bool GestureSequence::MaybeSwipe(const TouchEvent& event,
1354                                 const GesturePoint& point,
1355                                 Gestures* gestures) {
1356  DCHECK(state_ == GS_PINCH);
1357  float velocity_x = 0.f, velocity_y = 0.f;
1358  bool swipe_x = true, swipe_y = true;
1359  int sign_x = 0, sign_y = 0;
1360  int i = 0;
1361
1362  for (i = 0; i < kMaxGesturePoints; ++i) {
1363    if (points_[i].in_use())
1364      break;
1365  }
1366  DCHECK(i < kMaxGesturePoints);
1367
1368  velocity_x = points_[i].XVelocity();
1369  velocity_y = points_[i].YVelocity();
1370  sign_x = velocity_x < 0.f ? -1 : 1;
1371  sign_y = velocity_y < 0.f ? -1 : 1;
1372
1373  for (++i; i < kMaxGesturePoints; ++i) {
1374    if (!points_[i].in_use())
1375      continue;
1376
1377    if (sign_x * points_[i].XVelocity() < 0)
1378      swipe_x = false;
1379
1380    if (sign_y * points_[i].YVelocity() < 0)
1381      swipe_y = false;
1382
1383    velocity_x += points_[i].XVelocity();
1384    velocity_y += points_[i].YVelocity();
1385  }
1386
1387  float min_velocity = GestureConfiguration::min_swipe_speed();
1388  min_velocity *= min_velocity;
1389
1390  velocity_x = fabs(velocity_x / point_count_);
1391  velocity_y = fabs(velocity_y / point_count_);
1392  if (velocity_x < min_velocity)
1393    swipe_x = false;
1394  if (velocity_y < min_velocity)
1395    swipe_y = false;
1396
1397  if (!swipe_x && !swipe_y)
1398    return false;
1399
1400  if (!swipe_x)
1401    velocity_x = 0.001f;
1402  if (!swipe_y)
1403    velocity_y = 0.001f;
1404
1405  float ratio = velocity_x > velocity_y ? velocity_x / velocity_y :
1406                                          velocity_y / velocity_x;
1407  if (ratio < GestureConfiguration::max_swipe_deviation_ratio())
1408    return false;
1409
1410  if (velocity_x > velocity_y)
1411    sign_y = 0;
1412  else
1413    sign_x = 0;
1414
1415  AppendSwipeGesture(point, sign_x, sign_y, gestures);
1416
1417  return true;
1418}
1419
1420void GestureSequence::TwoFingerTapOrPinch(const TouchEvent& event,
1421                                          const GesturePoint& point,
1422                                          Gestures* gestures) {
1423  if (IsSecondTouchDownCloseEnoughForTwoFingerTap()) {
1424    TwoFingerTouchDown(event, point, gestures);
1425    set_state(GS_PENDING_TWO_FINGER_TAP);
1426  } else {
1427    set_state(GS_PENDING_PINCH);
1428  }
1429}
1430
1431
1432void GestureSequence::StopTimersIfRequired(const TouchEvent& event) {
1433  if ((!GetLongPressTimer()->IsRunning() &&
1434       !GetShowPressTimer()->IsRunning()) ||
1435      event.type() != ui::ET_TOUCH_MOVED)
1436    return;
1437
1438  // Since a timer is running, there should be a non-NULL point.
1439  const GesturePoint* point = GetPointByPointId(0);
1440  if (!ui::gestures::IsInsideManhattanSquare(point->first_touch_position(),
1441                                             event.location())) {
1442    GetLongPressTimer()->Stop();
1443    GetShowPressTimer()->Stop();
1444  }
1445}
1446
1447}  // namespace ui
1448