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 |