#include <stdint.h>
#include "base/functional/callback.h"
#include "content/common/input/input_injector.mojom.h"
#include "content/common/input/synthetic_touchpad_pinch_gesture.h"
#include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
namespace content {
namespace {
float Lerp(float start, float end, float progress) {
return start + progress * (end - start);
}
}
SyntheticTouchpadPinchGesture::SyntheticTouchpadPinchGesture(
const SyntheticPinchGestureParams& gesture_params)
: SyntheticGestureBase(gesture_params),
gesture_source_type_(content::mojom::GestureSourceType::kDefaultInput),
state_(SETUP),
current_scale_(1.0f) {
CHECK_EQ(SyntheticGestureParams::PINCH_GESTURE,
gesture_params.GetGestureType());
DCHECK_GT(params().scale_factor, 0.0f);
if (params().gesture_source_type !=
content::mojom::GestureSourceType::kTouchpadInput) {
DCHECK_EQ(params().gesture_source_type,
content::mojom::GestureSourceType::kDefaultInput);
params().gesture_source_type =
content::mojom::GestureSourceType::kTouchpadInput;
}
}
SyntheticTouchpadPinchGesture::~SyntheticTouchpadPinchGesture() {}
SyntheticGesture::Result SyntheticTouchpadPinchGesture::ForwardInputEvents(
const base::TimeTicks& timestamp,
SyntheticGestureTarget* target) {
CHECK(dispatching_controller_);
base::WeakPtr<SyntheticGestureController> weak_controller =
dispatching_controller_;
if (state_ == SETUP) {
gesture_source_type_ = params().gesture_source_type;
if (gesture_source_type_ ==
content::mojom::GestureSourceType::kDefaultInput)
gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType();
state_ = STARTED;
start_time_ = timestamp;
}
DCHECK_NE(gesture_source_type_,
content::mojom::GestureSourceType::kDefaultInput);
if (gesture_source_type_ == content::mojom::GestureSourceType::kMouseInput) {
ForwardGestureEvents(timestamp, target);
CHECK(weak_controller);
} else {
return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
}
return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED
: SyntheticGesture::GESTURE_RUNNING;
}
void SyntheticTouchpadPinchGesture::WaitForTargetAck(
base::OnceClosure callback,
SyntheticGestureTarget* target) const {
target->WaitForTargetAck(params().GetGestureType(), gesture_source_type_,
std::move(callback));
}
void SyntheticTouchpadPinchGesture::ForwardGestureEvents(
const base::TimeTicks& timestamp,
SyntheticGestureTarget* target) {
switch (state_) {
case STARTED:
if (params().scale_factor == 1.0f) {
state_ = DONE;
break;
}
CalculateEndTime(target);
target->DispatchInputEventToPlatform(
blink::SyntheticWebGestureEventBuilder::Build(
blink::WebGestureEvent::Type::kGesturePinchBegin,
blink::WebGestureDevice::kTouchpad,
params().from_devtools_debugger
? blink::WebInputEvent::kFromDebugger
: blink::WebInputEvent::kNoModifiers));
state_ = IN_PROGRESS;
break;
case IN_PROGRESS: {
base::TimeTicks event_timestamp = ClampTimestamp(timestamp);
float target_scale = CalculateTargetScale(event_timestamp);
float incremental_scale = target_scale / current_scale_;
current_scale_ = target_scale;
target->DispatchInputEventToPlatform(
blink::SyntheticWebGestureEventBuilder::BuildPinchUpdate(
incremental_scale, params().anchor.x(), params().anchor.y(),
params().from_devtools_debugger
? blink::WebInputEvent::kFromDebugger
: blink::WebInputEvent::kNoModifiers,
blink::WebGestureDevice::kTouchpad));
if (HasReachedTarget(event_timestamp)) {
target->DispatchInputEventToPlatform(
blink::SyntheticWebGestureEventBuilder::Build(
blink::WebGestureEvent::Type::kGesturePinchEnd,
blink::WebGestureDevice::kTouchpad,
params().from_devtools_debugger
? blink::WebInputEvent::kFromDebugger
: blink::WebInputEvent::kNoModifiers));
state_ = DONE;
}
break;
}
case SETUP:
NOTREACHED() << "State SETUP invalid for synthetic pinch.";
case DONE:
NOTREACHED() << "State DONE invalid for synthetic pinch.";
}
}
float SyntheticTouchpadPinchGesture::CalculateTargetScale(
const base::TimeTicks& timestamp) const {
if (HasReachedTarget(timestamp))
return params().scale_factor;
const float progress = (timestamp - start_time_) / (stop_time_ - start_time_);
return Lerp(1.0f, params().scale_factor, progress);
}
void SyntheticTouchpadPinchGesture::CalculateEndTime(
SyntheticGestureTarget* target) {
const int kPixelsNeededToDoubleOrHalve = 200;
float scale_factor = params().scale_factor;
if (scale_factor < 1.0f) {
scale_factor = 1.0f / scale_factor;
}
float scale_factor_delta =
(scale_factor - 1.0f) * kPixelsNeededToDoubleOrHalve;
const base::TimeDelta total_duration = base::Seconds(
scale_factor_delta / params().relative_pointer_speed_in_pixels_s);
DCHECK_GT(total_duration, base::TimeDelta());
stop_time_ = start_time_ + total_duration;
}
base::TimeTicks SyntheticTouchpadPinchGesture::ClampTimestamp(
const base::TimeTicks& timestamp) const {
return std::min(timestamp, stop_time_);
}
bool SyntheticTouchpadPinchGesture::HasReachedTarget(
const base::TimeTicks& timestamp) const {
return timestamp >= stop_time_;
}
}