Chromium Code Reviews| 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 = |
| 81 GetPositionDeltaAtTime(event_timestamp); | |
| 82 MoveTouchPoint(target, delta, event_timestamp); | |
| 78 | 83 |
| 79 if (HasScrolledEntireDistance()) { | 84 if (HasScrolledEntireDistance(event_timestamp)) { |
| 80 if (params_.prevent_fling) { | 85 if (params_.prevent_fling) { |
| 81 state_ = STOPPING; | 86 state_ = STOPPING; |
| 82 } else { | 87 } else { |
| 83 ReleaseTouchPoint(target); | 88 ReleaseTouchPoint(target, event_timestamp); |
| 84 state_ = DONE; | 89 state_ = DONE; |
| 85 } | 90 } |
| 86 } | 91 } |
| 87 break; | 92 } break; |
| 88 case STOPPING: | 93 case STOPPING: |
| 89 total_stopping_wait_time_ += interval; | 94 if (timestamp - stop_scrolling_time_ >= |
| 90 if (total_stopping_wait_time_ >= target->PointerAssumedStoppedTime()) { | 95 target->PointerAssumedStoppedTime()) { |
| 96 event_timestamp = | |
| 97 stop_scrolling_time_ + target->PointerAssumedStoppedTime(); | |
| 91 // Send one last move event, but don't change the location. Without this | 98 // Send one last move event, but don't change the location. Without this |
| 92 // we'd still sometimes cause a fling on Android. | 99 // we'd still sometimes cause a fling on Android. |
| 93 MoveTouchPoint(target); | 100 ForwardTouchEvent(target, event_timestamp); |
| 94 ReleaseTouchPoint(target); | 101 ReleaseTouchPoint(target, event_timestamp); |
| 95 state_ = DONE; | 102 state_ = DONE; |
| 96 } | 103 } |
| 97 break; | 104 break; |
| 98 case SETUP: | 105 case SETUP: |
| 99 NOTREACHED() | 106 NOTREACHED() |
| 100 << "State STARTED invalid for synthetic scroll using touch input."; | 107 << "State STARTED invalid for synthetic scroll using touch input."; |
| 101 case DONE: | 108 case DONE: |
| 102 NOTREACHED() | 109 NOTREACHED() |
| 103 << "State DONE invalid for synthetic scroll using touch input."; | 110 << "State DONE invalid for synthetic scroll using touch input."; |
| 104 } | 111 } |
| 105 } | 112 } |
| 106 | 113 |
| 107 void SyntheticSmoothScrollGesture::ForwardMouseInputEvents( | 114 void SyntheticSmoothScrollGesture::ForwardMouseInputEvents( |
| 108 const base::TimeDelta& interval, SyntheticGestureTarget* target) { | 115 const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { |
| 109 switch (state_) { | 116 switch (state_) { |
| 110 case STARTED: | 117 case STARTED: |
| 111 // Check for an early finish. | 118 // Check for an early finish. |
| 112 if (params_.distance.IsZero()) { | 119 if (params_.distance.IsZero()) { |
| 113 state_ = DONE; | 120 state_ = DONE; |
| 114 break; | 121 break; |
| 115 } | 122 } |
| 123 ComputeAndSetStopScrollingTime(); | |
| 116 state_ = MOVING; | 124 state_ = MOVING; |
| 117 // Fall through to forward the first event. | 125 // Fall through to forward the first event. |
| 118 case MOVING: | 126 case MOVING: { |
| 119 { | 127 // Even though WebMouseWheelEvents take floating point deltas, |
| 120 // Even though WebMouseWheelEvents take floating point deltas, | 128 // internally the scroll position is stored as an integer. We therefore |
| 121 // internally the scroll position is stored as an integer. We therefore | 129 // keep track of the discrete delta which is consistent with the |
| 122 // keep track of the discrete delta which is consistent with the | 130 // internal scrolling state. This ensures that when the gesture has |
| 123 // internal scrolling state. This ensures that when the gesture has | 131 // finished we've scrolled exactly the specified distance. |
| 124 // finished we've scrolled exactly the specified distance. | 132 base::TimeTicks event_timestamp = ClampTimestamp(timestamp); |
| 125 total_delta_ += GetPositionDelta(interval); | 133 gfx::Vector2dF total_delta = |
| 126 gfx::Vector2d delta_discrete = | 134 GetPositionDeltaAtTime(event_timestamp); |
| 127 FloorTowardZero(total_delta_ - total_delta_discrete_); | 135 gfx::Vector2d delta_discrete = |
| 128 ForwardMouseWheelEvent(target, delta_discrete); | 136 FloorTowardZero(total_delta - total_delta_discrete_); |
| 129 total_delta_discrete_ += delta_discrete; | 137 ForwardMouseWheelEvent(target, delta_discrete, event_timestamp); |
| 130 } | 138 total_delta_discrete_ += delta_discrete; |
| 131 if (HasScrolledEntireDistance()) | 139 |
| 140 if (HasScrolledEntireDistance(event_timestamp)) | |
| 132 state_ = DONE; | 141 state_ = DONE; |
| 133 break; | 142 } break; |
| 134 case SETUP: | 143 case SETUP: |
| 135 NOTREACHED() | 144 NOTREACHED() |
| 136 << "State STARTED invalid for synthetic scroll using touch input."; | 145 << "State STARTED invalid for synthetic scroll using touch input."; |
| 137 case STOPPING: | 146 case STOPPING: |
| 138 NOTREACHED() | 147 NOTREACHED() |
| 139 << "State STOPPING invalid for synthetic scroll using touch input."; | 148 << "State STOPPING invalid for synthetic scroll using touch input."; |
| 140 case DONE: | 149 case DONE: |
| 141 NOTREACHED() | 150 NOTREACHED() |
| 142 << "State DONE invalid for synthetic scroll using touch input."; | 151 << "State DONE invalid for synthetic scroll using touch input."; |
| 143 } | 152 } |
| 144 } | 153 } |
| 145 | 154 |
| 146 void SyntheticSmoothScrollGesture::ForwardTouchEvent( | 155 void SyntheticSmoothScrollGesture::ForwardTouchEvent( |
| 147 SyntheticGestureTarget* target) const { | 156 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
| 157 touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp); | |
| 158 | |
| 148 target->DispatchInputEventToPlatform( | 159 target->DispatchInputEventToPlatform( |
| 149 InputEvent(touch_event_, ui::LatencyInfo(), false)); | 160 InputEvent(touch_event_, ui::LatencyInfo(), false)); |
| 150 } | 161 } |
| 151 | 162 |
| 152 void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent( | 163 void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent( |
| 153 SyntheticGestureTarget* target, const gfx::Vector2dF& delta) const { | 164 SyntheticGestureTarget* target, |
| 165 const gfx::Vector2dF& delta, | |
| 166 const base::TimeTicks& timestamp) const { | |
| 154 blink::WebMouseWheelEvent mouse_wheel_event = | 167 blink::WebMouseWheelEvent mouse_wheel_event = |
| 155 SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false); | 168 SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false); |
| 156 | 169 |
| 157 mouse_wheel_event.x = params_.anchor.x(); | 170 mouse_wheel_event.x = params_.anchor.x(); |
| 158 mouse_wheel_event.y = params_.anchor.y(); | 171 mouse_wheel_event.y = params_.anchor.y(); |
| 159 | 172 |
| 173 mouse_wheel_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp); | |
| 174 | |
| 160 target->DispatchInputEventToPlatform( | 175 target->DispatchInputEventToPlatform( |
| 161 InputEvent(mouse_wheel_event, ui::LatencyInfo(), false)); | 176 InputEvent(mouse_wheel_event, ui::LatencyInfo(), false)); |
| 162 } | 177 } |
| 163 | 178 |
| 164 void SyntheticSmoothScrollGesture::PressTouchPoint( | 179 void SyntheticSmoothScrollGesture::PressTouchPoint( |
| 165 SyntheticGestureTarget* target) { | 180 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
| 166 touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y()); | 181 touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y()); |
| 167 ForwardTouchEvent(target); | 182 ForwardTouchEvent(target, timestamp); |
| 168 } | 183 } |
| 169 | 184 |
| 170 void SyntheticSmoothScrollGesture::MoveTouchPoint( | 185 void SyntheticSmoothScrollGesture::MoveTouchPoint( |
| 171 SyntheticGestureTarget* target) { | 186 SyntheticGestureTarget* target, |
| 172 gfx::PointF touch_position = params_.anchor + total_delta_; | 187 const gfx::Vector2dF& delta, |
| 188 const base::TimeTicks& timestamp) { | |
| 189 gfx::PointF touch_position = params_.anchor + delta; | |
| 173 touch_event_.MovePoint(0, touch_position.x(), touch_position.y()); | 190 touch_event_.MovePoint(0, touch_position.x(), touch_position.y()); |
| 174 ForwardTouchEvent(target); | 191 ForwardTouchEvent(target, timestamp); |
| 175 } | 192 } |
| 176 | 193 |
| 177 void SyntheticSmoothScrollGesture::ReleaseTouchPoint( | 194 void SyntheticSmoothScrollGesture::ReleaseTouchPoint( |
| 178 SyntheticGestureTarget* target) { | 195 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
| 179 touch_event_.ReleasePoint(0); | 196 touch_event_.ReleasePoint(0); |
| 180 ForwardTouchEvent(target); | 197 ForwardTouchEvent(target, timestamp); |
| 181 } | 198 } |
| 182 | 199 |
| 183 void SyntheticSmoothScrollGesture::AddTouchSlopToDistance( | 200 void SyntheticSmoothScrollGesture::AddTouchSlopToDistance( |
| 184 SyntheticGestureTarget* target) { | 201 SyntheticGestureTarget* target) { |
| 185 // Android uses euclidean distance to compute if a touch pointer has moved | 202 // 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 | 203 // beyond the slop, while Aura uses Manhattan distance. We're using Euclidean |
| 187 // distance and round up to the nearest integer. | 204 // distance and round up to the nearest integer. |
| 188 // For vertical and horizontal scrolls (the common case), both methods produce | 205 // For vertical and horizontal scrolls (the common case), both methods produce |
| 189 // the same result. | 206 // the same result. |
| 190 gfx::Vector2dF touch_slop_delta = ProjectLengthOntoScrollDirection( | 207 gfx::Vector2dF touch_slop_delta = |
| 191 target->GetTouchSlopInDips()); | 208 ProjectLengthOntoScrollDirection(target->GetTouchSlopInDips()); |
| 192 params_.distance += CeilFromZero(touch_slop_delta); | 209 params_.distance += CeilFromZero(touch_slop_delta); |
| 193 } | 210 } |
| 194 | 211 |
| 195 gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDelta( | 212 gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDeltaAtTime( |
| 196 const base::TimeDelta& interval) const { | 213 const base::TimeTicks& timestamp) const { |
| 197 float delta_length = params_.speed_in_pixels_s * interval.InSecondsF(); | 214 // Make sure the final delta is correct. Using the computation below can lead |
| 215 // to issues with floating point precision. | |
| 216 if (timestamp == stop_scrolling_time_) | |
|
jdduke (slow)
2014/01/06 21:41:14
Let's re-use the check below:
HasScrolledEntireDi
| |
| 217 return -params_.distance; | |
| 198 | 218 |
| 199 // Make sure we're not scrolling too far. | 219 float delta_length = |
| 200 gfx::Vector2dF remaining_delta = ComputeRemainingDelta(); | 220 params_.speed_in_pixels_s * (timestamp - start_time_).InSecondsF(); |
| 201 if (delta_length > remaining_delta.Length()) | 221 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 } | 222 } |
| 208 | 223 |
| 209 gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection( | 224 gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection( |
| 210 float delta_length) const { | 225 float delta_length) const { |
| 211 const float kTotalLength = params_.distance.Length(); | 226 const float kTotalLength = params_.distance.Length(); |
| 212 return ScaleVector2d(params_.distance, delta_length / kTotalLength); | 227 return ScaleVector2d(params_.distance, delta_length / kTotalLength); |
| 213 } | 228 } |
| 214 | 229 |
| 215 gfx::Vector2dF SyntheticSmoothScrollGesture::ComputeRemainingDelta() const { | 230 void SyntheticSmoothScrollGesture::ComputeAndSetStopScrollingTime() { |
| 216 return params_.distance + total_delta_; | 231 int64 total_duration_in_us = static_cast<int64>( |
| 232 1e6 * (params_.distance.Length() / params_.speed_in_pixels_s)); | |
| 233 stop_scrolling_time_ = | |
| 234 start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us); | |
| 217 } | 235 } |
| 218 | 236 |
| 219 bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance() const { | 237 base::TimeTicks SyntheticSmoothScrollGesture::ClampTimestamp( |
| 220 return ComputeRemainingDelta().IsZero(); | 238 const base::TimeTicks& timestamp) const { |
| 239 return std::min(timestamp, stop_scrolling_time_); | |
| 240 } | |
| 241 | |
| 242 bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance( | |
| 243 const base::TimeTicks& timestamp) const { | |
| 244 return timestamp == stop_scrolling_time_; | |
|
jdduke (slow)
2014/01/06 21:41:14
Let's make this general with
return timestamp >=
| |
| 221 } | 245 } |
| 222 | 246 |
| 223 } // namespace content | 247 } // namespace content |
| OLD | NEW |