Commit 445de0ec authored by hush's avatar hush Committed by Commit bot

Support dragging texts into Android WebView.

WebView gets callback 'onDragEvent' when something is dragged into the WebView.
This CL extracts the texts (if any) and location from the DragEvent and creates
DropData which is Chromium's internal representation of dragged data.

The event plumbing path is WebView#onDragEvent --> AwContents#onDragEvent -->
ContentViewCore --> WebContentsViewAndroid.

This CL also uses a new blink::WebView API that performs dropping with DropData,
because on Android, the DropData is only available when the drop is performed,
not when the drag is started.

BUG=584789

Review-Url: https://codereview.chromium.org/1728193002
Cr-Commit-Position: refs/heads/master@{#403417}
parent 760c56de
...@@ -3033,8 +3033,7 @@ public class AwContents implements SmartClipProvider, ...@@ -3033,8 +3033,7 @@ public class AwContents implements SmartClipProvider,
@Override @Override
public boolean onDragEvent(DragEvent event) { public boolean onDragEvent(DragEvent event) {
// TODO(hush): implement this. crbug.com/584789 return mContentViewCore.onDragEvent(event);
return false;
} }
@Override @Override
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
#include "cc/layers/layer.h" #include "cc/layers/layer.h"
...@@ -52,9 +53,11 @@ ...@@ -52,9 +53,11 @@
#include "content/public/common/menu_item.h" #include "content/public/common/menu_item.h"
#include "content/public/common/user_agent.h" #include "content/public/common/user_agent.h"
#include "jni/ContentViewCore_jni.h" #include "jni/ContentViewCore_jni.h"
#include "jni/DragEvent_jni.h"
#include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/android/view_android.h" #include "ui/android/view_android.h"
#include "ui/android/window_android.h" #include "ui/android/window_android.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/events/android/motion_event_android.h" #include "ui/events/android/motion_event_android.h"
#include "ui/events/blink/blink_event_util.h" #include "ui/events/blink/blink_event_util.h"
#include "ui/events/event_utils.h" #include "ui/events/event_utils.h"
...@@ -1473,6 +1476,64 @@ void ContentViewCoreImpl::SetBackgroundOpaque(JNIEnv* env, ...@@ -1473,6 +1476,64 @@ void ContentViewCoreImpl::SetBackgroundOpaque(JNIEnv* env,
} }
} }
void ContentViewCoreImpl::OnDragEvent(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jobj,
jint action,
jint x,
jint y,
jint screen_x,
jint screen_y,
const base::android::JavaParamRef<jobjectArray>& j_mimeTypes,
const base::android::JavaParamRef<jstring>& j_content) {
WebContentsViewAndroid* wcva = static_cast<WebContentsViewAndroid*>(
static_cast<WebContentsImpl*>(web_contents())->GetView());
const gfx::Point location(x, y);
const gfx::Point screen_location(screen_x, screen_y);
std::vector<base::string16> mime_types;
base::android::AppendJavaStringArrayToStringVector(env, j_mimeTypes,
&mime_types);
switch (action) {
case JNI_DragEvent::ACTION_DRAG_ENTERED: {
std::vector<DropData::Metadata> metadata;
for (const base::string16& mime_type : mime_types) {
metadata.push_back(DropData::Metadata::CreateForMimeType(
DropData::Kind::STRING, mime_type));
}
wcva->OnDragEntered(metadata, location, screen_location);
break;
}
case JNI_DragEvent::ACTION_DRAG_LOCATION: {
wcva->OnDragUpdated(location, screen_location);
break;
}
case JNI_DragEvent::ACTION_DROP: {
base::string16 text_to_drop = ConvertJavaStringToUTF16(env, j_content);
DropData drop_data;
drop_data.did_originate_from_renderer = false;
for (const base::string16& mime_type : mime_types) {
if (base::EqualsASCII(mime_type, ui::Clipboard::kMimeTypeURIList)) {
drop_data.url = GURL(text_to_drop);
} else if (base::EqualsASCII(mime_type, ui::Clipboard::kMimeTypeText)) {
drop_data.text = base::NullableString16(text_to_drop, false);
} else {
drop_data.html = base::NullableString16(text_to_drop, false);
}
}
wcva->OnPerformDrop(&drop_data, location, screen_location);
break;
}
case JNI_DragEvent::ACTION_DRAG_EXITED:
wcva->OnDragExited();
break;
default: // STARTED and ENDED. Nothing meaningful to do.
break;
}
}
void ContentViewCoreImpl::RequestTextSurroundingSelection( void ContentViewCoreImpl::RequestTextSurroundingSelection(
int max_length, int max_length,
const base::Callback< const base::Callback<
...@@ -1584,7 +1645,7 @@ static ScopedJavaLocalRef<jobject> FromWebContentsAndroid( ...@@ -1584,7 +1645,7 @@ static ScopedJavaLocalRef<jobject> FromWebContentsAndroid(
} }
bool RegisterContentViewCore(JNIEnv* env) { bool RegisterContentViewCore(JNIEnv* env) {
return RegisterNativesImpl(env); return RegisterNativesImpl(env) && JNI_DragEvent::RegisterNativesImpl(env);
} }
} // namespace content } // namespace content
...@@ -282,6 +282,15 @@ class ContentViewCoreImpl : public ContentViewCore, ...@@ -282,6 +282,15 @@ class ContentViewCoreImpl : public ContentViewCore,
void SetBackgroundOpaque(JNIEnv* env, void SetBackgroundOpaque(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jobj, const base::android::JavaParamRef<jobject>& jobj,
jboolean opaque); jboolean opaque);
void OnDragEvent(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jobj,
jint action,
jint x,
jint y,
jint screen_x,
jint screen_y,
const base::android::JavaParamRef<jobjectArray>& j_mimeTypes,
const base::android::JavaParamRef<jstring>& j_content);
jint GetCurrentRenderProcessId( jint GetCurrentRenderProcessId(
JNIEnv* env, JNIEnv* env,
......
...@@ -13,8 +13,10 @@ ...@@ -13,8 +13,10 @@
#include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/drop_data.h"
namespace content { namespace content {
WebContentsView* CreateWebContentsView( WebContentsView* CreateWebContentsView(
WebContentsImpl* web_contents, WebContentsImpl* web_contents,
WebContentsViewDelegate* delegate, WebContentsViewDelegate* delegate,
...@@ -199,6 +201,32 @@ void WebContentsViewAndroid::UpdateDragCursor(blink::WebDragOperation op) { ...@@ -199,6 +201,32 @@ void WebContentsViewAndroid::UpdateDragCursor(blink::WebDragOperation op) {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
void WebContentsViewAndroid::OnDragEntered(
const std::vector<DropData::Metadata>& metadata,
const gfx::Point& location,
const gfx::Point& screen_location) {
web_contents_->GetRenderViewHost()->DragTargetDragEnterWithMetaData(
metadata, location, screen_location, blink::WebDragOperationCopy, 0);
}
void WebContentsViewAndroid::OnDragUpdated(const gfx::Point& location,
const gfx::Point& screen_location) {
web_contents_->GetRenderViewHost()->DragTargetDragOver(
location, screen_location, blink::WebDragOperationCopy, 0);
}
void WebContentsViewAndroid::OnDragExited() {
web_contents_->GetRenderViewHost()->DragTargetDragLeave();
}
void WebContentsViewAndroid::OnPerformDrop(DropData* drop_data,
const gfx::Point& location,
const gfx::Point& screen_location) {
web_contents_->GetRenderViewHost()->FilterDropData(drop_data);
web_contents_->GetRenderViewHost()->DragTargetDrop(*drop_data, location,
screen_location, 0);
}
void WebContentsViewAndroid::GotFocus() { void WebContentsViewAndroid::GotFocus() {
// This is only used in the views FocusManager stuff but it bleeds through // This is only used in the views FocusManager stuff but it bleeds through
// all subclasses. http://crbug.com/21875 // all subclasses. http://crbug.com/21875
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "content/browser/web_contents/web_contents_view.h" #include "content/browser/web_contents/web_contents_view.h"
#include "content/public/browser/web_contents_view_delegate.h" #include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/common/context_menu_params.h" #include "content/public/common/context_menu_params.h"
#include "content/public/common/drop_data.h"
#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/rect_f.h"
namespace content { namespace content {
...@@ -76,6 +77,16 @@ class WebContentsViewAndroid : public WebContentsView, ...@@ -76,6 +77,16 @@ class WebContentsViewAndroid : public WebContentsView,
void GotFocus() override; void GotFocus() override;
void TakeFocus(bool reverse) override; void TakeFocus(bool reverse) override;
void OnDragEntered(const std::vector<DropData::Metadata>& metadata,
const gfx::Point& location,
const gfx::Point& screen_location);
void OnDragUpdated(const gfx::Point& location,
const gfx::Point& screen_location);
void OnDragExited();
void OnPerformDrop(DropData* drop_data,
const gfx::Point& location,
const gfx::Point& screen_location);
private: private:
// The WebContents whose contents we display. // The WebContents whose contents we display.
WebContentsImpl* web_contents_; WebContentsImpl* web_contents_;
......
...@@ -282,6 +282,7 @@ generate_jar_jni("jar_jni") { ...@@ -282,6 +282,7 @@ generate_jar_jni("jar_jni") {
jni_package = "content" jni_package = "content"
classes = [ classes = [
"java/util/HashSet.class", "java/util/HashSet.class",
"android/view/DragEvent.class",
"android/view/MotionEvent.class", "android/view/MotionEvent.class",
] ]
} }
......
...@@ -9,6 +9,8 @@ import android.annotation.TargetApi; ...@@ -9,6 +9,8 @@ import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.SearchManager; import android.app.SearchManager;
import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.ViewNode;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
...@@ -27,6 +29,7 @@ import android.text.TextUtils; ...@@ -27,6 +29,7 @@ import android.text.TextUtils;
import android.util.Pair; import android.util.Pair;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;
...@@ -106,7 +109,7 @@ import java.util.Map.Entry; ...@@ -106,7 +109,7 @@ import java.util.Map.Entry;
@JNINamespace("content") @JNINamespace("content")
public class ContentViewCore implements AccessibilityStateChangeListener, ScreenOrientationObserver, public class ContentViewCore implements AccessibilityStateChangeListener, ScreenOrientationObserver,
SystemCaptioningBridge.SystemCaptioningBridgeListener { SystemCaptioningBridge.SystemCaptioningBridgeListener {
private static final String TAG = "cr.ContentViewCore"; private static final String TAG = "cr_ContentViewCore";
// Used to avoid enabling zooming in / out if resulting zooming will // Used to avoid enabling zooming in / out if resulting zooming will
// produce little visible difference. // produce little visible difference.
...@@ -3275,6 +3278,54 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Screen ...@@ -3275,6 +3278,54 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Screen
} }
} }
/**
* @see View#onDragEvent(DragEvent)
*/
public boolean onDragEvent(DragEvent event) {
if (mNativeContentViewCore == 0) return false;
ClipDescription clipDescription = event.getClipDescription();
if (clipDescription == null && event.getAction() != DragEvent.ACTION_DRAG_ENDED) {
Log.e(TAG, "Null clipDescription when the drag is not ended.");
return false;
}
// text/* will match text/uri-list, text/html, text/plain.
String[] mimeTypes =
clipDescription == null ? new String[0] : clipDescription.filterMimeTypes("text/*");
if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
// TODO(hush): support dragging more than just text.
return mimeTypes.length > 0;
}
StringBuilder content = new StringBuilder("");
if (event.getAction() == DragEvent.ACTION_DROP) {
// TODO(hush): obtain dragdrop permissions (via reflection?), when dragging files into
// Chrome/WebView is supported. Not necessary to do so for now, because only text
// dragging is supported.
ClipData clipData = event.getClipData();
final int itemCount = clipData.getItemCount();
for (int i = 0; i < itemCount; i++) {
ClipData.Item item = clipData.getItemAt(i);
content.append(item.coerceToStyledText(mContainerView.getContext()));
}
}
float scale = (float) DeviceDisplayInfo.create(mContainerView.getContext()).getDIPScale();
int[] locationOnScreen = new int[2];
mContainerView.getLocationOnScreen(locationOnScreen);
int x = (int) (event.getX() / scale);
int y = (int) (event.getY() / scale);
int screenX = (int) ((event.getX() + locationOnScreen[0]) / scale);
int screenY = (int) ((event.getY() + locationOnScreen[1]) / scale);
nativeOnDragEvent(mNativeContentViewCore, event.getAction(), x, y, screenX, screenY,
mimeTypes, content.toString());
return true;
}
/** /**
* Offer a long press gesture to the embedding View, primarily for WebView compatibility. * Offer a long press gesture to the embedding View, primarily for WebView compatibility.
* *
...@@ -3514,4 +3565,6 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Screen ...@@ -3514,4 +3565,6 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Screen
int x, int y, int w, int h); int x, int y, int w, int h);
private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque); private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);
private native void nativeOnDragEvent(long nativeContentViewCoreImpl, int action, int x, int y,
int screenX, int screenY, String[] mimeTypes, String content);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment