| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h" | 5 #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "content/common/input/input_event.h" | 8 #include "content/common/input/input_event.h" |
| 9 #include "ui/events/latency_info.h" | 9 #include "ui/events/latency_info.h" |
| 10 #include "ui/gfx/point_f.h" | 10 #include "ui/gfx/point_f.h" |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 | 28 |
| 29 SyntheticSmoothScrollGesture::SyntheticSmoothScrollGesture( | 29 SyntheticSmoothScrollGesture::SyntheticSmoothScrollGesture( |
| 30 const SyntheticSmoothScrollGestureParams& params) | 30 const SyntheticSmoothScrollGestureParams& params) |
| 31 : params_(params), | 31 : params_(params), |
| 32 gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT), | 32 gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT), |
| 33 state_(SETUP) {} | 33 state_(SETUP) {} |
| 34 | 34 |
| 35 SyntheticSmoothScrollGesture::~SyntheticSmoothScrollGesture() {} | 35 SyntheticSmoothScrollGesture::~SyntheticSmoothScrollGesture() {} |
| 36 | 36 |
| 37 SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents( | 37 SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents( |
| 38 const base::TimeDelta& interval, SyntheticGestureTarget* target) { | 38 const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { |
| 39 if (state_ == SETUP) { | 39 if (state_ == SETUP) { |
| 40 gesture_source_type_ = params_.gesture_source_type; | 40 gesture_source_type_ = params_.gesture_source_type; |
| 41 if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) | 41 if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) |
| 42 gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType(); | 42 gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType(); |
| 43 | 43 |
| 44 if (!target->SupportsSyntheticGestureSourceType(gesture_source_type_)) | 44 if (!target->SupportsSyntheticGestureSourceType(gesture_source_type_)) |
| 45 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM; | 45 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM; |
| 46 | 46 |
| 47 state_ = STARTED; | 47 state_ = STARTED; |
| 48 start_time_ = timestamp; |
| 48 } | 49 } |
| 49 | 50 |
| 50 DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); | 51 DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); |
| 51 if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) | 52 if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) |
| 52 ForwardTouchInputEvents(interval, target); | 53 ForwardTouchInputEvents(timestamp, target); |
| 53 else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) | 54 else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) |
| 54 ForwardMouseInputEvents(interval, target); | 55 ForwardMouseInputEvents(timestamp, target); |
| 55 else | 56 else |
| 56 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; | 57 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; |
| 57 | 58 |
| 58 return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED | 59 return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED |
| 59 : SyntheticGesture::GESTURE_RUNNING; | 60 : SyntheticGesture::GESTURE_RUNNING; |
| 60 } | 61 } |
| 61 | 62 |
| 62 void SyntheticSmoothScrollGesture::ForwardTouchInputEvents( | 63 void SyntheticSmoothScrollGesture::ForwardTouchInputEvents( |
| 63 const base::TimeDelta& interval, SyntheticGestureTarget* target) { | 64 const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { |
| 65 base::TimeTicks event_timestamp = timestamp; |
| 64 switch (state_) { | 66 switch (state_) { |
| 65 case STARTED: | 67 case STARTED: |
| 66 // Check for an early finish. | 68 // Check for an early finish. |
| 67 if (params_.distance.IsZero()) { | 69 if (params_.distance.IsZero()) { |
| 68 state_ = DONE; | 70 state_ = DONE; |
| 69 break; | 71 break; |
| 70 } | 72 } |
| 71 AddTouchSlopToDistance(target); | 73 AddTouchSlopToDistance(target); |
| 72 PressTouchPoint(target); | 74 ComputeAndSetStopScrollingTime(); |
| 75 PressTouchPoint(target, event_timestamp); |
| 73 state_ = MOVING; | 76 state_ = MOVING; |
| 74 break; | 77 break; |
| 75 case MOVING: | 78 case MOVING: { |
| 76 total_delta_ += GetPositionDelta(interval); | 79 event_timestamp = ClampTimestamp(timestamp); |
| 77 MoveTouchPoint(target); | 80 gfx::Vector2dF delta = GetPositionDeltaAtTime(event_timestamp); |
| 81 MoveTouchPoint(target, delta, event_timestamp); |
| 78 | 82 |
| 79 if (HasScrolledEntireDistance()) { | 83 if (HasScrolledEntireDistance(event_timestamp)) { |
| 80 if (params_.prevent_fling) { | 84 if (params_.prevent_fling) { |
| 81 state_ = STOPPING; | 85 state_ = STOPPING; |
| 82 } else { | 86 } else { |
| 83 ReleaseTouchPoint(target); | 87 ReleaseTouchPoint(target, event_timestamp); |
| 84 state_ = DONE; | 88 state_ = DONE; |
| 85 } | 89 } |
| 86 } | 90 } |
| 87 break; | 91 } break; |
| 88 case STOPPING: | 92 case STOPPING: |
| 89 total_stopping_wait_time_ += interval; | 93 if (timestamp - stop_scrolling_time_ >= |
| 90 if (total_stopping_wait_time_ >= target->PointerAssumedStoppedTime()) { | 94 target->PointerAssumedStoppedTime()) { |
| 95 event_timestamp = |
| 96 stop_scrolling_time_ + target->PointerAssumedStoppedTime(); |
| 91 // Send one last move event, but don't change the location. Without this | 97 // Send one last move event, but don't change the location. Without this |
| 92 // we'd still sometimes cause a fling on Android. | 98 // we'd still sometimes cause a fling on Android. |
| 93 MoveTouchPoint(target); | 99 ForwardTouchEvent(target, event_timestamp); |
| 94 ReleaseTouchPoint(target); | 100 ReleaseTouchPoint(target, event_timestamp); |
| 95 state_ = DONE; | 101 state_ = DONE; |
| 96 } | 102 } |
| 97 break; | 103 break; |
| 98 case SETUP: | 104 case SETUP: |
| 99 NOTREACHED() | 105 NOTREACHED() |
| 100 << "State STARTED invalid for synthetic scroll using touch input."; | 106 << "State STARTED invalid for synthetic scroll using touch input."; |
| 101 case DONE: | 107 case DONE: |
| 102 NOTREACHED() | 108 NOTREACHED() |
| 103 << "State DONE invalid for synthetic scroll using touch input."; | 109 << "State DONE invalid for synthetic scroll using touch input."; |
| 104 } | 110 } |
| 105 } | 111 } |
| 106 | 112 |
| 107 void SyntheticSmoothScrollGesture::ForwardMouseInputEvents( | 113 void SyntheticSmoothScrollGesture::ForwardMouseInputEvents( |
| 108 const base::TimeDelta& interval, SyntheticGestureTarget* target) { | 114 const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { |
| 109 switch (state_) { | 115 switch (state_) { |
| 110 case STARTED: | 116 case STARTED: |
| 111 // Check for an early finish. | 117 // Check for an early finish. |
| 112 if (params_.distance.IsZero()) { | 118 if (params_.distance.IsZero()) { |
| 113 state_ = DONE; | 119 state_ = DONE; |
| 114 break; | 120 break; |
| 115 } | 121 } |
| 122 ComputeAndSetStopScrollingTime(); |
| 116 state_ = MOVING; | 123 state_ = MOVING; |
| 117 // Fall through to forward the first event. | 124 // Fall through to forward the first event. |
| 118 case MOVING: | 125 case MOVING: { |
| 119 { | 126 // Even though WebMouseWheelEvents take floating point deltas, |
| 120 // Even though WebMouseWheelEvents take floating point deltas, | 127 // internally the scroll position is stored as an integer. We therefore |
| 121 // internally the scroll position is stored as an integer. We therefore | 128 // keep track of the discrete delta which is consistent with the |
| 122 // keep track of the discrete delta which is consistent with the | 129 // internal scrolling state. This ensures that when the gesture has |
| 123 // internal scrolling state. This ensures that when the gesture has | 130 // finished we've scrolled exactly the specified distance. |
| 124 // finished we've scrolled exactly the specified distance. | 131 base::TimeTicks event_timestamp = ClampTimestamp(timestamp); |
| 125 total_delta_ += GetPositionDelta(interval); | 132 gfx::Vector2dF total_delta = GetPositionDeltaAtTime(event_timestamp); |
| 126 gfx::Vector2d delta_discrete = | 133 gfx::Vector2d delta_discrete = |
| 127 FloorTowardZero(total_delta_ - total_delta_discrete_); | 134 FloorTowardZero(total_delta - total_delta_discrete_); |
| 128 ForwardMouseWheelEvent(target, delta_discrete); | 135 ForwardMouseWheelEvent(target, delta_discrete, event_timestamp); |
| 129 total_delta_discrete_ += delta_discrete; | 136 total_delta_discrete_ += delta_discrete; |
| 130 } | 137 |
| 131 if (HasScrolledEntireDistance()) | 138 if (HasScrolledEntireDistance(event_timestamp)) |
| 132 state_ = DONE; | 139 state_ = DONE; |
| 133 break; | 140 } break; |
| 134 case SETUP: | 141 case SETUP: |
| 135 NOTREACHED() | 142 NOTREACHED() |
| 136 << "State STARTED invalid for synthetic scroll using touch input."; | 143 << "State STARTED invalid for synthetic scroll using touch input."; |
| 137 case STOPPING: | 144 case STOPPING: |
| 138 NOTREACHED() | 145 NOTREACHED() |
| 139 << "State STOPPING invalid for synthetic scroll using touch input."; | 146 << "State STOPPING invalid for synthetic scroll using touch input."; |
| 140 case DONE: | 147 case DONE: |
| 141 NOTREACHED() | 148 NOTREACHED() |
| 142 << "State DONE invalid for synthetic scroll using touch input."; | 149 << "State DONE invalid for synthetic scroll using touch input."; |
| 143 } | 150 } |
| 144 } | 151 } |
| 145 | 152 |
| 146 void SyntheticSmoothScrollGesture::ForwardTouchEvent( | 153 void SyntheticSmoothScrollGesture::ForwardTouchEvent( |
| 147 SyntheticGestureTarget* target) const { | 154 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
| 155 touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp); |
| 156 |
| 148 target->DispatchInputEventToPlatform( | 157 target->DispatchInputEventToPlatform( |
| 149 InputEvent(touch_event_, ui::LatencyInfo(), false)); | 158 InputEvent(touch_event_, ui::LatencyInfo(), false)); |
| 150 } | 159 } |
| 151 | 160 |
| 152 void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent( | 161 void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent( |
| 153 SyntheticGestureTarget* target, const gfx::Vector2dF& delta) const { | 162 SyntheticGestureTarget* target, |
| 163 const gfx::Vector2dF& delta, |
| 164 const base::TimeTicks& timestamp) const { |
| 154 blink::WebMouseWheelEvent mouse_wheel_event = | 165 blink::WebMouseWheelEvent mouse_wheel_event = |
| 155 SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false); | 166 SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false); |
| 156 | 167 |
| 157 mouse_wheel_event.x = params_.anchor.x(); | 168 mouse_wheel_event.x = params_.anchor.x(); |
| 158 mouse_wheel_event.y = params_.anchor.y(); | 169 mouse_wheel_event.y = params_.anchor.y(); |
| 159 | 170 |
| 171 mouse_wheel_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp); |
| 172 |
| 160 target->DispatchInputEventToPlatform( | 173 target->DispatchInputEventToPlatform( |
| 161 InputEvent(mouse_wheel_event, ui::LatencyInfo(), false)); | 174 InputEvent(mouse_wheel_event, ui::LatencyInfo(), false)); |
| 162 } | 175 } |
| 163 | 176 |
| 164 void SyntheticSmoothScrollGesture::PressTouchPoint( | 177 void SyntheticSmoothScrollGesture::PressTouchPoint( |
| 165 SyntheticGestureTarget* target) { | 178 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
| 166 touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y()); | 179 touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y()); |
| 167 ForwardTouchEvent(target); | 180 ForwardTouchEvent(target, timestamp); |
| 168 } | 181 } |
| 169 | 182 |
| 170 void SyntheticSmoothScrollGesture::MoveTouchPoint( | 183 void SyntheticSmoothScrollGesture::MoveTouchPoint( |
| 171 SyntheticGestureTarget* target) { | 184 SyntheticGestureTarget* target, |
| 172 gfx::PointF touch_position = params_.anchor + total_delta_; | 185 const gfx::Vector2dF& delta, |
| 186 const base::TimeTicks& timestamp) { |
| 187 gfx::PointF touch_position = params_.anchor + delta; |
| 173 touch_event_.MovePoint(0, touch_position.x(), touch_position.y()); | 188 touch_event_.MovePoint(0, touch_position.x(), touch_position.y()); |
| 174 ForwardTouchEvent(target); | 189 ForwardTouchEvent(target, timestamp); |
| 175 } | 190 } |
| 176 | 191 |
| 177 void SyntheticSmoothScrollGesture::ReleaseTouchPoint( | 192 void SyntheticSmoothScrollGesture::ReleaseTouchPoint( |
| 178 SyntheticGestureTarget* target) { | 193 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
| 179 touch_event_.ReleasePoint(0); | 194 touch_event_.ReleasePoint(0); |
| 180 ForwardTouchEvent(target); | 195 ForwardTouchEvent(target, timestamp); |
| 181 } | 196 } |
| 182 | 197 |
| 183 void SyntheticSmoothScrollGesture::AddTouchSlopToDistance( | 198 void SyntheticSmoothScrollGesture::AddTouchSlopToDistance( |
| 184 SyntheticGestureTarget* target) { | 199 SyntheticGestureTarget* target) { |
| 185 // Android uses euclidean distance to compute if a touch pointer has moved | 200 // Android uses euclidean distance to compute if a touch pointer has moved |
| 186 // beyond the slop, while Aura uses Manhattan distance. We're using Euclidean | 201 // beyond the slop, while Aura uses Manhattan distance. We're using Euclidean |
| 187 // distance and round up to the nearest integer. | 202 // distance and round up to the nearest integer. |
| 188 // For vertical and horizontal scrolls (the common case), both methods produce | 203 // For vertical and horizontal scrolls (the common case), both methods produce |
| 189 // the same result. | 204 // the same result. |
| 190 gfx::Vector2dF touch_slop_delta = ProjectLengthOntoScrollDirection( | 205 gfx::Vector2dF touch_slop_delta = |
| 191 target->GetTouchSlopInDips()); | 206 ProjectLengthOntoScrollDirection(target->GetTouchSlopInDips()); |
| 192 params_.distance += CeilFromZero(touch_slop_delta); | 207 params_.distance += CeilFromZero(touch_slop_delta); |
| 193 } | 208 } |
| 194 | 209 |
| 195 gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDelta( | 210 gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDeltaAtTime( |
| 196 const base::TimeDelta& interval) const { | 211 const base::TimeTicks& timestamp) const { |
| 197 float delta_length = params_.speed_in_pixels_s * interval.InSecondsF(); | 212 // Make sure the final delta is correct. Using the computation below can lead |
| 213 // to issues with floating point precision. |
| 214 if (HasScrolledEntireDistance(timestamp)) |
| 215 return -params_.distance; |
| 198 | 216 |
| 199 // Make sure we're not scrolling too far. | 217 float delta_length = |
| 200 gfx::Vector2dF remaining_delta = ComputeRemainingDelta(); | 218 params_.speed_in_pixels_s * (timestamp - start_time_).InSecondsF(); |
| 201 if (delta_length > remaining_delta.Length()) | 219 return -ProjectLengthOntoScrollDirection(delta_length); |
| 202 // In order to scroll in a certain direction we need to move the | |
| 203 // touch pointer/mouse wheel in the opposite direction. | |
| 204 return -remaining_delta; | |
| 205 else | |
| 206 return -ProjectLengthOntoScrollDirection(delta_length); | |
| 207 } | 220 } |
| 208 | 221 |
| 209 gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection( | 222 gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection( |
| 210 float delta_length) const { | 223 float delta_length) const { |
| 211 const float kTotalLength = params_.distance.Length(); | 224 const float kTotalLength = params_.distance.Length(); |
| 212 return ScaleVector2d(params_.distance, delta_length / kTotalLength); | 225 return ScaleVector2d(params_.distance, delta_length / kTotalLength); |
| 213 } | 226 } |
| 214 | 227 |
| 215 gfx::Vector2dF SyntheticSmoothScrollGesture::ComputeRemainingDelta() const { | 228 void SyntheticSmoothScrollGesture::ComputeAndSetStopScrollingTime() { |
| 216 return params_.distance + total_delta_; | 229 int64 total_duration_in_us = static_cast<int64>( |
| 230 1e6 * (params_.distance.Length() / params_.speed_in_pixels_s)); |
| 231 DCHECK_GT(total_duration_in_us, 0); |
| 232 stop_scrolling_time_ = |
| 233 start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us); |
| 217 } | 234 } |
| 218 | 235 |
| 219 bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance() const { | 236 base::TimeTicks SyntheticSmoothScrollGesture::ClampTimestamp( |
| 220 return ComputeRemainingDelta().IsZero(); | 237 const base::TimeTicks& timestamp) const { |
| 238 return std::min(timestamp, stop_scrolling_time_); |
| 239 } |
| 240 |
| 241 bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance( |
| 242 const base::TimeTicks& timestamp) const { |
| 243 return timestamp >= stop_scrolling_time_; |
| 221 } | 244 } |
| 222 | 245 |
| 223 } // namespace content | 246 } // namespace content |
| OLD | NEW |