Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / renderer_host / input / synthetic_smooth_move_gesture.cc
blob80f3ef76c662e54ee6dc94e17dc91a88d426b153
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.
5 #include "content/browser/renderer_host/input/synthetic_smooth_move_gesture.h"
7 #include "base/logging.h"
8 #include "ui/gfx/geometry/point_f.h"
10 namespace content {
11 namespace {
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);
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);
25 gfx::Vector2dF ProjectScalarOntoVector(float scalar,
26 const gfx::Vector2dF& vector) {
27 return gfx::ScaleVector2d(vector, scalar / vector.Length());
30 const int kDefaultSpeedInPixelsPerSec = 800;
32 } // namespace
34 SyntheticSmoothMoveGestureParams::SyntheticSmoothMoveGestureParams()
35 : speed_in_pixels_s(kDefaultSpeedInPixelsPerSec),
36 prevent_fling(true),
37 add_slop(true) {}
39 SyntheticSmoothMoveGestureParams::~SyntheticSmoothMoveGestureParams() {}
41 SyntheticSmoothMoveGesture::SyntheticSmoothMoveGesture(
42 SyntheticSmoothMoveGestureParams params)
43 : params_(params),
44 current_move_segment_start_position_(params.start_point),
45 state_(SETUP) {
48 SyntheticSmoothMoveGesture::~SyntheticSmoothMoveGesture() {}
50 SyntheticGesture::Result SyntheticSmoothMoveGesture::ForwardInputEvents(
51 const base::TimeTicks& timestamp,
52 SyntheticGestureTarget* target) {
53 if (state_ == SETUP) {
54 state_ = STARTED;
55 current_move_segment_ = -1;
56 current_move_segment_stop_time_ = timestamp;
59 switch (params_.input_type) {
60 case SyntheticSmoothMoveGestureParams::TOUCH_INPUT:
61 ForwardTouchInputEvents(timestamp, target);
62 break;
63 case SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT:
64 ForwardMouseClickInputEvents(timestamp, target);
65 break;
66 case SyntheticSmoothMoveGestureParams::MOUSE_WHEEL_INPUT:
67 ForwardMouseWheelInputEvents(timestamp, target);
68 break;
69 default:
70 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
72 return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED
73 : SyntheticGesture::GESTURE_RUNNING;
76 // TODO(ssid): Clean up the switch statements by adding functions instead of
77 // large code, in the Forward*Events functions. Move the actions for all input
78 // types to different class (SyntheticInputDevice) which generates input events
79 // for all input types. The gesture class can use instance of device actions.
80 // Refer: crbug.com/461825
82 void SyntheticSmoothMoveGesture::ForwardTouchInputEvents(
83 const base::TimeTicks& timestamp,
84 SyntheticGestureTarget* target) {
85 base::TimeTicks event_timestamp = timestamp;
86 switch (state_) {
87 case STARTED:
88 if (MoveIsNoOp()) {
89 state_ = DONE;
90 break;
92 if (params_.add_slop)
93 AddTouchSlopToFirstDistance(target);
94 ComputeNextMoveSegment();
95 PressTouchPoint(target, event_timestamp);
96 state_ = MOVING;
97 break;
98 case MOVING: {
99 event_timestamp = ClampTimestamp(timestamp);
100 gfx::Vector2dF delta = GetPositionDeltaAtTime(event_timestamp);
101 MoveTouchPoint(target, delta, event_timestamp);
103 if (FinishedCurrentMoveSegment(event_timestamp)) {
104 if (!IsLastMoveSegment()) {
105 current_move_segment_start_position_ +=
106 params_.distances[current_move_segment_];
107 ComputeNextMoveSegment();
108 } else if (params_.prevent_fling) {
109 state_ = STOPPING;
110 } else {
111 ReleaseTouchPoint(target, event_timestamp);
112 state_ = DONE;
115 } break;
116 case STOPPING:
117 if (timestamp - current_move_segment_stop_time_ >=
118 target->PointerAssumedStoppedTime()) {
119 event_timestamp = current_move_segment_stop_time_ +
120 target->PointerAssumedStoppedTime();
121 ReleaseTouchPoint(target, event_timestamp);
122 state_ = DONE;
124 break;
125 case SETUP:
126 NOTREACHED()
127 << "State SETUP invalid for synthetic scroll using touch input.";
128 case DONE:
129 NOTREACHED()
130 << "State DONE invalid for synthetic scroll using touch input.";
134 void SyntheticSmoothMoveGesture::ForwardMouseWheelInputEvents(
135 const base::TimeTicks& timestamp,
136 SyntheticGestureTarget* target) {
137 switch (state_) {
138 case STARTED:
139 if (MoveIsNoOp()) {
140 state_ = DONE;
141 break;
143 ComputeNextMoveSegment();
144 state_ = MOVING;
145 // Fall through to forward the first event.
146 case MOVING: {
147 // Even though WebMouseWheelEvents take floating point deltas,
148 // internally the scroll position is stored as an integer. We therefore
149 // keep track of the discrete delta which is consistent with the
150 // internal scrolling state. This ensures that when the gesture has
151 // finished we've scrolled exactly the specified distance.
152 base::TimeTicks event_timestamp = ClampTimestamp(timestamp);
153 gfx::Vector2dF current_move_segment_total_delta =
154 GetPositionDeltaAtTime(event_timestamp);
155 gfx::Vector2d delta_discrete =
156 FloorTowardZero(current_move_segment_total_delta -
157 current_move_segment_total_delta_discrete_);
158 ForwardMouseWheelEvent(target, delta_discrete, event_timestamp);
159 current_move_segment_total_delta_discrete_ += delta_discrete;
161 if (FinishedCurrentMoveSegment(event_timestamp)) {
162 if (!IsLastMoveSegment()) {
163 current_move_segment_total_delta_discrete_ = gfx::Vector2d();
164 ComputeNextMoveSegment();
165 ForwardMouseWheelInputEvents(timestamp, target);
166 } else {
167 state_ = DONE;
170 } break;
171 case SETUP:
172 NOTREACHED() << "State SETUP invalid for synthetic scroll using mouse "
173 "wheel input.";
174 case STOPPING:
175 NOTREACHED() << "State STOPPING invalid for synthetic scroll using mouse "
176 "wheel input.";
177 case DONE:
178 NOTREACHED()
179 << "State DONE invalid for synthetic scroll using mouse wheel input.";
183 void SyntheticSmoothMoveGesture::ForwardMouseClickInputEvents(
184 const base::TimeTicks& timestamp,
185 SyntheticGestureTarget* target) {
186 base::TimeTicks event_timestamp = timestamp;
187 switch (state_) {
188 case STARTED:
189 if (MoveIsNoOp()) {
190 state_ = DONE;
191 break;
193 ComputeNextMoveSegment();
194 PressMousePoint(target, event_timestamp);
195 state_ = MOVING;
196 break;
197 case MOVING: {
198 base::TimeTicks event_timestamp = ClampTimestamp(timestamp);
199 gfx::Vector2dF delta = GetPositionDeltaAtTime(event_timestamp);
200 MoveMousePoint(target, delta, event_timestamp);
202 if (FinishedCurrentMoveSegment(event_timestamp)) {
203 if (!IsLastMoveSegment()) {
204 current_move_segment_start_position_ +=
205 params_.distances[current_move_segment_];
206 ComputeNextMoveSegment();
207 } else {
208 ReleaseMousePoint(target, event_timestamp);
209 state_ = DONE;
212 } break;
213 case STOPPING:
214 NOTREACHED()
215 << "State STOPPING invalid for synthetic drag using mouse input.";
216 case SETUP:
217 NOTREACHED()
218 << "State SETUP invalid for synthetic drag using mouse input.";
219 case DONE:
220 NOTREACHED()
221 << "State DONE invalid for synthetic drag using mouse input.";
225 void SyntheticSmoothMoveGesture::ForwardTouchEvent(
226 SyntheticGestureTarget* target,
227 const base::TimeTicks& timestamp) {
228 touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
230 target->DispatchInputEventToPlatform(touch_event_);
233 void SyntheticSmoothMoveGesture::ForwardMouseWheelEvent(
234 SyntheticGestureTarget* target,
235 const gfx::Vector2dF& delta,
236 const base::TimeTicks& timestamp) const {
237 blink::WebMouseWheelEvent mouse_wheel_event =
238 SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false);
240 mouse_wheel_event.x = current_move_segment_start_position_.x();
241 mouse_wheel_event.y = current_move_segment_start_position_.y();
243 mouse_wheel_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
245 target->DispatchInputEventToPlatform(mouse_wheel_event);
248 void SyntheticSmoothMoveGesture::PressTouchPoint(
249 SyntheticGestureTarget* target,
250 const base::TimeTicks& timestamp) {
251 DCHECK_EQ(current_move_segment_, 0);
252 touch_event_.PressPoint(current_move_segment_start_position_.x(),
253 current_move_segment_start_position_.y());
254 ForwardTouchEvent(target, timestamp);
257 void SyntheticSmoothMoveGesture::MoveTouchPoint(
258 SyntheticGestureTarget* target,
259 const gfx::Vector2dF& delta,
260 const base::TimeTicks& timestamp) {
261 DCHECK_GE(current_move_segment_, 0);
262 DCHECK_LT(current_move_segment_, static_cast<int>(params_.distances.size()));
263 gfx::PointF touch_position = current_move_segment_start_position_ + delta;
264 touch_event_.MovePoint(0, touch_position.x(), touch_position.y());
265 ForwardTouchEvent(target, timestamp);
268 void SyntheticSmoothMoveGesture::ReleaseTouchPoint(
269 SyntheticGestureTarget* target,
270 const base::TimeTicks& timestamp) {
271 DCHECK_EQ(current_move_segment_,
272 static_cast<int>(params_.distances.size()) - 1);
273 touch_event_.ReleasePoint(0);
274 ForwardTouchEvent(target, timestamp);
277 void SyntheticSmoothMoveGesture::PressMousePoint(
278 SyntheticGestureTarget* target,
279 const base::TimeTicks& timestamp) {
280 DCHECK_EQ(params_.input_type,
281 SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT);
282 blink::WebMouseEvent mouse_event = SyntheticWebMouseEventBuilder::Build(
283 blink::WebInputEvent::MouseDown, current_move_segment_start_position_.x(),
284 current_move_segment_start_position_.y(), 0);
285 mouse_event.clickCount = 1;
286 mouse_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
287 target->DispatchInputEventToPlatform(mouse_event);
290 void SyntheticSmoothMoveGesture::ReleaseMousePoint(
291 SyntheticGestureTarget* target,
292 const base::TimeTicks& timestamp) {
293 DCHECK_EQ(params_.input_type,
294 SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT);
295 gfx::PointF mouse_position =
296 current_move_segment_start_position_ + GetPositionDeltaAtTime(timestamp);
297 blink::WebMouseEvent mouse_event = SyntheticWebMouseEventBuilder::Build(
298 blink::WebInputEvent::MouseUp, mouse_position.x(), mouse_position.y(), 0);
299 mouse_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
300 target->DispatchInputEventToPlatform(mouse_event);
303 void SyntheticSmoothMoveGesture::MoveMousePoint(
304 SyntheticGestureTarget* target,
305 const gfx::Vector2dF& delta,
306 const base::TimeTicks& timestamp) {
307 gfx::PointF mouse_position = current_move_segment_start_position_ + delta;
308 DCHECK_EQ(params_.input_type,
309 SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT);
310 blink::WebMouseEvent mouse_event = SyntheticWebMouseEventBuilder::Build(
311 blink::WebInputEvent::MouseMove, mouse_position.x(), mouse_position.y(),
313 mouse_event.button = blink::WebMouseEvent::ButtonLeft;
314 mouse_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
315 target->DispatchInputEventToPlatform(mouse_event);
318 void SyntheticSmoothMoveGesture::AddTouchSlopToFirstDistance(
319 SyntheticGestureTarget* target) {
320 DCHECK_GE(params_.distances.size(), 1ul);
321 gfx::Vector2dF& first_move_distance = params_.distances[0];
322 DCHECK_GT(first_move_distance.Length(), 0);
323 first_move_distance += CeilFromZero(ProjectScalarOntoVector(
324 target->GetTouchSlopInDips(), first_move_distance));
327 gfx::Vector2dF SyntheticSmoothMoveGesture::GetPositionDeltaAtTime(
328 const base::TimeTicks& timestamp) const {
329 // Make sure the final delta is correct. Using the computation below can lead
330 // to issues with floating point precision.
331 if (FinishedCurrentMoveSegment(timestamp))
332 return params_.distances[current_move_segment_];
334 float delta_length =
335 params_.speed_in_pixels_s *
336 (timestamp - current_move_segment_start_time_).InSecondsF();
337 return ProjectScalarOntoVector(delta_length,
338 params_.distances[current_move_segment_]);
341 void SyntheticSmoothMoveGesture::ComputeNextMoveSegment() {
342 current_move_segment_++;
343 DCHECK_LT(current_move_segment_, static_cast<int>(params_.distances.size()));
344 int64 total_duration_in_us = static_cast<int64>(
345 1e6 * (params_.distances[current_move_segment_].Length() /
346 params_.speed_in_pixels_s));
347 DCHECK_GT(total_duration_in_us, 0);
348 current_move_segment_start_time_ = current_move_segment_stop_time_;
349 current_move_segment_stop_time_ =
350 current_move_segment_start_time_ +
351 base::TimeDelta::FromMicroseconds(total_duration_in_us);
354 base::TimeTicks SyntheticSmoothMoveGesture::ClampTimestamp(
355 const base::TimeTicks& timestamp) const {
356 return std::min(timestamp, current_move_segment_stop_time_);
359 bool SyntheticSmoothMoveGesture::FinishedCurrentMoveSegment(
360 const base::TimeTicks& timestamp) const {
361 return timestamp >= current_move_segment_stop_time_;
364 bool SyntheticSmoothMoveGesture::IsLastMoveSegment() const {
365 DCHECK_LT(current_move_segment_, static_cast<int>(params_.distances.size()));
366 return current_move_segment_ ==
367 static_cast<int>(params_.distances.size()) - 1;
370 bool SyntheticSmoothMoveGesture::MoveIsNoOp() const {
371 return params_.distances.size() == 0 || params_.distances[0].IsZero();
374 } // namespace content