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 if (timestamp == stop_scrolling_time_) |
jdduke (slow)
2014/01/06 18:56:28
Nit: Maybe put a note here about precision issues?
Dominik Grewe
2014/01/06 19:16:43
Done.
| |
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 1e6f * (params_.distance.Length() / params_.speed_in_pixels_s)); | |
231 stop_scrolling_time_ = | |
232 start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us); | |
217 } | 233 } |
218 | 234 |
219 bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance() const { | 235 base::TimeTicks SyntheticSmoothScrollGesture::ClampTimestamp( |
220 return ComputeRemainingDelta().IsZero(); | 236 const base::TimeTicks& timestamp) const { |
237 return timestamp > stop_scrolling_time_ ? stop_scrolling_time_ : timestamp; | |
jdduke (slow)
2014/01/06 18:56:28
Nit: std::min(timestamp, stop_scrolling_time_);
Dominik Grewe
2014/01/06 19:16:43
Done.
| |
238 } | |
239 | |
240 bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance( | |
241 const base::TimeTicks& timestamp) const { | |
242 return timestamp == stop_scrolling_time_; | |
221 } | 243 } |
222 | 244 |
223 } // namespace content | 245 } // namespace content |
OLD | NEW |