| // 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 "content/browser/android/content_view_core.h" |
| |
| #include <stddef.h> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/string_util.h" |
| #include "base/values.h" |
| #include "cc/layers/layer.h" |
| #include "cc/layers/solid_color_layer.h" |
| #include "components/viz/common/frame_sinks/begin_frame_args.h" |
| #include "content/browser/android/gesture_event_type.h" |
| #include "content/browser/android/interstitial_page_delegate_android.h" |
| #include "content/browser/frame_host/interstitial_page_impl.h" |
| #include "content/browser/media/media_web_contents_observer.h" |
| #include "content/browser/renderer_host/compositor_impl_android.h" |
| #include "content/browser/renderer_host/input/web_input_event_builders_android.h" |
| #include "content/browser/renderer_host/render_view_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_view_android.h" |
| #include "content/browser/web_contents/web_contents_android.h" |
| #include "content/browser/web_contents/web_contents_view_android.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/browser/android/compositor.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/favicon_status.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/ssl_host_state_delegate.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/menu_item.h" |
| #include "content/public/common/user_agent.h" |
| #include "jni/ContentViewCore_jni.h" |
| #include "third_party/WebKit/public/platform/WebInputEvent.h" |
| #include "ui/android/view_android.h" |
| #include "ui/android/window_android.h" |
| #include "ui/events/blink/blink_event_util.h" |
| #include "ui/events/blink/web_input_event_traits.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/events/gesture_detection/motion_event.h" |
| #include "ui/gfx/android/java_bitmap.h" |
| #include "ui/gfx/geometry/point_conversions.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| #include "ui/gfx/geometry/size_f.h" |
| |
| using base::android::AttachCurrentThread; |
| using base::android::ConvertJavaStringToUTF16; |
| using base::android::ConvertJavaStringToUTF8; |
| using base::android::ConvertUTF16ToJavaString; |
| using base::android::ConvertUTF8ToJavaString; |
| using base::android::JavaParamRef; |
| using base::android::JavaRef; |
| using base::android::ScopedJavaLocalRef; |
| using blink::WebGestureEvent; |
| using blink::WebInputEvent; |
| |
| namespace content { |
| |
| namespace { |
| |
| // Describes the type and enabled state of a select popup item. |
| // |
| // A Java counterpart will be generated for this enum. |
| // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content.browser.input |
| enum PopupItemType { |
| // Popup item is of type group |
| POPUP_ITEM_TYPE_GROUP, |
| |
| // Popup item is disabled |
| POPUP_ITEM_TYPE_DISABLED, |
| |
| // Popup item is enabled |
| POPUP_ITEM_TYPE_ENABLED, |
| }; |
| |
| const void* const kContentViewUserDataKey = &kContentViewUserDataKey; |
| |
| int GetRenderProcessIdFromRenderViewHost(RenderViewHost* host) { |
| DCHECK(host); |
| RenderProcessHost* render_process = host->GetProcess(); |
| DCHECK(render_process); |
| if (render_process->HasConnection()) |
| return render_process->GetHandle(); |
| return 0; |
| } |
| |
| int ToGestureEventType(WebInputEvent::Type type) { |
| switch (type) { |
| case WebInputEvent::kGestureScrollBegin: |
| return GESTURE_EVENT_TYPE_SCROLL_START; |
| case WebInputEvent::kGestureScrollEnd: |
| return GESTURE_EVENT_TYPE_SCROLL_END; |
| case WebInputEvent::kGestureScrollUpdate: |
| return GESTURE_EVENT_TYPE_SCROLL_BY; |
| case WebInputEvent::kGestureFlingStart: |
| return GESTURE_EVENT_TYPE_FLING_START; |
| case WebInputEvent::kGestureFlingCancel: |
| return GESTURE_EVENT_TYPE_FLING_CANCEL; |
| case WebInputEvent::kGestureShowPress: |
| return GESTURE_EVENT_TYPE_SHOW_PRESS; |
| case WebInputEvent::kGestureTap: |
| return GESTURE_EVENT_TYPE_SINGLE_TAP_CONFIRMED; |
| case WebInputEvent::kGestureTapUnconfirmed: |
| return GESTURE_EVENT_TYPE_SINGLE_TAP_UNCONFIRMED; |
| case WebInputEvent::kGestureTapDown: |
| return GESTURE_EVENT_TYPE_TAP_DOWN; |
| case WebInputEvent::kGestureTapCancel: |
| return GESTURE_EVENT_TYPE_TAP_CANCEL; |
| case WebInputEvent::kGestureDoubleTap: |
| return GESTURE_EVENT_TYPE_DOUBLE_TAP; |
| case WebInputEvent::kGestureLongPress: |
| return GESTURE_EVENT_TYPE_LONG_PRESS; |
| case WebInputEvent::kGestureLongTap: |
| return GESTURE_EVENT_TYPE_LONG_TAP; |
| case WebInputEvent::kGesturePinchBegin: |
| return GESTURE_EVENT_TYPE_PINCH_BEGIN; |
| case WebInputEvent::kGesturePinchEnd: |
| return GESTURE_EVENT_TYPE_PINCH_END; |
| case WebInputEvent::kGesturePinchUpdate: |
| return GESTURE_EVENT_TYPE_PINCH_BY; |
| case WebInputEvent::kGestureTwoFingerTap: |
| default: |
| NOTREACHED() << "Invalid source gesture type: " |
| << WebInputEvent::GetName(type); |
| return -1; |
| } |
| } |
| |
| } // namespace |
| |
| // Enables a callback when the underlying WebContents is destroyed, to enable |
| // nulling the back-pointer. |
| class ContentViewCore::ContentViewUserData |
| : public base::SupportsUserData::Data { |
| public: |
| explicit ContentViewUserData(ContentViewCore* content_view_core) |
| : content_view_core_(content_view_core) {} |
| |
| ~ContentViewUserData() override { |
| // TODO(joth): When chrome has finished removing the TabContents class (see |
| // crbug.com/107201) consider inverting relationship, so ContentViewCore |
| // would own WebContents. That effectively implies making the WebContents |
| // destructor private on Android. |
| delete content_view_core_; |
| } |
| |
| ContentViewCore* get() const { return content_view_core_; } |
| |
| private: |
| // Not using scoped_ptr as ContentViewCore destructor is private. |
| ContentViewCore* content_view_core_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(ContentViewUserData); |
| }; |
| |
| // static |
| ContentViewCore* ContentViewCore::FromWebContents( |
| content::WebContents* web_contents) { |
| ContentViewCore::ContentViewUserData* data = |
| static_cast<ContentViewCore::ContentViewUserData*>( |
| web_contents->GetUserData(kContentViewUserDataKey)); |
| return data ? data->get() : NULL; |
| } |
| |
| ContentViewCore::ContentViewCore(JNIEnv* env, |
| const JavaRef<jobject>& obj, |
| WebContents* web_contents, |
| float dpi_scale) |
| : WebContentsObserver(web_contents), |
| java_ref_(env, obj), |
| web_contents_(static_cast<WebContentsImpl*>(web_contents)), |
| dpi_scale_(dpi_scale), |
| device_orientation_(0) { |
| GetViewAndroid()->SetLayer(cc::Layer::Create()); |
| |
| // Currently, the only use case we have for overriding a user agent involves |
| // spoofing a desktop Linux user agent for "Request desktop site". |
| // Automatically set it for all WebContents so that it is available when a |
| // NavigationEntry requires the user agent to be overridden. |
| const char kLinuxInfoStr[] = "X11; Linux x86_64"; |
| std::string product = content::GetContentClient()->GetProduct(); |
| std::string spoofed_ua = |
| BuildUserAgentFromOSAndProduct(kLinuxInfoStr, product); |
| web_contents->SetUserAgentOverride(spoofed_ua); |
| |
| InitWebContents(); |
| } |
| |
| ContentViewCore::~ContentViewCore() { |
| for (auto* host : web_contents_->GetAllRenderWidgetHosts()) { |
| static_cast<RenderWidgetHostViewAndroid*>(host->GetView()) |
| ->OnContentViewCoreDestroyed(); |
| } |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| java_ref_.reset(); |
| if (!j_obj.is_null()) { |
| Java_ContentViewCore_onNativeContentViewCoreDestroyed( |
| env, j_obj, reinterpret_cast<intptr_t>(this)); |
| } |
| } |
| |
| void ContentViewCore::UpdateWindowAndroid( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jlong window_android) { |
| auto* window = reinterpret_cast<ui::WindowAndroid*>(window_android); |
| auto* old_window = GetWindowAndroid(); |
| if (window == old_window) |
| return; |
| |
| auto* view = GetViewAndroid(); |
| if (old_window) |
| view->RemoveFromParent(); |
| if (window) |
| window->AddChild(view); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| ContentViewCore::GetWebContentsAndroid(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return web_contents_->GetJavaWebContents(); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| ContentViewCore::GetJavaWindowAndroid(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| if (!GetWindowAndroid()) |
| return ScopedJavaLocalRef<jobject>(); |
| return GetWindowAndroid()->GetJavaObject(); |
| } |
| |
| void ContentViewCore::OnJavaContentViewCoreDestroyed( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| DCHECK(env->IsSameObject(java_ref_.get(env).obj(), obj)); |
| java_ref_.reset(); |
| // Java peer has gone, ContentViewCore is not functional and waits to |
| // be destroyed with WebContents. |
| // We need to reset WebContentsViewAndroid's reference, otherwise, there |
| // could have call in when swapping the WebContents, |
| // see http://crbug.com/383939 . |
| DCHECK(web_contents_); |
| static_cast<WebContentsViewAndroid*>( |
| static_cast<WebContentsImpl*>(web_contents_)->GetView()) |
| ->SetContentViewCore(NULL); |
| } |
| |
| void ContentViewCore::InitWebContents() { |
| DCHECK(web_contents_); |
| static_cast<WebContentsViewAndroid*>( |
| static_cast<WebContentsImpl*>(web_contents_)->GetView()) |
| ->SetContentViewCore(this); |
| DCHECK(!web_contents_->GetUserData(kContentViewUserDataKey)); |
| web_contents_->SetUserData(kContentViewUserDataKey, |
| std::make_unique<ContentViewUserData>(this)); |
| } |
| |
| void ContentViewCore::RenderViewReady() { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (!obj.is_null()) |
| Java_ContentViewCore_onRenderProcessChange(env, obj); |
| |
| if (device_orientation_ != 0) |
| SendOrientationChangeEventInternal(); |
| } |
| |
| void ContentViewCore::RenderViewHostChanged(RenderViewHost* old_host, |
| RenderViewHost* new_host) { |
| int old_pid = 0; |
| if (old_host) { |
| old_pid = GetRenderProcessIdFromRenderViewHost(old_host); |
| |
| RenderWidgetHostViewAndroid* view = |
| static_cast<RenderWidgetHostViewAndroid*>( |
| old_host->GetWidget()->GetView()); |
| if (view) |
| view->SetContentViewCore(NULL); |
| |
| view = static_cast<RenderWidgetHostViewAndroid*>( |
| new_host->GetWidget()->GetView()); |
| if (view) |
| view->SetContentViewCore(this); |
| } |
| int new_pid = |
| GetRenderProcessIdFromRenderViewHost(web_contents_->GetRenderViewHost()); |
| if (new_pid != old_pid) { |
| // Notify the Java side that the renderer process changed. |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (!obj.is_null()) { |
| Java_ContentViewCore_onRenderProcessChange(env, obj); |
| } |
| } |
| |
| SetFocusInternal(GetViewAndroid()->HasFocus()); |
| } |
| |
| RenderWidgetHostViewAndroid* ContentViewCore::GetRenderWidgetHostViewAndroid() |
| const { |
| RenderWidgetHostView* rwhv = NULL; |
| if (web_contents_) { |
| rwhv = web_contents_->GetRenderWidgetHostView(); |
| if (web_contents_->ShowingInterstitialPage()) { |
| rwhv = web_contents_->GetInterstitialPage() |
| ->GetMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->GetView(); |
| } |
| } |
| return static_cast<RenderWidgetHostViewAndroid*>(rwhv); |
| } |
| |
| ScopedJavaLocalRef<jobject> ContentViewCore::GetJavaObject() { |
| JNIEnv* env = AttachCurrentThread(); |
| return java_ref_.get(env); |
| } |
| |
| jint ContentViewCore::GetBackgroundColor(JNIEnv* env, jobject obj) { |
| RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid(); |
| if (!rwhva) |
| return SK_ColorWHITE; |
| return rwhva->GetCachedBackgroundColor(); |
| } |
| |
| // All positions and sizes (except |top_shown_pix|) are in CSS pixels. |
| // Note that viewport_width/height is a best effort based. |
| // ContentViewCore has the actual information about the physical viewport size. |
| void ContentViewCore::UpdateFrameInfo( |
| const gfx::Vector2dF& scroll_offset, |
| float page_scale_factor, |
| const gfx::Vector2dF& page_scale_factor_limits, |
| const gfx::SizeF& content_size, |
| const gfx::SizeF& viewport_size, |
| const float content_offset, |
| const float top_shown_pix, |
| bool top_changed, |
| bool is_mobile_optimized_hint) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (obj.is_null() || !GetWindowAndroid()) |
| return; |
| |
| GetViewAndroid()->UpdateFrameInfo({ |
| viewport_size, page_scale_factor, content_offset, |
| }); |
| |
| // Current viewport size in css. |
| gfx::SizeF view_size = gfx::SizeF(gfx::ScaleToCeiledSize( |
| GetViewportSizePix(), 1.0f / (dpi_scale() * page_scale_factor))); |
| |
| // Adjust content size to be always at least as big as the actual |
| // viewport (as set by onSizeChanged). |
| float content_width = std::max(content_size.width(), view_size.width()); |
| float content_height = std::max(content_size.height(), view_size.height()); |
| |
| Java_ContentViewCore_updateFrameInfo( |
| env, obj, scroll_offset.x(), scroll_offset.y(), page_scale_factor, |
| page_scale_factor_limits.x(), page_scale_factor_limits.y(), content_width, |
| content_height, top_shown_pix, top_changed, is_mobile_optimized_hint); |
| web_contents_->GetWebContentsAndroid()->UpdateFrameInfo( |
| scroll_offset, content_width, content_height, viewport_size, |
| page_scale_factor, page_scale_factor_limits, top_shown_pix); |
| } |
| |
| void ContentViewCore::ShowSelectPopupMenu(RenderFrameHost* frame, |
| const gfx::Rect& bounds, |
| const std::vector<MenuItem>& items, |
| int selected_item, |
| bool multiple, |
| bool right_aligned) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (j_obj.is_null()) |
| return; |
| |
| // For multi-select list popups we find the list of previous selections by |
| // iterating through the items. But for single selection popups we take the |
| // given |selected_item| as is. |
| ScopedJavaLocalRef<jintArray> selected_array; |
| if (multiple) { |
| std::unique_ptr<jint[]> native_selected_array(new jint[items.size()]); |
| size_t selected_count = 0; |
| for (size_t i = 0; i < items.size(); ++i) { |
| if (items[i].checked) |
| native_selected_array[selected_count++] = i; |
| } |
| |
| selected_array = |
| ScopedJavaLocalRef<jintArray>(env, env->NewIntArray(selected_count)); |
| env->SetIntArrayRegion(selected_array.obj(), 0, selected_count, |
| native_selected_array.get()); |
| } else { |
| selected_array = ScopedJavaLocalRef<jintArray>(env, env->NewIntArray(1)); |
| jint value = selected_item; |
| env->SetIntArrayRegion(selected_array.obj(), 0, 1, &value); |
| } |
| |
| ScopedJavaLocalRef<jintArray> enabled_array(env, |
| env->NewIntArray(items.size())); |
| std::vector<base::string16> labels; |
| labels.reserve(items.size()); |
| for (size_t i = 0; i < items.size(); ++i) { |
| labels.push_back(items[i].label); |
| jint enabled = (items[i].type == MenuItem::GROUP |
| ? POPUP_ITEM_TYPE_GROUP |
| : (items[i].enabled ? POPUP_ITEM_TYPE_ENABLED |
| : POPUP_ITEM_TYPE_DISABLED)); |
| env->SetIntArrayRegion(enabled_array.obj(), i, 1, &enabled); |
| } |
| ScopedJavaLocalRef<jobjectArray> items_array( |
| base::android::ToJavaArrayOfStrings(env, labels)); |
| ui::ViewAndroid* view = GetViewAndroid(); |
| select_popup_ = view->AcquireAnchorView(); |
| const ScopedJavaLocalRef<jobject> popup_view = select_popup_.view(); |
| if (popup_view.is_null()) |
| return; |
| view->SetAnchorRect(popup_view, gfx::RectF(bounds)); |
| Java_ContentViewCore_showSelectPopup( |
| env, j_obj, popup_view, reinterpret_cast<intptr_t>(frame), items_array, |
| enabled_array, multiple, selected_array, right_aligned); |
| } |
| |
| void ContentViewCore::HideSelectPopupMenu() { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (!j_obj.is_null()) |
| Java_ContentViewCore_hideSelectPopup(env, j_obj); |
| select_popup_.Reset(); |
| } |
| |
| void ContentViewCore::OnGestureEventAck(const blink::WebGestureEvent& event, |
| InputEventAckState ack_result) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (j_obj.is_null()) |
| return; |
| |
| switch (event.GetType()) { |
| case WebInputEvent::kGestureFlingStart: |
| if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) { |
| // The view expects the fling velocity in pixels/s. |
| Java_ContentViewCore_onFlingStartEventConsumed(env, j_obj); |
| } else { |
| // If a scroll ends with a fling, a SCROLL_END event is never sent. |
| // However, if that fling went unconsumed, we still need to let the |
| // listeners know that scrolling has ended. |
| Java_ContentViewCore_onScrollEndEventAck(env, j_obj); |
| } |
| break; |
| case WebInputEvent::kGestureFlingCancel: |
| Java_ContentViewCore_onFlingCancelEventAck(env, j_obj); |
| break; |
| case WebInputEvent::kGestureScrollBegin: |
| Java_ContentViewCore_onScrollBeginEventAck(env, j_obj); |
| break; |
| case WebInputEvent::kGestureScrollUpdate: |
| if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
| Java_ContentViewCore_onScrollUpdateGestureConsumed(env, j_obj); |
| break; |
| case WebInputEvent::kGestureScrollEnd: |
| Java_ContentViewCore_onScrollEndEventAck(env, j_obj); |
| break; |
| case WebInputEvent::kGesturePinchBegin: |
| Java_ContentViewCore_onPinchBeginEventAck(env, j_obj); |
| break; |
| case WebInputEvent::kGesturePinchEnd: |
| Java_ContentViewCore_onPinchEndEventAck(env, j_obj); |
| break; |
| case WebInputEvent::kGestureTap: |
| Java_ContentViewCore_onSingleTapEventAck( |
| env, j_obj, ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); |
| break; |
| case WebInputEvent::kGestureLongPress: |
| if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
| Java_ContentViewCore_performLongPressHapticFeedback(env, j_obj); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| bool ContentViewCore::FilterInputEvent(const blink::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; |
| |
| GetViewAndroid()->RequestFocus(); |
| |
| if (event.GetType() == WebInputEvent::kMouseDown) |
| return false; |
| |
| const blink::WebGestureEvent& gesture = |
| static_cast<const blink::WebGestureEvent&>(event); |
| int gesture_type = ToGestureEventType(event.GetType()); |
| return Java_ContentViewCore_filterTapOrPressEvent(env, j_obj, gesture_type, |
| gesture.x * dpi_scale(), |
| gesture.y * dpi_scale()); |
| } |
| |
| void ContentViewCore::RequestDisallowInterceptTouchEvent() { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (!obj.is_null()) |
| Java_ContentViewCore_requestDisallowInterceptTouchEvent(env, obj); |
| } |
| |
| void ContentViewCore::DidStopFlinging() { |
| JNIEnv* env = AttachCurrentThread(); |
| |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (!obj.is_null()) |
| Java_ContentViewCore_onNativeFlingStopped(env, obj); |
| } |
| |
| gfx::Size ContentViewCore::GetViewSize() const { |
| auto size = gfx::ScaleToCeiledSize(GetViewportSizePix(), 1.0f / dpi_scale()); |
| if (DoBrowserControlsShrinkBlinkSize()) |
| size.Enlarge(0, -GetTopControlsHeightDip() - GetBottomControlsHeightDip()); |
| return size; |
| } |
| |
| gfx::Size ContentViewCore::GetViewportSizePix() const { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (j_obj.is_null()) |
| return gfx::Size(); |
| return gfx::Size(Java_ContentViewCore_getViewportWidthPix(env, j_obj), |
| Java_ContentViewCore_getViewportHeightPix(env, j_obj)); |
| } |
| |
| int ContentViewCore::GetTopControlsHeightPix() const { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (j_obj.is_null()) |
| return 0; |
| return Java_ContentViewCore_getTopControlsHeightPix(env, j_obj); |
| } |
| |
| int ContentViewCore::GetBottomControlsHeightPix() const { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (j_obj.is_null()) |
| return 0; |
| return Java_ContentViewCore_getBottomControlsHeightPix(env, j_obj); |
| } |
| |
| bool ContentViewCore::DoBrowserControlsShrinkBlinkSize() const { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (j_obj.is_null()) |
| return false; |
| return Java_ContentViewCore_doBrowserControlsShrinkBlinkSize(env, j_obj); |
| } |
| |
| float ContentViewCore::GetTopControlsHeightDip() const { |
| return GetTopControlsHeightPix() / dpi_scale(); |
| } |
| |
| float ContentViewCore::GetBottomControlsHeightDip() const { |
| return GetBottomControlsHeightPix() / dpi_scale(); |
| } |
| |
| int ContentViewCore::GetMouseWheelMinimumGranularity() const { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_ref_.get(env); |
| if (j_obj.is_null()) |
| return 0; |
| return Java_ContentViewCore_getMouseWheelTickMultiplier(env, j_obj); |
| } |
| |
| void ContentViewCore::SendScreenRectsAndResizeWidget() { |
| RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid(); |
| if (view) { |
| // |SendScreenRects()| indirectly calls GetViewSize() that asks Java layer. |
| web_contents_->SendScreenRects(); |
| view->WasResized(); |
| } |
| } |
| |
| ui::WindowAndroid* ContentViewCore::GetWindowAndroid() const { |
| return GetViewAndroid()->GetWindowAndroid(); |
| } |
| |
| ui::ViewAndroid* ContentViewCore::GetViewAndroid() const { |
| return web_contents_->GetView()->GetNativeView(); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Methods called from Java via JNI |
| // ---------------------------------------------------------------------------- |
| |
| void ContentViewCore::SelectPopupMenuItems( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong selectPopupSourceFrame, |
| const JavaParamRef<jintArray>& indices) { |
| RenderFrameHostImpl* rfhi = |
| reinterpret_cast<RenderFrameHostImpl*>(selectPopupSourceFrame); |
| DCHECK(rfhi); |
| if (indices == NULL) { |
| rfhi->DidCancelPopupMenu(); |
| return; |
| } |
| |
| int selected_count = env->GetArrayLength(indices); |
| std::vector<int> selected_indices; |
| jint* indices_ptr = env->GetIntArrayElements(indices, NULL); |
| for (int i = 0; i < selected_count; ++i) |
| selected_indices.push_back(indices_ptr[i]); |
| env->ReleaseIntArrayElements(indices, indices_ptr, JNI_ABORT); |
| rfhi->DidSelectPopupMenuItems(selected_indices); |
| } |
| |
| WebContents* ContentViewCore::GetWebContents() const { |
| return web_contents_; |
| } |
| |
| void ContentViewCore::SetFocus(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean focused) { |
| SetFocusInternal(focused); |
| } |
| |
| void ContentViewCore::SetDIPScale(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jfloat dpi_scale) { |
| if (dpi_scale_ == dpi_scale) |
| return; |
| |
| dpi_scale_ = dpi_scale; |
| SendScreenRectsAndResizeWidget(); |
| } |
| |
| void ContentViewCore::SetFocusInternal(bool focused) { |
| if (!GetRenderWidgetHostViewAndroid()) |
| return; |
| |
| if (focused) |
| GetRenderWidgetHostViewAndroid()->GotFocus(); |
| else |
| GetRenderWidgetHostViewAndroid()->LostFocus(); |
| } |
| |
| void ContentViewCore::SendOrientationChangeEvent( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jint orientation) { |
| if (device_orientation_ != orientation) { |
| base::RecordAction(base::UserMetricsAction("ScreenOrientationChange")); |
| device_orientation_ = orientation; |
| SendOrientationChangeEventInternal(); |
| } |
| } |
| |
| WebGestureEvent ContentViewCore::MakeGestureEvent(WebInputEvent::Type type, |
| int64_t time_ms, |
| float x, |
| float y) const { |
| return WebGestureEventBuilder::Build(type, time_ms / 1000.0, x / dpi_scale(), |
| y / dpi_scale()); |
| } |
| |
| void ContentViewCore::SendGestureEvent(const blink::WebGestureEvent& event) { |
| RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); |
| if (rwhv) |
| rwhv->SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::ScrollBegin(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms, |
| jfloat x, |
| jfloat y, |
| jfloat hintx, |
| jfloat hinty, |
| jboolean target_viewport, |
| jboolean from_gamepad) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGestureScrollBegin, time_ms, x, y); |
| event.data.scroll_begin.delta_x_hint = hintx / dpi_scale(); |
| event.data.scroll_begin.delta_y_hint = hinty / dpi_scale(); |
| event.data.scroll_begin.target_viewport = target_viewport; |
| |
| if (from_gamepad) |
| event.source_device = blink::kWebGestureDeviceSyntheticAutoscroll; |
| |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::ScrollEnd(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGestureScrollEnd, time_ms, 0, 0); |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::ScrollBy(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms, |
| jfloat x, |
| jfloat y, |
| jfloat dx, |
| jfloat dy) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGestureScrollUpdate, time_ms, x, y); |
| event.data.scroll_update.delta_x = -dx / dpi_scale(); |
| event.data.scroll_update.delta_y = -dy / dpi_scale(); |
| |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::FlingStart(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms, |
| jfloat x, |
| jfloat y, |
| jfloat vx, |
| jfloat vy, |
| jboolean target_viewport, |
| jboolean from_gamepad) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGestureFlingStart, time_ms, x, y); |
| event.data.fling_start.velocity_x = vx / dpi_scale(); |
| event.data.fling_start.velocity_y = vy / dpi_scale(); |
| event.data.fling_start.target_viewport = target_viewport; |
| |
| if (from_gamepad) |
| event.source_device = blink::kWebGestureDeviceSyntheticAutoscroll; |
| |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::FlingCancel(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms, |
| jboolean from_gamepad) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGestureFlingCancel, time_ms, 0, 0); |
| event.data.fling_cancel.prevent_boosting = true; |
| |
| if (from_gamepad) { |
| event.data.fling_cancel.target_viewport = true; |
| event.source_device = blink::kWebGestureDeviceSyntheticAutoscroll; |
| } |
| |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::DoubleTap(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms, |
| jfloat x, |
| jfloat y) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGestureDoubleTap, time_ms, x, y); |
| // Set the tap count to 1 even for DoubleTap, in order to be consistent with |
| // double tap behavior on a mobile viewport. See crbug.com/234986 for context. |
| event.data.tap.tap_count = 1; |
| |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::PinchBegin(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms, |
| jfloat x, |
| jfloat y) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGesturePinchBegin, time_ms, x, y); |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::PinchEnd(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms) { |
| WebGestureEvent event = |
| MakeGestureEvent(WebInputEvent::kGesturePinchEnd, time_ms, 0, 0); |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::PinchBy(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong time_ms, |
| jfloat anchor_x, |
| jfloat anchor_y, |
| jfloat delta) { |
| WebGestureEvent event = MakeGestureEvent(WebInputEvent::kGesturePinchUpdate, |
| time_ms, anchor_x, anchor_y); |
| event.data.pinch_update.scale = delta; |
| |
| SendGestureEvent(event); |
| } |
| |
| void ContentViewCore::SetTextHandlesTemporarilyHidden( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean hidden) { |
| RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); |
| if (rwhv) |
| rwhv->SetTextHandlesTemporarilyHidden(hidden); |
| } |
| |
| void ContentViewCore::ResetGestureDetection(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); |
| if (rwhv) |
| rwhv->ResetGestureDetection(); |
| } |
| |
| void ContentViewCore::SetDoubleTapSupportEnabled( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean enabled) { |
| RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); |
| if (rwhv) |
| rwhv->SetDoubleTapSupportEnabled(enabled); |
| } |
| |
| void ContentViewCore::SetMultiTouchZoomSupportEnabled( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean enabled) { |
| RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); |
| if (rwhv) |
| rwhv->SetMultiTouchZoomSupportEnabled(enabled); |
| } |
| |
| void ContentViewCore::OnTouchDown( |
| const base::android::ScopedJavaLocalRef<jobject>& event) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (obj.is_null()) |
| return; |
| Java_ContentViewCore_onTouchDown(env, obj, event); |
| } |
| |
| void ContentViewCore::WasResized(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| SendScreenRectsAndResizeWidget(); |
| } |
| |
| void ContentViewCore::SetTextTrackSettings( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean textTracksEnabled, |
| const JavaParamRef<jstring>& textTrackBackgroundColor, |
| const JavaParamRef<jstring>& textTrackFontFamily, |
| const JavaParamRef<jstring>& textTrackFontStyle, |
| const JavaParamRef<jstring>& textTrackFontVariant, |
| const JavaParamRef<jstring>& textTrackTextColor, |
| const JavaParamRef<jstring>& textTrackTextShadow, |
| const JavaParamRef<jstring>& textTrackTextSize) { |
| FrameMsg_TextTrackSettings_Params params; |
| params.text_tracks_enabled = textTracksEnabled; |
| params.text_track_background_color = |
| ConvertJavaStringToUTF8(env, textTrackBackgroundColor); |
| params.text_track_font_family = |
| ConvertJavaStringToUTF8(env, textTrackFontFamily); |
| params.text_track_font_style = |
| ConvertJavaStringToUTF8(env, textTrackFontStyle); |
| params.text_track_font_variant = |
| ConvertJavaStringToUTF8(env, textTrackFontVariant); |
| params.text_track_text_color = |
| ConvertJavaStringToUTF8(env, textTrackTextColor); |
| params.text_track_text_shadow = |
| ConvertJavaStringToUTF8(env, textTrackTextShadow); |
| params.text_track_text_size = ConvertJavaStringToUTF8(env, textTrackTextSize); |
| web_contents_->GetMainFrame()->SetTextTrackSettings(params); |
| } |
| |
| bool ContentViewCore::IsFullscreenRequiredForOrientationLock() const { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (obj.is_null()) |
| return true; |
| return Java_ContentViewCore_isFullscreenRequiredForOrientationLock(env, obj); |
| } |
| |
| void ContentViewCore::SendOrientationChangeEventInternal() { |
| RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); |
| if (rwhv) |
| rwhv->UpdateScreenInfo(GetViewAndroid()); |
| |
| static_cast<WebContentsImpl*>(web_contents())->OnScreenOrientationChange(); |
| } |
| |
| jint ContentViewCore::GetCurrentRenderProcessId( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return GetRenderProcessIdFromRenderViewHost( |
| web_contents_->GetRenderViewHost()); |
| } |
| |
| jboolean ContentViewCore::UsingSynchronousCompositing( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| return content::GetContentClient()->UsingSynchronousCompositing(); |
| } |
| |
| void ContentViewCore::SetBackgroundOpaque(JNIEnv* env, |
| const JavaParamRef<jobject>& jobj, |
| jboolean opaque) { |
| if (GetRenderWidgetHostViewAndroid()) { |
| if (opaque) |
| GetRenderWidgetHostViewAndroid()->SetBackgroundColorToDefault(); |
| else |
| GetRenderWidgetHostViewAndroid()->SetBackgroundColor(SK_ColorTRANSPARENT); |
| } |
| } |
| |
| void ContentViewCore::HidePopupsAndPreserveSelection() { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| if (obj.is_null()) |
| return; |
| |
| Java_ContentViewCore_hidePopupsAndPreserveSelection(env, obj); |
| } |
| |
| void ContentViewCore::WebContentsDestroyed() { |
| WebContentsViewAndroid* wcva = static_cast<WebContentsViewAndroid*>( |
| static_cast<WebContentsImpl*>(web_contents())->GetView()); |
| DCHECK(wcva); |
| wcva->SetContentViewCore(NULL); |
| } |
| |
| // This is called for each ContentView. |
| jlong Init(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& jweb_contents, |
| const JavaParamRef<jobject>& jview_android_delegate, |
| jlong jwindow_android, |
| jfloat dip_scale) { |
| WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( |
| WebContents::FromJavaWebContents(jweb_contents)); |
| CHECK(web_contents) |
| << "A ContentViewCore should be created with a valid WebContents."; |
| ui::ViewAndroid* view_android = web_contents->GetView()->GetNativeView(); |
| view_android->SetDelegate(jview_android_delegate); |
| |
| ui::WindowAndroid* window_android = |
| reinterpret_cast<ui::WindowAndroid*>(jwindow_android); |
| DCHECK(window_android); |
| window_android->AddChild(view_android); |
| |
| ContentViewCore* view = |
| new ContentViewCore(env, obj, web_contents, dip_scale); |
| return reinterpret_cast<intptr_t>(view); |
| } |
| |
| static ScopedJavaLocalRef<jobject> FromWebContentsAndroid( |
| JNIEnv* env, |
| const JavaParamRef<jclass>& clazz, |
| const JavaParamRef<jobject>& jweb_contents) { |
| WebContents* web_contents = WebContents::FromJavaWebContents(jweb_contents); |
| if (!web_contents) |
| return ScopedJavaLocalRef<jobject>(); |
| |
| ContentViewCore* view = ContentViewCore::FromWebContents(web_contents); |
| if (!view) |
| return ScopedJavaLocalRef<jobject>(); |
| |
| return view->GetJavaObject(); |
| } |
| |
| } // namespace content |