Home | History | Annotate | Download | only in gestures
      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/base/gestures/gesture_sequence.h"
      6 
      7 #include <cmath>
      8 #include <stdlib.h>
      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/base/events/event.h"
     16 #include "ui/base/events/event_constants.h"
     17 #include "ui/base/gestures/gesture_configuration.h"
     18 #include "ui/base/gestures/gesture_util.h"
     19 #include "ui/base/ui_base_switches.h"
     20 #include "ui/gfx/rect.h"
     21 
     22 namespace ui {
     23 
     24 namespace {
     25 
     26 // ui::EventType is mapped to TouchState so it can fit into 3 bits of
     27 // Signature.
     28 enum 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.
     39 enum 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|.
     55 TouchState 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 
     83 enum 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_SCROLL_FIRST_RELEASED =
    112       G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS),
    113 
    114   // Once scroll has started, process all touch-move events.
    115   GST_SCROLL_FIRST_MOVED =
    116       G(GS_SCROLL, 0, TS_MOVED, TSI_ALWAYS),
    117 
    118   GST_SCROLL_FIRST_CANCELLED =
    119       G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
    120 
    121   GST_SCROLL_SECOND_PRESSED =
    122       G(GS_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
    123 
    124   GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED =
    125       G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_NOT_PROCESSED),
    126 
    127   GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED =
    128       G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_PROCESSED),
    129 
    130   GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED =
    131       G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_NOT_PROCESSED),
    132 
    133   GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED =
    134       G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_PROCESSED),
    135 
    136   GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED =
    137       G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_ALWAYS),
    138 
    139   GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED =
    140       G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_ALWAYS),
    141 
    142   GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED =
    143       G(GS_PENDING_TWO_FINGER_TAP, 0, TS_CANCELLED, TSI_ALWAYS),
    144 
    145   GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED =
    146       G(GS_PENDING_TWO_FINGER_TAP, 1, TS_CANCELLED, TSI_ALWAYS),
    147 
    148   GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED =
    149       G(GS_PENDING_TWO_FINGER_TAP, 2, TS_PRESSED, TSI_NOT_PROCESSED),
    150 
    151   GST_PINCH_FIRST_MOVED =
    152       G(GS_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED),
    153 
    154   GST_PINCH_FIRST_MOVED_HANDLED =
    155       G(GS_PINCH, 0, TS_MOVED, TSI_PROCESSED),
    156 
    157   GST_PINCH_SECOND_MOVED =
    158       G(GS_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED),
    159 
    160   GST_PINCH_SECOND_MOVED_HANDLED =
    161       G(GS_PINCH, 1, TS_MOVED, TSI_PROCESSED),
    162 
    163   GST_PINCH_FIRST_RELEASED =
    164       G(GS_PINCH, 0, TS_RELEASED, TSI_ALWAYS),
    165 
    166   GST_PINCH_SECOND_RELEASED =
    167       G(GS_PINCH, 1, TS_RELEASED, TSI_ALWAYS),
    168 
    169   GST_PINCH_FIRST_CANCELLED =
    170       G(GS_PINCH, 0, TS_CANCELLED, TSI_ALWAYS),
    171 
    172   GST_PINCH_SECOND_CANCELLED =
    173       G(GS_PINCH, 1, TS_CANCELLED, TSI_ALWAYS),
    174 
    175   GST_PINCH_THIRD_PRESSED =
    176       G(GS_PINCH, 2, TS_PRESSED, TSI_NOT_PROCESSED),
    177 
    178   GST_PINCH_THIRD_MOVED =
    179       G(GS_PINCH, 2, TS_MOVED, TSI_NOT_PROCESSED),
    180 
    181   GST_PINCH_THIRD_MOVED_HANDLED =
    182       G(GS_PINCH, 2, TS_MOVED, TSI_PROCESSED),
    183 
    184   GST_PINCH_THIRD_RELEASED =
    185       G(GS_PINCH, 2, TS_RELEASED, TSI_ALWAYS),
    186 
    187   GST_PINCH_THIRD_CANCELLED =
    188       G(GS_PINCH, 2, TS_CANCELLED, TSI_ALWAYS),
    189 
    190   GST_PINCH_FOURTH_PRESSED =
    191       G(GS_PINCH, 3, TS_PRESSED, TSI_NOT_PROCESSED),
    192 
    193   GST_PINCH_FOURTH_MOVED =
    194       G(GS_PINCH, 3, TS_MOVED, TSI_NOT_PROCESSED),
    195 
    196   GST_PINCH_FOURTH_MOVED_HANDLED =
    197       G(GS_PINCH, 3, TS_MOVED, TSI_PROCESSED),
    198 
    199   GST_PINCH_FOURTH_RELEASED =
    200       G(GS_PINCH, 3, TS_RELEASED, TSI_ALWAYS),
    201 
    202   GST_PINCH_FOURTH_CANCELLED =
    203       G(GS_PINCH, 3, TS_CANCELLED, TSI_ALWAYS),
    204 
    205   GST_PINCH_FIFTH_PRESSED =
    206       G(GS_PINCH, 4, TS_PRESSED, TSI_NOT_PROCESSED),
    207 
    208   GST_PINCH_FIFTH_MOVED =
    209       G(GS_PINCH, 4, TS_MOVED, TSI_NOT_PROCESSED),
    210 
    211   GST_PINCH_FIFTH_MOVED_HANDLED =
    212       G(GS_PINCH, 4, TS_MOVED, TSI_PROCESSED),
    213 
    214   GST_PINCH_FIFTH_RELEASED =
    215       G(GS_PINCH, 4, TS_RELEASED, TSI_ALWAYS),
    216 
    217   GST_PINCH_FIFTH_CANCELLED =
    218       G(GS_PINCH, 4, TS_CANCELLED, TSI_ALWAYS),
    219 };
    220 
    221 // Builds a signature. Signatures are assembled by joining together
    222 // multiple bits.
    223 // 1 LSB bit so that the computed signature is always greater than 0
    224 // 3 bits for the |type|.
    225 // 2 bit for |touch_status|
    226 // 12 bits for |touch_id|
    227 // 14 bits for the |gesture_state|.
    228 EdgeStateSignatureType Signature(GestureState gesture_state,
    229                                  unsigned int touch_id,
    230                                  ui::EventType type,
    231                                  TouchStatusInternal touch_status) {
    232   CHECK((touch_id & 0xfff) == touch_id);
    233   TouchState touch_state = TouchEventTypeToTouchState(type);
    234   EdgeStateSignatureType signature = static_cast<EdgeStateSignatureType>
    235       (G(gesture_state, touch_id, touch_state, touch_status));
    236 
    237   switch (signature) {
    238     case GST_NO_GESTURE_FIRST_PRESSED:
    239     case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
    240     case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
    241     case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
    242     case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
    243     case GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY:
    244     case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
    245     case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
    246     case GST_SCROLL_FIRST_RELEASED:
    247     case GST_SCROLL_FIRST_MOVED:
    248     case GST_SCROLL_FIRST_CANCELLED:
    249     case GST_SCROLL_SECOND_PRESSED:
    250     case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
    251     case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED:
    252     case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED:
    253     case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED:
    254     case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED:
    255     case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED:
    256     case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED:
    257     case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED:
    258     case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED:
    259     case GST_PINCH_FIRST_MOVED:
    260     case GST_PINCH_FIRST_MOVED_HANDLED:
    261     case GST_PINCH_SECOND_MOVED:
    262     case GST_PINCH_SECOND_MOVED_HANDLED:
    263     case GST_PINCH_FIRST_RELEASED:
    264     case GST_PINCH_SECOND_RELEASED:
    265     case GST_PINCH_FIRST_CANCELLED:
    266     case GST_PINCH_SECOND_CANCELLED:
    267     case GST_PINCH_THIRD_PRESSED:
    268     case GST_PINCH_THIRD_MOVED:
    269     case GST_PINCH_THIRD_MOVED_HANDLED:
    270     case GST_PINCH_THIRD_RELEASED:
    271     case GST_PINCH_THIRD_CANCELLED:
    272     case GST_PINCH_FOURTH_PRESSED:
    273     case GST_PINCH_FOURTH_MOVED:
    274     case GST_PINCH_FOURTH_MOVED_HANDLED:
    275     case GST_PINCH_FOURTH_RELEASED:
    276     case GST_PINCH_FOURTH_CANCELLED:
    277     case GST_PINCH_FIFTH_PRESSED:
    278     case GST_PINCH_FIFTH_MOVED:
    279     case GST_PINCH_FIFTH_MOVED_HANDLED:
    280     case GST_PINCH_FIFTH_RELEASED:
    281     case GST_PINCH_FIFTH_CANCELLED:
    282       break;
    283     default:
    284       signature = GST_INVALID;
    285       break;
    286   }
    287 
    288   return signature;
    289 }
    290 #undef G
    291 
    292 float BoundingBoxDiagonal(const gfx::Rect& rect) {
    293   float width = rect.width() * rect.width();
    294   float height = rect.height() * rect.height();
    295   return sqrt(width + height);
    296 }
    297 
    298 unsigned int ComputeTouchBitmask(const GesturePoint* points) {
    299   unsigned int touch_bitmask = 0;
    300   for (int i = 0; i < GestureSequence::kMaxGesturePoints; ++i) {
    301     if (points[i].in_use())
    302       touch_bitmask |= 1 << points[i].touch_id();
    303   }
    304   return touch_bitmask;
    305 }
    306 
    307 const float kFlingCurveNormalization = 1.0f / 1875.f;
    308 
    309 float CalibrateFlingVelocity(float velocity) {
    310   const unsigned last_coefficient =
    311       GestureConfiguration::NumAccelParams - 1;
    312   float normalized_velocity = fabs(velocity * kFlingCurveNormalization);
    313   float nu = 0.0f, x = 1.f;
    314 
    315   for (int i = last_coefficient ; i >= 0; i--) {
    316     float a = GestureConfiguration::fling_acceleration_curve_coefficients(i);
    317     nu += x * a;
    318     x *= normalized_velocity;
    319   }
    320   if (velocity < 0.f)
    321     return std::max(nu * velocity, -GestureConfiguration::fling_velocity_cap());
    322   else
    323     return std::min(nu * velocity, GestureConfiguration::fling_velocity_cap());
    324 }
    325 
    326 }  // namespace
    327 
    328 ////////////////////////////////////////////////////////////////////////////////
    329 // GestureSequence Public:
    330 
    331 GestureSequence::GestureSequence(GestureEventHelper* helper)
    332     : state_(GS_NO_GESTURE),
    333       flags_(0),
    334       pinch_distance_start_(0.f),
    335       pinch_distance_current_(0.f),
    336       scroll_type_(ST_FREE),
    337       point_count_(0),
    338       helper_(helper) {
    339 }
    340 
    341 GestureSequence::~GestureSequence() {
    342 }
    343 
    344 GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
    345     const TouchEvent& event,
    346     EventResult result) {
    347   StopLongPressTimerIfRequired(event);
    348   last_touch_location_ = event.location();
    349   if (result & ER_CONSUMED)
    350     return NULL;
    351 
    352   // Set a limit on the number of simultaneous touches in a gesture.
    353   if (event.touch_id() >= kMaxGesturePoints)
    354     return NULL;
    355 
    356   if (event.type() == ui::ET_TOUCH_PRESSED) {
    357     if (point_count_ == kMaxGesturePoints)
    358       return NULL;
    359     GesturePoint* new_point = &points_[event.touch_id()];
    360     // We shouldn't be able to get two PRESSED events from the same
    361     // finger without either a RELEASE or CANCEL in between. But let's not crash
    362     // in a release build.
    363     if (new_point->in_use()) {
    364       LOG(ERROR) << "Received a second press for a point: " << event.touch_id();
    365       new_point->ResetVelocity();
    366       new_point->UpdateValues(event);
    367       return NULL;
    368     }
    369     new_point->set_point_id(point_count_++);
    370     new_point->set_touch_id(event.touch_id());
    371   }
    372 
    373   GestureState last_state = state_;
    374 
    375   // NOTE: when modifying these state transitions, also update gestures.dot
    376   scoped_ptr<Gestures> gestures(new Gestures());
    377   GesturePoint& point = GesturePointForEvent(event);
    378   point.UpdateValues(event);
    379   RecreateBoundingBox();
    380   flags_ = event.flags();
    381   const int point_id = point.point_id();
    382   if (point_id < 0)
    383     return NULL;
    384 
    385   // Send GESTURE_BEGIN for any touch pressed.
    386   if (event.type() == ui::ET_TOUCH_PRESSED)
    387     AppendBeginGestureEvent(point, gestures.get());
    388 
    389   TouchStatusInternal status_internal = (result == ER_UNHANDLED) ?
    390       TSI_NOT_PROCESSED : TSI_PROCESSED;
    391 
    392   EdgeStateSignatureType signature = Signature(state_, point_id,
    393       event.type(), status_internal);
    394 
    395   if (signature == GST_INVALID)
    396     signature = Signature(state_, point_id, event.type(), TSI_ALWAYS);
    397 
    398   switch (signature) {
    399     case GST_INVALID:
    400       break;
    401 
    402     case GST_NO_GESTURE_FIRST_PRESSED:
    403       TouchDown(event, point, gestures.get());
    404       set_state(GS_PENDING_SYNTHETIC_CLICK);
    405       break;
    406     case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
    407       if (Click(event, point, gestures.get()))
    408         point.UpdateForTap();
    409       else
    410         PrependTapCancelGestureEvent(point, gestures.get());
    411       set_state(GS_NO_GESTURE);
    412       break;
    413     case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED:
    414     case GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY:
    415       if (ScrollStart(event, point, gestures.get())) {
    416         PrependTapCancelGestureEvent(point, gestures.get());
    417         set_state(GS_SCROLL);
    418         if (ScrollUpdate(event, point, gestures.get()))
    419           point.UpdateForScroll();
    420       }
    421       break;
    422     case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
    423       // TODO(rbyers): This should be able to trigger a TapCancel
    424       // if we moved far enough. crbug.com/146397
    425       break;
    426     case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
    427     case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
    428       PrependTapCancelGestureEvent(point, gestures.get());
    429       set_state(GS_NO_GESTURE);
    430       break;
    431     case GST_SCROLL_FIRST_MOVED:
    432       if (scroll_type_ == ST_VERTICAL ||
    433           scroll_type_ == ST_HORIZONTAL)
    434         BreakRailScroll(event, point, gestures.get());
    435       if (ScrollUpdate(event, point, gestures.get()))
    436         point.UpdateForScroll();
    437       break;
    438     case GST_SCROLL_FIRST_RELEASED:
    439     case GST_SCROLL_FIRST_CANCELLED:
    440       ScrollEnd(event, point, gestures.get());
    441       set_state(GS_NO_GESTURE);
    442       break;
    443     case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
    444       PrependTapCancelGestureEvent(point, gestures.get());
    445       if (IsSecondTouchDownCloseEnoughForTwoFingerTap()) {
    446         TwoFingerTouchDown(event, point, gestures.get());
    447         set_state(GS_PENDING_TWO_FINGER_TAP);
    448         break;
    449       }
    450       // fall through
    451     case GST_SCROLL_SECOND_PRESSED:
    452       PinchStart(event, point, gestures.get());
    453       set_state(GS_PINCH);
    454       break;
    455     case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
    456     case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED:
    457       TwoFingerTouchReleased(event, point, gestures.get());
    458       set_state(GS_SCROLL);
    459       break;
    460     case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED:
    461     case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED:
    462       if (TwoFingerTouchMove(event, point, gestures.get()))
    463         set_state(GS_PINCH);
    464       break;
    465     case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED:
    466     case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED:
    467     case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED:
    468     case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED:
    469       scroll_type_ = ST_FREE;
    470       set_state(GS_SCROLL);
    471       break;
    472     case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED:
    473       PinchStart(event, point, gestures.get());
    474       set_state(GS_PINCH);
    475       break;
    476     case GST_PINCH_FIRST_MOVED_HANDLED:
    477     case GST_PINCH_SECOND_MOVED_HANDLED:
    478     case GST_PINCH_THIRD_MOVED_HANDLED:
    479     case GST_PINCH_FOURTH_MOVED_HANDLED:
    480     case GST_PINCH_FIFTH_MOVED_HANDLED:
    481       break;
    482     case GST_PINCH_FIRST_MOVED:
    483     case GST_PINCH_SECOND_MOVED:
    484     case GST_PINCH_THIRD_MOVED:
    485     case GST_PINCH_FOURTH_MOVED:
    486     case GST_PINCH_FIFTH_MOVED:
    487       if (PinchUpdate(event, point, gestures.get())) {
    488         for (int i = 0; i < point_count_; ++i)
    489           GetPointByPointId(i)->UpdateForScroll();
    490       }
    491       break;
    492     case GST_PINCH_FIRST_RELEASED:
    493     case GST_PINCH_SECOND_RELEASED:
    494     case GST_PINCH_THIRD_RELEASED:
    495     case GST_PINCH_FOURTH_RELEASED:
    496     case GST_PINCH_FIFTH_RELEASED:
    497     case GST_PINCH_FIRST_CANCELLED:
    498     case GST_PINCH_SECOND_CANCELLED:
    499     case GST_PINCH_THIRD_CANCELLED:
    500     case GST_PINCH_FOURTH_CANCELLED:
    501     case GST_PINCH_FIFTH_CANCELLED:
    502       // Was it a swipe? i.e. were all the fingers moving in the same
    503       // direction?
    504       MaybeSwipe(event, point, gestures.get());
    505 
    506       if (point_count_ == 2) {
    507         PinchEnd(event, point, gestures.get());
    508 
    509         // Once pinch ends, it should still be possible to scroll with the
    510         // remaining finger on the screen.
    511         set_state(GS_SCROLL);
    512       } else {
    513         // Nothing else to do if we have more than 2 fingers active, since after
    514         // the release/cancel, there are still enough fingers to do pinch.
    515         // pinch_distance_current_ and pinch_distance_start_ will be updated
    516         // when the bounding-box is updated.
    517       }
    518       ResetVelocities();
    519       break;
    520     case GST_PINCH_THIRD_PRESSED:
    521     case GST_PINCH_FOURTH_PRESSED:
    522     case GST_PINCH_FIFTH_PRESSED:
    523       pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
    524       pinch_distance_start_ = pinch_distance_current_;
    525       break;
    526   }
    527 
    528   if (event.type() == ui::ET_TOUCH_RELEASED ||
    529       event.type() == ui::ET_TOUCH_CANCELLED)
    530     AppendEndGestureEvent(point, gestures.get());
    531 
    532   if (state_ != last_state)
    533     DVLOG(4) << "Gesture Sequence"
    534              << " State: " << state_
    535              << " touch id: " << event.touch_id();
    536 
    537   if (last_state == GS_PENDING_SYNTHETIC_CLICK && state_ != last_state)
    538     GetLongPressTimer()->Stop();
    539 
    540   // The set of point_ids must be contiguous and include 0.
    541   // When a touch point is released, all points with ids greater than the
    542   // released point must have their ids decremented, or the set of point_ids
    543   // could end up with gaps.
    544   if (event.type() == ui::ET_TOUCH_RELEASED ||
    545       event.type() == ui::ET_TOUCH_CANCELLED) {
    546     for (int i = 0; i < kMaxGesturePoints; ++i) {
    547       GesturePoint& iter_point = points_[i];
    548       if (iter_point.point_id() > point.point_id())
    549         iter_point.set_point_id(iter_point.point_id() - 1);
    550     }
    551 
    552     point.Reset();
    553     --point_count_;
    554     CHECK_GE(point_count_, 0);
    555     RecreateBoundingBox();
    556     if (state_ == GS_PINCH) {
    557       pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
    558       pinch_distance_start_ = pinch_distance_current_;
    559     }
    560   }
    561 
    562   const ui::LatencyInfo* touch_latency = event.latency();
    563   Gestures::iterator it = gestures->begin();
    564   for (; it != gestures->end(); it++) {
    565     (*it)->latency()->MergeWith(*touch_latency);
    566   }
    567 
    568   return gestures.release();
    569 }
    570 
    571 void GestureSequence::RecreateBoundingBox() {
    572   // TODO(sad): Recreating the bounding box at every touch-event is not very
    573   // efficient. This should be made better.
    574   if (point_count_ == 0) {
    575     bounding_box_.SetRect(0, 0, 0, 0);
    576   } else if (point_count_ == 1) {
    577     bounding_box_ = GetPointByPointId(0)->enclosing_rectangle();
    578   } else {
    579     int left = INT_MAX / 20, top = INT_MAX / 20;
    580     int right = INT_MIN / 20, bottom = INT_MIN / 20;
    581     for (int i = 0; i < kMaxGesturePoints; ++i) {
    582       if (!points_[i].in_use())
    583         continue;
    584       // Using the |enclosing_rectangle()| for the touch-points would be ideal.
    585       // However, this becomes brittle especially when a finger is in motion
    586       // because the change in radius can overshadow the actual change in
    587       // position. So the actual position of the point is used instead.
    588       const gfx::Point& point = points_[i].last_touch_position();
    589       left = std::min(left, point.x());
    590       right = std::max(right, point.x());
    591       top = std::min(top, point.y());
    592       bottom = std::max(bottom, point.y());
    593     }
    594     bounding_box_.SetRect(left, top, right - left, bottom - top);
    595   }
    596 }
    597 
    598 void GestureSequence::ResetVelocities() {
    599   for (int i = 0; i < kMaxGesturePoints; ++i) {
    600     if (points_[i].in_use())
    601       points_[i].ResetVelocity();
    602   }
    603 }
    604 
    605 ////////////////////////////////////////////////////////////////////////////////
    606 // GestureSequence Protected:
    607 
    608 base::OneShotTimer<GestureSequence>* GestureSequence::CreateTimer() {
    609   return new base::OneShotTimer<GestureSequence>();
    610 }
    611 
    612 base::OneShotTimer<GestureSequence>* GestureSequence::GetLongPressTimer() {
    613   if (!long_press_timer_.get())
    614     long_press_timer_.reset(CreateTimer());
    615   return long_press_timer_.get();
    616 }
    617 
    618 ////////////////////////////////////////////////////////////////////////////////
    619 // GestureSequence Private:
    620 
    621 GesturePoint& GestureSequence::GesturePointForEvent(
    622     const TouchEvent& event) {
    623   return points_[event.touch_id()];
    624 }
    625 
    626 GesturePoint* GestureSequence::GetPointByPointId(int point_id) {
    627   DCHECK(0 <= point_id && point_id < kMaxGesturePoints);
    628   for (int i = 0; i < kMaxGesturePoints; ++i) {
    629     GesturePoint& point = points_[i];
    630     if (point.in_use() && point.point_id() == point_id)
    631       return &point;
    632   }
    633   NOTREACHED();
    634   return NULL;
    635 }
    636 
    637 bool GestureSequence::IsSecondTouchDownCloseEnoughForTwoFingerTap() {
    638   gfx::Point p1 = GetPointByPointId(0)->last_touch_position();
    639   gfx::Point p2 = GetPointByPointId(1)->last_touch_position();
    640   double max_distance =
    641       GestureConfiguration::max_distance_for_two_finger_tap_in_pixels();
    642   double distance = (p1.x() - p2.x()) * (p1.x() - p2.x()) +
    643       (p1.y() - p2.y()) * (p1.y() - p2.y());
    644   if (distance < max_distance * max_distance)
    645     return true;
    646   return false;
    647 }
    648 
    649 GestureEvent* GestureSequence::CreateGestureEvent(
    650     const GestureEventDetails& details,
    651     const gfx::Point& location,
    652     int flags,
    653     base::Time timestamp,
    654     unsigned int touch_id_bitmask) {
    655   GestureEventDetails gesture_details(details);
    656   gesture_details.set_touch_points(point_count_);
    657   gesture_details.set_bounding_box(bounding_box_);
    658   base::TimeDelta time_stamp =
    659       base::TimeDelta::FromMicroseconds(timestamp.ToDoubleT() * 1000000);
    660   return new GestureEvent(gesture_details.type(), location.x(), location.y(),
    661                           flags, time_stamp, gesture_details,
    662                           touch_id_bitmask);
    663 }
    664 
    665 void GestureSequence::AppendTapDownGestureEvent(const GesturePoint& point,
    666                                                 Gestures* gestures) {
    667   gestures->push_back(CreateGestureEvent(
    668       GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0),
    669       point.first_touch_position(),
    670       flags_,
    671       base::Time::FromDoubleT(point.last_touch_time()),
    672       1 << point.touch_id()));
    673 }
    674 
    675 void GestureSequence::PrependTapCancelGestureEvent(const GesturePoint& point,
    676                                             Gestures* gestures) {
    677   gestures->insert(gestures->begin(), CreateGestureEvent(
    678     GestureEventDetails(ui::ET_GESTURE_TAP_CANCEL, 0, 0),
    679     point.first_touch_position(),
    680     flags_,
    681     base::Time::FromDoubleT(point.last_touch_time()),
    682     1 << point.touch_id()));
    683 }
    684 
    685 void GestureSequence::AppendBeginGestureEvent(const GesturePoint& point,
    686                                               Gestures* gestures) {
    687   gestures->push_back(CreateGestureEvent(
    688       GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0),
    689       point.first_touch_position(),
    690       flags_,
    691       base::Time::FromDoubleT(point.last_touch_time()),
    692       1 << point.touch_id()));
    693 }
    694 
    695 void GestureSequence::AppendEndGestureEvent(const GesturePoint& point,
    696                                               Gestures* gestures) {
    697   gestures->push_back(CreateGestureEvent(
    698       GestureEventDetails(ui::ET_GESTURE_END, 0, 0),
    699       point.first_touch_position(),
    700       flags_,
    701       base::Time::FromDoubleT(point.last_touch_time()),
    702       1 << point.touch_id()));
    703 }
    704 
    705 void GestureSequence::AppendClickGestureEvent(const GesturePoint& point,
    706                                               int tap_count,
    707                                               Gestures* gestures) {
    708   gfx::Rect er = point.enclosing_rectangle();
    709   gfx::Point center = er.CenterPoint();
    710   gestures->push_back(CreateGestureEvent(
    711       GestureEventDetails(ui::ET_GESTURE_TAP, tap_count, 0),
    712       center,
    713       flags_,
    714       base::Time::FromDoubleT(point.last_touch_time()),
    715       1 << point.touch_id()));
    716 }
    717 
    718 void GestureSequence::AppendScrollGestureBegin(const GesturePoint& point,
    719                                                const gfx::Point& location,
    720                                                Gestures* gestures) {
    721   gestures->push_back(CreateGestureEvent(
    722       GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0),
    723       location,
    724       flags_,
    725       base::Time::FromDoubleT(point.last_touch_time()),
    726       1 << point.touch_id()));
    727 }
    728 
    729 void GestureSequence::AppendScrollGestureEnd(const GesturePoint& point,
    730                                              const gfx::Point& location,
    731                                              Gestures* gestures,
    732                                              float x_velocity,
    733                                              float y_velocity) {
    734   float railed_x_velocity = x_velocity;
    735   float railed_y_velocity = y_velocity;
    736   last_scroll_prediction_offset_.set_x(0);
    737   last_scroll_prediction_offset_.set_y(0);
    738 
    739   if (scroll_type_ == ST_HORIZONTAL)
    740     railed_y_velocity = 0;
    741   else if (scroll_type_ == ST_VERTICAL)
    742     railed_x_velocity = 0;
    743 
    744   if (railed_x_velocity != 0 || railed_y_velocity != 0) {
    745 
    746     gestures->push_back(CreateGestureEvent(
    747         GestureEventDetails(ui::ET_SCROLL_FLING_START,
    748             CalibrateFlingVelocity(railed_x_velocity),
    749             CalibrateFlingVelocity(railed_y_velocity),
    750             CalibrateFlingVelocity(x_velocity),
    751             CalibrateFlingVelocity(y_velocity)),
    752         location,
    753         flags_,
    754         base::Time::FromDoubleT(point.last_touch_time()),
    755         1 << point.touch_id()));
    756   } else {
    757     gestures->push_back(CreateGestureEvent(
    758         GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0),
    759         location,
    760         flags_,
    761         base::Time::FromDoubleT(point.last_touch_time()),
    762         1 << point.touch_id()));
    763   }
    764 }
    765 
    766 void GestureSequence::AppendScrollGestureUpdate(GesturePoint& point,
    767                                                 Gestures* gestures) {
    768   static bool use_scroll_prediction = CommandLine::ForCurrentProcess()->
    769       HasSwitch(switches::kEnableScrollPrediction);
    770   gfx::Vector2dF d;
    771   gfx::Point location;
    772   if (point_count_ == 1) {
    773     d = point.ScrollDelta();
    774     location = point.last_touch_position();
    775   } else {
    776     location = bounding_box_.CenterPoint();
    777     d = location - latest_multi_scroll_update_location_;
    778     latest_multi_scroll_update_location_ = location;
    779   }
    780 
    781   if (use_scroll_prediction) {
    782     // Remove the extra distance added by the last scroll prediction and add
    783     // the new prediction offset.
    784     d -= last_scroll_prediction_offset_;
    785     last_scroll_prediction_offset_.set_x(
    786         GestureConfiguration::scroll_prediction_seconds() * point.XVelocity());
    787     last_scroll_prediction_offset_.set_y(
    788         GestureConfiguration::scroll_prediction_seconds() * point.YVelocity());
    789     d += last_scroll_prediction_offset_;
    790     location += gfx::Vector2d(last_scroll_prediction_offset_.x(),
    791                               last_scroll_prediction_offset_.y());
    792   }
    793 
    794   gfx::Vector2dF o = d;
    795 
    796   if (scroll_type_ == ST_HORIZONTAL)
    797     d.set_y(0);
    798   else if (scroll_type_ == ST_VERTICAL)
    799     d.set_x(0);
    800   if (d.IsZero())
    801     return;
    802 
    803   GestureEventDetails details(ui::ET_GESTURE_SCROLL_UPDATE,
    804                               d.x(), d.y(), o.x(), o.y());
    805   details.SetScrollVelocity(
    806       scroll_type_ == ST_VERTICAL ? 0 : point.XVelocity(),
    807       scroll_type_ == ST_HORIZONTAL ? 0 : point.YVelocity(),
    808       point.XVelocity(),
    809       point.YVelocity());
    810   gestures->push_back(CreateGestureEvent(
    811       details,
    812       location,
    813       flags_,
    814       base::Time::FromDoubleT(point.last_touch_time()),
    815       ComputeTouchBitmask(points_)));
    816 }
    817 
    818 void GestureSequence::AppendPinchGestureBegin(const GesturePoint& p1,
    819                                               const GesturePoint& p2,
    820                                               Gestures* gestures) {
    821   gfx::Point center = bounding_box_.CenterPoint();
    822   gestures->push_back(CreateGestureEvent(
    823       GestureEventDetails(ui::ET_GESTURE_PINCH_BEGIN, 0, 0),
    824       center,
    825       flags_,
    826       base::Time::FromDoubleT(p1.last_touch_time()),
    827       1 << p1.touch_id() | 1 << p2.touch_id()));
    828 }
    829 
    830 void GestureSequence::AppendPinchGestureEnd(const GesturePoint& p1,
    831                                             const GesturePoint& p2,
    832                                             float scale,
    833                                             Gestures* gestures) {
    834   gfx::Point center = bounding_box_.CenterPoint();
    835   gestures->push_back(CreateGestureEvent(
    836       GestureEventDetails(ui::ET_GESTURE_PINCH_END, 0, 0),
    837       center,
    838       flags_,
    839       base::Time::FromDoubleT(p1.last_touch_time()),
    840       1 << p1.touch_id() | 1 << p2.touch_id()));
    841 }
    842 
    843 void GestureSequence::AppendPinchGestureUpdate(const GesturePoint& point,
    844                                                float scale,
    845                                                Gestures* gestures) {
    846   // TODO(sad): Compute rotation and include it in delta_y.
    847   // http://crbug.com/113145
    848   gestures->push_back(CreateGestureEvent(
    849       GestureEventDetails(ui::ET_GESTURE_PINCH_UPDATE, scale, 0),
    850       bounding_box_.CenterPoint(),
    851       flags_,
    852       base::Time::FromDoubleT(point.last_touch_time()),
    853       ComputeTouchBitmask(points_)));
    854 }
    855 
    856 void GestureSequence::AppendSwipeGesture(const GesturePoint& point,
    857                                          int swipe_x,
    858                                          int swipe_y,
    859                                          Gestures* gestures) {
    860   gestures->push_back(CreateGestureEvent(
    861       GestureEventDetails(ui::ET_GESTURE_MULTIFINGER_SWIPE, swipe_x, swipe_y),
    862       bounding_box_.CenterPoint(),
    863       flags_,
    864       base::Time::FromDoubleT(point.last_touch_time()),
    865       ComputeTouchBitmask(points_)));
    866 }
    867 
    868 void GestureSequence::AppendTwoFingerTapGestureEvent(Gestures* gestures) {
    869   const GesturePoint* point = GetPointByPointId(0);
    870   const gfx::Rect rect = point->enclosing_rectangle();
    871   gestures->push_back(CreateGestureEvent(
    872       GestureEventDetails(ui::ET_GESTURE_TWO_FINGER_TAP,
    873                           rect.width(),
    874                           rect.height()),
    875       point->enclosing_rectangle().CenterPoint(),
    876       flags_,
    877       base::Time::FromDoubleT(point->last_touch_time()),
    878       1 << point->touch_id()));
    879 }
    880 
    881 bool GestureSequence::Click(const TouchEvent& event,
    882                             const GesturePoint& point,
    883                             Gestures* gestures) {
    884   DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
    885   if (point.IsInClickWindow(event)) {
    886     int tap_count = 1;
    887     if (point.IsInTripleClickWindow(event))
    888       tap_count = 3;
    889     else if (point.IsInDoubleClickWindow(event))
    890       tap_count = 2;
    891     AppendClickGestureEvent(point, tap_count, gestures);
    892     return true;
    893   } else if (point.IsInsideManhattanSquare(event) &&
    894       !GetLongPressTimer()->IsRunning()) {
    895     AppendLongTapGestureEvent(point, gestures);
    896   }
    897   return false;
    898 }
    899 
    900 bool GestureSequence::ScrollStart(const TouchEvent& event,
    901                                   GesturePoint& point,
    902                                   Gestures* gestures) {
    903   DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
    904   if (!point.IsConsistentScrollingActionUnderway() &&
    905       !point.IsInScrollWindow(event))
    906     return false;
    907   AppendScrollGestureBegin(point, point.first_touch_position(), gestures);
    908   if (point.IsInHorizontalRailWindow())
    909     scroll_type_ = ST_HORIZONTAL;
    910   else if (point.IsInVerticalRailWindow())
    911     scroll_type_ = ST_VERTICAL;
    912   else
    913     scroll_type_ = ST_FREE;
    914   return true;
    915 }
    916 
    917 void GestureSequence::BreakRailScroll(const TouchEvent& event,
    918                                       GesturePoint& point,
    919                                       Gestures* gestures) {
    920   DCHECK(state_ == GS_SCROLL);
    921   if (scroll_type_ == ST_HORIZONTAL &&
    922       point.BreaksHorizontalRail())
    923     scroll_type_ = ST_FREE;
    924   else if (scroll_type_ == ST_VERTICAL &&
    925            point.BreaksVerticalRail())
    926     scroll_type_ = ST_FREE;
    927 }
    928 
    929 bool GestureSequence::ScrollUpdate(const TouchEvent& event,
    930                                    GesturePoint& point,
    931                                    Gestures* gestures) {
    932   DCHECK(state_ == GS_SCROLL);
    933   if (!point.DidScroll(event, 0))
    934     return false;
    935   AppendScrollGestureUpdate(point, gestures);
    936   return true;
    937 }
    938 
    939 bool GestureSequence::TouchDown(const TouchEvent& event,
    940                                 const GesturePoint& point,
    941                                 Gestures* gestures) {
    942   DCHECK(state_ == GS_NO_GESTURE);
    943   AppendTapDownGestureEvent(point, gestures);
    944   GetLongPressTimer()->Start(
    945       FROM_HERE,
    946       base::TimeDelta::FromMilliseconds(
    947           GestureConfiguration::long_press_time_in_seconds() * 1000),
    948       this,
    949       &GestureSequence::AppendLongPressGestureEvent);
    950   return true;
    951 }
    952 
    953 bool GestureSequence::TwoFingerTouchDown(const TouchEvent& event,
    954                                          const GesturePoint& point,
    955                                          Gestures* gestures) {
    956   DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK || state_ == GS_SCROLL);
    957   if (state_ == GS_SCROLL) {
    958     AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
    959         0.f, 0.f);
    960   }
    961   second_touch_time_ = event.time_stamp();
    962   return true;
    963 }
    964 
    965 bool GestureSequence::TwoFingerTouchMove(const TouchEvent& event,
    966                                          const GesturePoint& point,
    967                                          Gestures* gestures) {
    968   DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP);
    969 
    970   base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
    971   base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
    972       ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
    973   if (time_delta > max_delta || !point.IsInsideManhattanSquare(event)) {
    974     PinchStart(event, point, gestures);
    975     return true;
    976   }
    977   return false;
    978 }
    979 
    980 bool GestureSequence::TwoFingerTouchReleased(const TouchEvent& event,
    981                                              const GesturePoint& point,
    982                                              Gestures* gestures) {
    983   DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP);
    984   base::TimeDelta time_delta = event.time_stamp() - second_touch_time_;
    985   base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 *
    986       ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click());
    987   if (time_delta < max_delta && point.IsInsideManhattanSquare(event))
    988     AppendTwoFingerTapGestureEvent(gestures);
    989   return true;
    990 }
    991 
    992 void GestureSequence::AppendLongPressGestureEvent() {
    993   const GesturePoint* point = GetPointByPointId(0);
    994   scoped_ptr<GestureEvent> gesture(CreateGestureEvent(
    995       GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0),
    996       point->first_touch_position(),
    997       flags_,
    998       base::Time::FromDoubleT(point->last_touch_time()),
    999       1 << point->touch_id()));
   1000   helper_->DispatchLongPressGestureEvent(gesture.get());
   1001 }
   1002 
   1003 void GestureSequence::AppendLongTapGestureEvent(const GesturePoint& point,
   1004                                                 Gestures* gestures) {
   1005   gfx::Rect er = point.enclosing_rectangle();
   1006   gfx::Point center = er.CenterPoint();
   1007   gestures->push_back(CreateGestureEvent(
   1008       GestureEventDetails(ui::ET_GESTURE_LONG_TAP, 0, 0),
   1009       center,
   1010       flags_,
   1011       base::Time::FromDoubleT(point.last_touch_time()),
   1012       1 << point.touch_id()));
   1013 }
   1014 
   1015 bool GestureSequence::ScrollEnd(const TouchEvent& event,
   1016                                 GesturePoint& point,
   1017                                 Gestures* gestures) {
   1018   DCHECK(state_ == GS_SCROLL);
   1019   if (point.IsInFlickWindow(event)) {
   1020     AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
   1021         point.XVelocity(), point.YVelocity());
   1022   } else {
   1023     AppendScrollGestureEnd(point, point.last_touch_position(), gestures,
   1024         0.f, 0.f);
   1025   }
   1026   return true;
   1027 }
   1028 
   1029 bool GestureSequence::PinchStart(const TouchEvent& event,
   1030                                  const GesturePoint& point,
   1031                                  Gestures* gestures) {
   1032   DCHECK(state_ == GS_SCROLL ||
   1033          state_ == GS_PENDING_SYNTHETIC_CLICK ||
   1034          state_ == GS_PENDING_TWO_FINGER_TAP);
   1035 
   1036   // Once pinch starts, we immediately break rail scroll.
   1037   scroll_type_ = ST_FREE;
   1038 
   1039   const GesturePoint* point1 = GetPointByPointId(0);
   1040   const GesturePoint* point2 = GetPointByPointId(1);
   1041 
   1042   pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_);
   1043   pinch_distance_start_ = pinch_distance_current_;
   1044   latest_multi_scroll_update_location_ = bounding_box_.CenterPoint();
   1045   AppendPinchGestureBegin(*point1, *point2, gestures);
   1046 
   1047   if (state_ == GS_PENDING_SYNTHETIC_CLICK ||
   1048       state_ == GS_PENDING_TWO_FINGER_TAP) {
   1049     gfx::Point center = bounding_box_.CenterPoint();
   1050     AppendScrollGestureBegin(point, center, gestures);
   1051   }
   1052 
   1053   return true;
   1054 }
   1055 
   1056 bool GestureSequence::PinchUpdate(const TouchEvent& event,
   1057                                   GesturePoint& point,
   1058                                   Gestures* gestures) {
   1059   DCHECK(state_ == GS_PINCH);
   1060 
   1061   // It is possible that the none of the touch-points changed their position,
   1062   // but their radii changed, and that caused the bounding box to also change.
   1063   // But in such cases, we do not want to either pinch or scroll.
   1064   // To avoid small jiggles, it is also necessary to make sure that at least one
   1065   // of the fingers moved enough before a pinch or scroll update is created.
   1066   bool did_scroll = false;
   1067   for (int i = 0; i < kMaxGesturePoints; ++i) {
   1068     if (!points_[i].in_use() || !points_[i].DidScroll(event, 0))
   1069       continue;
   1070     did_scroll = true;
   1071     break;
   1072   }
   1073 
   1074   if (!did_scroll)
   1075     return false;
   1076 
   1077   float distance = BoundingBoxDiagonal(bounding_box_);
   1078 
   1079   if (abs(distance - pinch_distance_current_) >=
   1080       GestureConfiguration::min_pinch_update_distance_in_pixels()) {
   1081     AppendPinchGestureUpdate(point,
   1082         distance / pinch_distance_current_, gestures);
   1083     pinch_distance_current_ = distance;
   1084   }
   1085   AppendScrollGestureUpdate(point, gestures);
   1086 
   1087   return true;
   1088 }
   1089 
   1090 bool GestureSequence::PinchEnd(const TouchEvent& event,
   1091                                const GesturePoint& point,
   1092                                Gestures* gestures) {
   1093   DCHECK(state_ == GS_PINCH);
   1094 
   1095   GesturePoint* point1 = GetPointByPointId(0);
   1096   GesturePoint* point2 = GetPointByPointId(1);
   1097 
   1098   float distance = BoundingBoxDiagonal(bounding_box_);
   1099   AppendPinchGestureEnd(*point1, *point2,
   1100       distance / pinch_distance_start_, gestures);
   1101 
   1102   pinch_distance_start_ = 0;
   1103   pinch_distance_current_ = 0;
   1104   return true;
   1105 }
   1106 
   1107 bool GestureSequence::MaybeSwipe(const TouchEvent& event,
   1108                                  const GesturePoint& point,
   1109                                  Gestures* gestures) {
   1110   DCHECK(state_ == GS_PINCH);
   1111   float velocity_x = 0.f, velocity_y = 0.f;
   1112   bool swipe_x = true, swipe_y = true;
   1113   int sign_x = 0, sign_y = 0;
   1114   int i = 0;
   1115 
   1116   for (i = 0; i < kMaxGesturePoints; ++i) {
   1117     if (points_[i].in_use())
   1118       break;
   1119   }
   1120   DCHECK(i < kMaxGesturePoints);
   1121 
   1122   velocity_x = points_[i].XVelocity();
   1123   velocity_y = points_[i].YVelocity();
   1124   sign_x = velocity_x < 0.f ? -1 : 1;
   1125   sign_y = velocity_y < 0.f ? -1 : 1;
   1126 
   1127   for (++i; i < kMaxGesturePoints; ++i) {
   1128     if (!points_[i].in_use())
   1129       continue;
   1130 
   1131     if (sign_x * points_[i].XVelocity() < 0)
   1132       swipe_x = false;
   1133 
   1134     if (sign_y * points_[i].YVelocity() < 0)
   1135       swipe_y = false;
   1136 
   1137     velocity_x += points_[i].XVelocity();
   1138     velocity_y += points_[i].YVelocity();
   1139   }
   1140 
   1141   float min_velocity = GestureConfiguration::min_swipe_speed();
   1142   min_velocity *= min_velocity;
   1143 
   1144   velocity_x = fabs(velocity_x / point_count_);
   1145   velocity_y = fabs(velocity_y / point_count_);
   1146   if (velocity_x < min_velocity)
   1147     swipe_x = false;
   1148   if (velocity_y < min_velocity)
   1149     swipe_y = false;
   1150 
   1151   if (!swipe_x && !swipe_y)
   1152     return false;
   1153 
   1154   if (!swipe_x)
   1155     velocity_x = 0.001f;
   1156   if (!swipe_y)
   1157     velocity_y = 0.001f;
   1158 
   1159   float ratio = velocity_x > velocity_y ? velocity_x / velocity_y :
   1160                                           velocity_y / velocity_x;
   1161   if (ratio < GestureConfiguration::max_swipe_deviation_ratio())
   1162     return false;
   1163 
   1164   if (velocity_x > velocity_y)
   1165     sign_y = 0;
   1166   else
   1167     sign_x = 0;
   1168 
   1169   AppendSwipeGesture(point, sign_x, sign_y, gestures);
   1170 
   1171   return true;
   1172 }
   1173 
   1174 void GestureSequence::StopLongPressTimerIfRequired(const TouchEvent& event) {
   1175   if (!GetLongPressTimer()->IsRunning() ||
   1176       event.type() != ui::ET_TOUCH_MOVED)
   1177     return;
   1178 
   1179   // Since long press timer has been started, there should be a non-NULL point.
   1180   const GesturePoint* point = GetPointByPointId(0);
   1181   if (!ui::gestures::IsInsideManhattanSquare(point->first_touch_position(),
   1182       event.location()))
   1183     GetLongPressTimer()->Stop();
   1184 }
   1185 
   1186 }  // namespace ui
   1187