Home | History | Annotate | Download | only in input
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
      6 
      7 #include "base/logging.h"
      8 #include "ui/gfx/point_f.h"
      9 
     10 namespace content {
     11 namespace {
     12 
     13 gfx::Vector2d FloorTowardZero(const gfx::Vector2dF& vector) {
     14   int x = vector.x() > 0 ? floor(vector.x()) : ceil(vector.x());
     15   int y = vector.y() > 0 ? floor(vector.y()) : ceil(vector.y());
     16   return gfx::Vector2d(x, y);
     17 }
     18 
     19 gfx::Vector2d CeilFromZero(const gfx::Vector2dF& vector) {
     20   int x = vector.x() > 0 ? ceil(vector.x()) : floor(vector.x());
     21   int y = vector.y() > 0 ? ceil(vector.y()) : floor(vector.y());
     22   return gfx::Vector2d(x, y);
     23 }
     24 
     25 gfx::Vector2dF ProjectScalarOntoVector(
     26     float scalar, const gfx::Vector2d& vector) {
     27   return gfx::ScaleVector2d(vector, scalar / vector.Length());
     28 }
     29 
     30 }  // namespace
     31 
     32 SyntheticSmoothScrollGesture::SyntheticSmoothScrollGesture(
     33     const SyntheticSmoothScrollGestureParams& params)
     34     : params_(params),
     35       gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT),
     36       state_(SETUP) {}
     37 
     38 SyntheticSmoothScrollGesture::~SyntheticSmoothScrollGesture() {}
     39 
     40 SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents(
     41     const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
     42   if (state_ == SETUP) {
     43     gesture_source_type_ = params_.gesture_source_type;
     44     if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT)
     45       gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType();
     46 
     47     state_ = STARTED;
     48     current_scroll_segment_ = -1;
     49     current_scroll_segment_stop_time_ = timestamp;
     50   }
     51 
     52   DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT);
     53   if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT)
     54     ForwardTouchInputEvents(timestamp, target);
     55   else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT)
     56     ForwardMouseInputEvents(timestamp, target);
     57   else
     58     return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
     59 
     60   return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED
     61                           : SyntheticGesture::GESTURE_RUNNING;
     62 }
     63 
     64 void SyntheticSmoothScrollGesture::ForwardTouchInputEvents(
     65     const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
     66   base::TimeTicks event_timestamp = timestamp;
     67   switch (state_) {
     68     case STARTED:
     69       if (ScrollIsNoOp()) {
     70         state_ = DONE;
     71         break;
     72       }
     73       AddTouchSlopToFirstDistance(target);
     74       ComputeNextScrollSegment();
     75       current_scroll_segment_start_position_ = params_.anchor;
     76       PressTouchPoint(target, event_timestamp);
     77       state_ = MOVING;
     78       break;
     79     case MOVING: {
     80       event_timestamp = ClampTimestamp(timestamp);
     81       gfx::Vector2dF delta = GetPositionDeltaAtTime(event_timestamp);
     82       MoveTouchPoint(target, delta, event_timestamp);
     83 
     84       if (FinishedCurrentScrollSegment(event_timestamp)) {
     85         if (!IsLastScrollSegment()) {
     86           current_scroll_segment_start_position_ +=
     87               params_.distances[current_scroll_segment_];
     88           ComputeNextScrollSegment();
     89         } else if (params_.prevent_fling) {
     90           state_ = STOPPING;
     91         } else {
     92           ReleaseTouchPoint(target, event_timestamp);
     93           state_ = DONE;
     94         }
     95       }
     96     } break;
     97     case STOPPING:
     98       if (timestamp - current_scroll_segment_stop_time_ >=
     99           target->PointerAssumedStoppedTime()) {
    100         event_timestamp = current_scroll_segment_stop_time_ +
    101                           target->PointerAssumedStoppedTime();
    102         ReleaseTouchPoint(target, event_timestamp);
    103         state_ = DONE;
    104       }
    105       break;
    106     case SETUP:
    107       NOTREACHED()
    108           << "State STARTED invalid for synthetic scroll using touch input.";
    109     case DONE:
    110       NOTREACHED()
    111           << "State DONE invalid for synthetic scroll using touch input.";
    112   }
    113 }
    114 
    115 void SyntheticSmoothScrollGesture::ForwardMouseInputEvents(
    116     const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
    117   switch (state_) {
    118     case STARTED:
    119       if (ScrollIsNoOp()) {
    120         state_ = DONE;
    121         break;
    122       }
    123       ComputeNextScrollSegment();
    124       state_ = MOVING;
    125       // Fall through to forward the first event.
    126     case MOVING: {
    127       // Even though WebMouseWheelEvents take floating point deltas,
    128       // internally the scroll position is stored as an integer. We therefore
    129       // keep track of the discrete delta which is consistent with the
    130       // internal scrolling state. This ensures that when the gesture has
    131       // finished we've scrolled exactly the specified distance.
    132       base::TimeTicks event_timestamp = ClampTimestamp(timestamp);
    133       gfx::Vector2dF current_scroll_segment_total_delta =
    134           GetPositionDeltaAtTime(event_timestamp);
    135       gfx::Vector2d delta_discrete =
    136           FloorTowardZero(current_scroll_segment_total_delta -
    137                           current_scroll_segment_total_delta_discrete_);
    138       ForwardMouseWheelEvent(target, delta_discrete, event_timestamp);
    139       current_scroll_segment_total_delta_discrete_ += delta_discrete;
    140 
    141       if (FinishedCurrentScrollSegment(event_timestamp)) {
    142         if (!IsLastScrollSegment()) {
    143           current_scroll_segment_total_delta_discrete_ = gfx::Vector2d();
    144           ComputeNextScrollSegment();
    145           ForwardMouseInputEvents(timestamp, target);
    146         } else {
    147           state_ = DONE;
    148         }
    149       }
    150     } break;
    151     case SETUP:
    152       NOTREACHED()
    153           << "State STARTED invalid for synthetic scroll using touch input.";
    154     case STOPPING:
    155       NOTREACHED()
    156           << "State STOPPING invalid for synthetic scroll using touch input.";
    157     case DONE:
    158       NOTREACHED()
    159           << "State DONE invalid for synthetic scroll using touch input.";
    160   }
    161 }
    162 
    163 void SyntheticSmoothScrollGesture::ForwardTouchEvent(
    164     SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
    165   touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
    166 
    167   target->DispatchInputEventToPlatform(touch_event_);
    168 }
    169 
    170 void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent(
    171     SyntheticGestureTarget* target,
    172     const gfx::Vector2dF& delta,
    173     const base::TimeTicks& timestamp) const {
    174   blink::WebMouseWheelEvent mouse_wheel_event =
    175       SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false);
    176 
    177   mouse_wheel_event.x = params_.anchor.x();
    178   mouse_wheel_event.y = params_.anchor.y();
    179 
    180   mouse_wheel_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
    181 
    182   target->DispatchInputEventToPlatform(mouse_wheel_event);
    183 }
    184 
    185 void SyntheticSmoothScrollGesture::PressTouchPoint(
    186     SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
    187   DCHECK_EQ(current_scroll_segment_, 0);
    188   touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y());
    189   ForwardTouchEvent(target, timestamp);
    190 }
    191 
    192 void SyntheticSmoothScrollGesture::MoveTouchPoint(
    193     SyntheticGestureTarget* target,
    194     const gfx::Vector2dF& delta,
    195     const base::TimeTicks& timestamp) {
    196   DCHECK_GE(current_scroll_segment_, 0);
    197   DCHECK_LT(current_scroll_segment_,
    198             static_cast<int>(params_.distances.size()));
    199   gfx::PointF touch_position = current_scroll_segment_start_position_ + delta;
    200   touch_event_.MovePoint(0, touch_position.x(), touch_position.y());
    201   ForwardTouchEvent(target, timestamp);
    202 }
    203 
    204 void SyntheticSmoothScrollGesture::ReleaseTouchPoint(
    205     SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
    206   DCHECK_EQ(current_scroll_segment_,
    207             static_cast<int>(params_.distances.size()) - 1);
    208   touch_event_.ReleasePoint(0);
    209   ForwardTouchEvent(target, timestamp);
    210 }
    211 
    212 void SyntheticSmoothScrollGesture::AddTouchSlopToFirstDistance(
    213     SyntheticGestureTarget* target) {
    214   DCHECK_GE(params_.distances.size(), 1ul);
    215   gfx::Vector2d& first_scroll_distance = params_.distances[0];
    216   DCHECK_GT(first_scroll_distance.Length(), 0);
    217   first_scroll_distance += CeilFromZero(ProjectScalarOntoVector(
    218       target->GetTouchSlopInDips(), first_scroll_distance));
    219 }
    220 
    221 gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDeltaAtTime(
    222     const base::TimeTicks& timestamp) const {
    223   // Make sure the final delta is correct. Using the computation below can lead
    224   // to issues with floating point precision.
    225   if (FinishedCurrentScrollSegment(timestamp))
    226     return params_.distances[current_scroll_segment_];
    227 
    228   float delta_length =
    229       params_.speed_in_pixels_s *
    230       (timestamp - current_scroll_segment_start_time_).InSecondsF();
    231   return ProjectScalarOntoVector(delta_length,
    232                                  params_.distances[current_scroll_segment_]);
    233 }
    234 
    235 void SyntheticSmoothScrollGesture::ComputeNextScrollSegment() {
    236   current_scroll_segment_++;
    237   DCHECK_LT(current_scroll_segment_,
    238             static_cast<int>(params_.distances.size()));
    239   int64 total_duration_in_us = static_cast<int64>(
    240       1e6 * (params_.distances[current_scroll_segment_].Length() /
    241              params_.speed_in_pixels_s));
    242   DCHECK_GT(total_duration_in_us, 0);
    243   current_scroll_segment_start_time_ = current_scroll_segment_stop_time_;
    244   current_scroll_segment_stop_time_ =
    245       current_scroll_segment_start_time_ +
    246       base::TimeDelta::FromMicroseconds(total_duration_in_us);
    247 }
    248 
    249 base::TimeTicks SyntheticSmoothScrollGesture::ClampTimestamp(
    250     const base::TimeTicks& timestamp) const {
    251   return std::min(timestamp, current_scroll_segment_stop_time_);
    252 }
    253 
    254 bool SyntheticSmoothScrollGesture::FinishedCurrentScrollSegment(
    255     const base::TimeTicks& timestamp) const {
    256   return timestamp >= current_scroll_segment_stop_time_;
    257 }
    258 
    259 bool SyntheticSmoothScrollGesture::IsLastScrollSegment() const {
    260   DCHECK_LT(current_scroll_segment_,
    261             static_cast<int>(params_.distances.size()));
    262   return current_scroll_segment_ ==
    263          static_cast<int>(params_.distances.size()) - 1;
    264 }
    265 
    266 bool SyntheticSmoothScrollGesture::ScrollIsNoOp() const {
    267   return params_.distances.size() == 0 || params_.distances[0].IsZero();
    268 }
    269 
    270 }  // namespace content
    271