|  | // 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 "content/public/android/content_jni_headers/ContentUiEventHandler_jni.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.h" | 
|  | #include "ui/events/base_event_utils.h" | 
|  | #include "ui/events/event_utils.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.key_code(), | 
|  | event.GetJavaObject()); | 
|  | } | 
|  | 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.GetJavaObject()); | 
|  | } | 
|  | 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 JavaParamRef<jobject>& obj, | 
|  | jlong time_ms, | 
|  | jfloat x, | 
|  | jfloat y, | 
|  | jfloat ticks_x, | 
|  | jfloat ticks_y) { | 
|  | 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() + base::Milliseconds(time_ms); | 
|  | ComputeEventLatencyOS(ui::ET_MOUSEWHEEL, event_time, current_time); | 
|  | ui::MotionEventAndroid::Pointer pointer( | 
|  | 0, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */, 0.0f, 0.0f, 0); | 
|  |  | 
|  | auto* view = web_contents_->GetNativeView(); | 
|  | auto* window = view->GetWindowAndroid(); | 
|  | float pixels_per_tick = | 
|  | window ? window->mouse_wheel_scroll_factor() | 
|  | : ui::kDefaultMouseWheelTickMultiplier * view->GetDipScale(); | 
|  | ui::MotionEventAndroid event( | 
|  | env, nullptr, 1.f / view->GetDipScale(), ticks_x, ticks_y, | 
|  | pixels_per_tick, time_ms, 0 /* action */, 1 /* pointer_count */, | 
|  | 0 /* history_size */, 0 /* action_index */, 0, 0, 0, 0, | 
|  | 0 /* raw_offset_x_pixels */, 0 /* raw_offset_y_pixels */, | 
|  | false /* for_touch_handle */, &pointer, nullptr); | 
|  | event_handler->OnMouseWheelEvent(event); | 
|  | } | 
|  |  | 
|  | void ContentUiEventHandler::SendMouseEvent(JNIEnv* env, | 
|  | const JavaParamRef<jobject>& obj, | 
|  | jlong time_ms, | 
|  | jint android_action, | 
|  | jfloat x, | 
|  | jfloat y, | 
|  | jint pointer_id, | 
|  | jfloat orientation, | 
|  | jfloat pressure, | 
|  | jfloat tilt, | 
|  | jint android_action_button, | 
|  | jint android_button_state, | 
|  | jint android_meta_state, | 
|  | jint android_tool_type) { | 
|  | auto* event_handler = GetRenderWidgetHostView(); | 
|  | if (!event_handler) | 
|  | return; | 
|  |  | 
|  | // 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( | 
|  | pointer_id, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */, | 
|  | orientation, tilt, android_tool_type); | 
|  | ui::MotionEventAndroid event( | 
|  | env, nullptr /* event */, | 
|  | 1.f / web_contents_->GetNativeView()->GetDipScale(), 0.f, 0.f, 0.f, | 
|  | time_ms, android_action, 1 /* pointer_count */, 0 /* history_size */, | 
|  | 0 /* action_index */, android_action_button, | 
|  | 0 /* gesture_classification */, android_button_state, android_meta_state, | 
|  | 0 /* raw_offset_x_pixels */, 0 /* raw_offset_y_pixels */, | 
|  | false /* for_touch_handle */, &pointer, nullptr); | 
|  | event_handler->OnMouseEvent(event); | 
|  | } | 
|  |  | 
|  | void ContentUiEventHandler::SendScrollEvent(JNIEnv* env, | 
|  | const JavaParamRef<jobject>& jobj, | 
|  | 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, | 
|  | const JavaParamRef<jobject>& jobj, | 
|  | 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 |