blob: 6d8d70f19c11ee307c9b05e9889cbde59eca913a [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/android/content_ui_event_handler.h"
#include "base/metrics/histogram_macros.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_contents/web_contents_view_android.h"
#include "ui/android/window_android.h"
#include "ui/events/android/event_handler_android.h"
#include "ui/events/android/gesture_event_android.h"
#include "ui/events/android/gesture_event_type.h"
#include "ui/events/android/key_event_android.h"
#include "ui/events/android/motion_event_android_factory.h"
#include "ui/events/android/motion_event_android_java.h"
#include "ui/events/android/motion_event_android_source_java.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_utils.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "content/public/android/content_jni_headers/ContentUiEventHandler_jni.h"
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace content {
ContentUiEventHandler::ContentUiEventHandler(JNIEnv* env,
const JavaRef<jobject>& obj,
WebContentsImpl* web_contents)
: java_ref_(env, obj), web_contents_(web_contents) {}
RenderWidgetHostViewAndroid* ContentUiEventHandler::GetRenderWidgetHostView() {
RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
return static_cast<RenderWidgetHostViewAndroid*>(rwhv);
}
bool ContentUiEventHandler::OnGenericMotionEvent(
const ui::MotionEventAndroid& event) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (!j_obj.is_null()) {
return Java_ContentUiEventHandler_onGenericMotionEvent(
env, j_obj, event.GetJavaObject());
}
return false;
}
bool ContentUiEventHandler::OnKeyUp(const ui::KeyEventAndroid& event) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (!j_obj.is_null()) {
return Java_ContentUiEventHandler_onKeyUp(env, j_obj, event);
}
return false;
}
bool ContentUiEventHandler::DispatchKeyEvent(const ui::KeyEventAndroid& event) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (!j_obj.is_null()) {
return Java_ContentUiEventHandler_dispatchKeyEvent(env, j_obj, event);
}
return false;
}
bool ContentUiEventHandler::ScrollBy(float delta_x, float delta_y) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (!j_obj.is_null()) {
Java_ContentUiEventHandler_scrollBy(env, j_obj, delta_x, delta_y);
}
return false;
}
bool ContentUiEventHandler::ScrollTo(float x, float y) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (!j_obj.is_null()) {
Java_ContentUiEventHandler_scrollTo(env, j_obj, x, y);
}
return false;
}
void ContentUiEventHandler::SendMouseWheelEvent(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& motion_event,
jlong time_ns) {
auto* event_handler = GetRenderWidgetHostView();
if (!event_handler)
return;
// Compute Event.Latency.OS2.MOUSE_WHEEL histogram.
base::TimeTicks current_time = ui::EventTimeForNow();
base::TimeTicks event_time = base::TimeTicks::FromJavaNanoTime(time_ns);
ComputeEventLatencyOS(ui::EventType::kMousewheel, event_time, current_time);
auto source = ui::MotionEventAndroidSourceJava::Create(motion_event, false);
ui::MotionEventAndroid::Pointer pointer(
/*id=*/0, /*pos_x_pixels=*/source->GetXPix(0),
/*pos_y_pixels=*/source->GetYPix(0),
/*touch_major_pixels=*/0.0f,
/*touch_minor_pixels=*/0.0f, /*pressure=*/0.0f, /*orientation_rad=*/0.0f,
/*tilt_rad=*/0.0f, /*tool_type=*/0);
auto* view = web_contents_->GetNativeView();
auto* window = view->GetWindowAndroid();
float pixels_per_tick =
window ? window->mouse_wheel_scroll_factor()
: ui::kDefaultMouseWheelTickMultiplier * view->GetDipScale();
auto event = ui::MotionEventAndroidFactory::CreateFromJava(
env, motion_event,
/*pix_to_dip=*/1.f / view->GetDipScale(),
/*ticks_x=*/source->GetAxisHscroll(0),
/*ticks_y=*/source->GetAxisVscroll(0),
/*tick_multiplier=*/pixels_per_tick,
/*oldest_event_time=*/base::TimeTicks::FromJavaNanoTime(time_ns),
/*android_action=*/0,
/*pointer_count=*/1,
/*history_size=*/0,
/*action_index=*/0,
/*android_action_button=*/0,
/*android_gesture_classification=*/0,
/*android_button_state=*/0,
/*raw_offset_x_pixels=*/0,
/*raw_offset_y_pixels=*/0,
/*for_touch_handle=*/false,
/*pointer0=*/&pointer,
/*pointer1=*/nullptr);
event_handler->OnMouseWheelEvent(*event);
}
void ContentUiEventHandler::SendMouseEvent(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& motion_event,
jlong time_ns,
jint android_action_button,
jint android_tool_type) {
auto* event_handler = GetRenderWidgetHostView();
if (!event_handler)
return;
auto source = ui::MotionEventAndroidSourceJava::Create(motion_event, false);
// Construct a motion_event object minimally, only to convert the raw
// parameters to ui::MotionEvent values. Since we used only the cached values
// at index=0, it is okay to even pass a null event to the constructor.
ui::MotionEventAndroid::Pointer pointer(
/*id=*/source->GetPointerId(0), /*pos_x_pixels=*/source->GetXPix(0),
/*pos_y_pixels=*/source->GetYPix(0),
/*touch_major_pixels=*/0.0f,
/*touch_minor_pixels=*/0.0f, /*pressure=*/source->GetPressure(0),
/*orientation_rad=*/source->GetRawOrientation(0),
/*tilt_rad=*/source->GetRawTilt(0), /*tool_type=*/android_tool_type);
auto event = ui::MotionEventAndroidFactory::CreateFromJava(
env, /*event=*/motion_event,
/*pix_to_dip=*/1.f / web_contents_->GetNativeView()->GetDipScale(),
/*ticks_x=*/0.f,
/*ticks_y=*/0.f,
/*tick_multiplier=*/0.f,
/*oldest_event_time=*/base::TimeTicks::FromJavaNanoTime(time_ns),
/*android_action=*/source->GetActionMasked(),
/*pointer_count=*/1,
/*history_size=*/0,
/*action_index=*/0, android_action_button,
/*android_gesture_classification=*/0,
/*android_button_state=*/source->GetButtonState(),
/*raw_offset_x_pixels=*/0,
/*raw_offset_y_pixels=*/0,
/*for_touch_handle=*/false,
/*pointer0=*/&pointer,
/*pointer1=*/nullptr);
event_handler->OnMouseEvent(*event);
}
void ContentUiEventHandler::SendScrollEvent(JNIEnv* env,
jlong time_ms,
jfloat delta_x,
jfloat delta_y) {
auto* event_handler = GetRenderWidgetHostView();
if (!event_handler)
return;
float dip_scale = web_contents_->GetNativeView()->GetDipScale();
float delta_xdip = delta_x / dip_scale;
float delta_ydip = delta_y / dip_scale;
constexpr bool target_viewport = true;
constexpr bool synthetic_scroll = false;
constexpr bool prevent_boosting = false;
event_handler->OnGestureEvent(ui::GestureEventAndroid(
ui::GESTURE_EVENT_TYPE_SCROLL_START, gfx::PointF(), gfx::PointF(),
time_ms, 0, -delta_xdip, -delta_ydip, 0, 0, target_viewport,
synthetic_scroll, prevent_boosting));
event_handler->OnGestureEvent(ui::GestureEventAndroid(
ui::GESTURE_EVENT_TYPE_SCROLL_BY, gfx::PointF(), gfx::PointF(), time_ms,
0, -delta_xdip, -delta_ydip, 0, 0, target_viewport, synthetic_scroll,
prevent_boosting));
event_handler->OnGestureEvent(ui::GestureEventAndroid(
ui::GESTURE_EVENT_TYPE_SCROLL_END, gfx::PointF(), gfx::PointF(), time_ms,
0, -delta_xdip, -delta_ydip, 0, 0, target_viewport, synthetic_scroll,
prevent_boosting));
}
void ContentUiEventHandler::CancelFling(JNIEnv* env, jlong time_ms) {
auto* event_handler = GetRenderWidgetHostView();
if (!event_handler)
return;
event_handler->OnGestureEvent(ui::GestureEventAndroid(
ui::GESTURE_EVENT_TYPE_FLING_CANCEL, gfx::PointF(), gfx::PointF(),
time_ms, 0, 0, 0, 0, 0, /*target_viewport*/ false,
/*synthetic_scroll*/ false, true));
}
jlong JNI_ContentUiEventHandler_Init(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& jweb_contents) {
WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
WebContents::FromJavaWebContents(jweb_contents));
CHECK(web_contents)
<< "A ContentUiEventHandler should be created with a valid WebContents.";
auto handler =
std::make_unique<ContentUiEventHandler>(env, obj, web_contents);
auto* handler_ptr = handler.get();
static_cast<WebContentsViewAndroid*>(web_contents->GetView())
->SetContentUiEventHandler(std::move(handler));
return reinterpret_cast<intptr_t>(handler_ptr);
}
} // namespace content