blob: 4d03f78eec8aed2e59b4e429149c60f801752c24 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "content/browser/android/gesture_listener_manager.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_contents/web_contents_view_android.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents_observer.h"
#include "jni/GestureListenerManagerImpl_jni.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "ui/events/android/gesture_event_type.h"
#include "ui/gfx/geometry/size_f.h"
using blink::WebGestureEvent;
using blink::WebInputEvent;
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace content {
namespace {
int ToGestureEventType(WebInputEvent::Type type) {
switch (type) {
case WebInputEvent::kGestureScrollBegin:
return ui::GESTURE_EVENT_TYPE_SCROLL_START;
case WebInputEvent::kGestureScrollEnd:
return ui::GESTURE_EVENT_TYPE_SCROLL_END;
case WebInputEvent::kGestureScrollUpdate:
return ui::GESTURE_EVENT_TYPE_SCROLL_BY;
case WebInputEvent::kGestureFlingStart:
return ui::GESTURE_EVENT_TYPE_FLING_START;
case WebInputEvent::kGestureFlingCancel:
return ui::GESTURE_EVENT_TYPE_FLING_CANCEL;
case WebInputEvent::kGestureShowPress:
return ui::GESTURE_EVENT_TYPE_SHOW_PRESS;
case WebInputEvent::kGestureTap:
return ui::GESTURE_EVENT_TYPE_SINGLE_TAP_CONFIRMED;
case WebInputEvent::kGestureTapUnconfirmed:
return ui::GESTURE_EVENT_TYPE_SINGLE_TAP_UNCONFIRMED;
case WebInputEvent::kGestureTapDown:
return ui::GESTURE_EVENT_TYPE_TAP_DOWN;
case WebInputEvent::kGestureTapCancel:
return ui::GESTURE_EVENT_TYPE_TAP_CANCEL;
case WebInputEvent::kGestureDoubleTap:
return ui::GESTURE_EVENT_TYPE_DOUBLE_TAP;
case WebInputEvent::kGestureLongPress:
return ui::GESTURE_EVENT_TYPE_LONG_PRESS;
case WebInputEvent::kGestureLongTap:
return ui::GESTURE_EVENT_TYPE_LONG_TAP;
case WebInputEvent::kGesturePinchBegin:
return ui::GESTURE_EVENT_TYPE_PINCH_BEGIN;
case WebInputEvent::kGesturePinchEnd:
return ui::GESTURE_EVENT_TYPE_PINCH_END;
case WebInputEvent::kGesturePinchUpdate:
return ui::GESTURE_EVENT_TYPE_PINCH_BY;
case WebInputEvent::kGestureTwoFingerTap:
default:
NOTREACHED() << "Invalid source gesture type: "
<< WebInputEvent::GetName(type);
return -1;
}
}
} // namespace
// Reset scroll, hide popups on navigation finish/render process gone event.
class GestureListenerManager::ResetScrollObserver : public WebContentsObserver {
public:
ResetScrollObserver(WebContents* web_contents,
GestureListenerManager* manager);
void DidFinishNavigation(NavigationHandle* navigation_handle) override;
void RenderProcessGone(base::TerminationStatus status) override;
private:
GestureListenerManager* const manager_;
DISALLOW_COPY_AND_ASSIGN(ResetScrollObserver);
};
GestureListenerManager::ResetScrollObserver::ResetScrollObserver(
WebContents* web_contents,
GestureListenerManager* manager)
: WebContentsObserver(web_contents), manager_(manager) {}
void GestureListenerManager::ResetScrollObserver::DidFinishNavigation(
NavigationHandle* navigation_handle) {
manager_->OnNavigationFinished(navigation_handle);
}
void GestureListenerManager::ResetScrollObserver::RenderProcessGone(
base::TerminationStatus status) {
manager_->OnRenderProcessGone();
}
GestureListenerManager::GestureListenerManager(JNIEnv* env,
const JavaParamRef<jobject>& obj,
WebContentsImpl* web_contents)
: RenderWidgetHostConnector(web_contents),
reset_scroll_observer_(new ResetScrollObserver(web_contents, this)),
web_contents_(web_contents),
java_ref_(env, obj) {}
GestureListenerManager::~GestureListenerManager() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (j_obj.is_null())
return;
Java_GestureListenerManagerImpl_onNativeDestroyed(env, j_obj);
}
void GestureListenerManager::ResetGestureDetection(
JNIEnv* env,
const JavaParamRef<jobject>& obj) {
if (rwhva_)
rwhva_->ResetGestureDetection();
}
void GestureListenerManager::SetDoubleTapSupportEnabled(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jboolean enabled) {
if (rwhva_)
rwhva_->SetDoubleTapSupportEnabled(enabled);
}
void GestureListenerManager::SetMultiTouchZoomSupportEnabled(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
jboolean enabled) {
if (rwhva_)
rwhva_->SetMultiTouchZoomSupportEnabled(enabled);
}
void GestureListenerManager::GestureEventAck(
const blink::WebGestureEvent& event,
InputEventAckState ack_result) {
// This is called to fix crash happening while WebContents is being
// destroyed. See https://crbug.com/803244#c20
if (web_contents_->IsBeingDestroyed())
return;
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (j_obj.is_null())
return;
Java_GestureListenerManagerImpl_onEventAck(
env, j_obj, event.GetType(),
ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
}
void GestureListenerManager::DidStopFlinging() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (j_obj.is_null())
return;
Java_GestureListenerManagerImpl_onFlingEnd(env, j_obj);
}
bool GestureListenerManager::FilterInputEvent(const WebInputEvent& event) {
if (event.GetType() != WebInputEvent::kGestureTap &&
event.GetType() != WebInputEvent::kGestureLongTap &&
event.GetType() != WebInputEvent::kGestureLongPress &&
event.GetType() != WebInputEvent::kMouseDown)
return false;
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env);
if (j_obj.is_null())
return false;
web_contents_->GetNativeView()->RequestFocus();
if (event.GetType() == WebInputEvent::kMouseDown)
return false;
const WebGestureEvent& gesture = static_cast<const WebGestureEvent&>(event);
int gesture_type = ToGestureEventType(event.GetType());
float dip_scale = web_contents_->GetNativeView()->GetDipScale();
return Java_GestureListenerManagerImpl_filterTapOrPressEvent(
env, j_obj, gesture_type, gesture.PositionInWidget().x * dip_scale,
gesture.PositionInWidget().y * dip_scale);
}
// All positions and sizes (except |top_shown_pix|) are in CSS pixels.
// Note that viewport_width/height is a best effort based.
void GestureListenerManager::UpdateScrollInfo(
const gfx::Vector2dF& scroll_offset,
float page_scale_factor,
const float min_page_scale,
const float max_page_scale,
const gfx::SizeF& content,
const gfx::SizeF& viewport,
const float content_offset,
const float top_shown_pix,
bool top_changed) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
web_contents_->GetNativeView()->UpdateFrameInfo({viewport, content_offset});
Java_GestureListenerManagerImpl_updateScrollInfo(
env, obj, scroll_offset.x(), scroll_offset.y(), page_scale_factor,
min_page_scale, max_page_scale, content.width(), content.height(),
viewport.width(), viewport.height(), top_shown_pix, top_changed);
}
void GestureListenerManager::UpdateOnTouchDown() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
Java_GestureListenerManagerImpl_updateOnTouchDown(env, obj);
}
void GestureListenerManager::UpdateRenderProcessConnection(
RenderWidgetHostViewAndroid* old_rwhva,
RenderWidgetHostViewAndroid* new_rwhva) {
if (old_rwhva)
old_rwhva->set_gesture_listener_manager(nullptr);
if (new_rwhva) {
new_rwhva->set_gesture_listener_manager(this);
}
rwhva_ = new_rwhva;
}
void GestureListenerManager::OnNavigationFinished(
NavigationHandle* navigation_handle) {
if (navigation_handle->IsInMainFrame() && navigation_handle->HasCommitted() &&
!navigation_handle->IsSameDocument()) {
ResetPopupsAndInput(false);
}
}
void GestureListenerManager::OnRenderProcessGone() {
ResetPopupsAndInput(true);
}
void GestureListenerManager::ResetPopupsAndInput(bool render_process_gone) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
Java_GestureListenerManagerImpl_resetPopupsAndInput(env, obj,
render_process_gone);
}
jlong JNI_GestureListenerManagerImpl_Init(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& jweb_contents) {
auto* web_contents = WebContents::FromJavaWebContents(jweb_contents);
CHECK(web_contents) << "Should be created with a valid WebContents.";
// Owns itself and gets destroyed when |WebContentsDestroyed| is called.
auto* manager = new GestureListenerManager(
env, obj, static_cast<WebContentsImpl*>(web_contents));
manager->Initialize();
return reinterpret_cast<intptr_t>(manager);
}
} // namespace content