Bug 1586471 - Part 2. Implement nsDragService on GeckoView. r=geckoview-reviewers,amejiamarmol

This implementation supports
- HTML drag & drop API.
- Drop and drop for text/plain or text/html from/to external application.

Differential Revision: https://phabricator.services.mozilla.com/D197330
This commit is contained in:
Makoto Kato 2024-01-28 07:49:53 +00:00
Родитель 7c13fc91e5
Коммит d88b0348e7
26 изменённых файлов: 1131 добавлений и 51 удалений

Просмотреть файл

@ -24,6 +24,7 @@ import android.print.PrintDocumentAdapter;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.ActionMode;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@ -2089,6 +2090,7 @@ package org.mozilla.geckoview {
@UiThread public class PanZoomController {
ctor protected PanZoomController(GeckoSession);
method public float getScrollFactor();
method public boolean onDragEvent(@NonNull DragEvent);
method public void onMotionEvent(@NonNull MotionEvent);
method public void onMouseEvent(@NonNull MotionEvent);
method public void onTouchEvent(@NonNull MotionEvent);

Просмотреть файл

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<script>
document.addEventListener("DOMContentLoaded", () =>
document
.querySelector("#drop")
.addEventListener("dragover", e => e.preventDefault())
);
</script>
</head>
<body>
<img
id="drag"
draggable="true"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2PwmvDoPwAFoAK895OuoAAAAABJRU5ErkJggg=="
width="200"
height="100"
/>
<br />
<div id="drop" style="border: 1px solid red; width: 200px; height: 100px">
drop
</div>
</body>
</html>

Просмотреть файл

@ -4,7 +4,7 @@
<title>Hello, world!</title>
<meta name="viewport" content="initial-scale=1.0" />
</head>
<body style="height: 100%" onmousemove="window.location.reload()">
<body style="height: 100%">
<p>Hello, world!</p>
</body>
</html>

Просмотреть файл

@ -38,6 +38,7 @@ open class BaseSessionTest(
const val CLICK_TO_RELOAD_HTML_PATH = "/assets/www/clickToReload.html"
const val CLIPBOARD_READ_HTML_PATH = "/assets/www/clipboard_read.html"
const val CONTENT_CRASH_URL = "about:crashcontent"
const val DND_HTML_PATH = "/assets/www/dnd.html"
const val DOWNLOAD_HTML_PATH = "/assets/www/download.html"
const val FORM_BLANK_HTML_PATH = "/assets/www/form_blank.html"
const val FORMS_HTML_PATH = "/assets/www/forms.html"
@ -233,6 +234,9 @@ open class BaseSessionTest(
fun GeckoSession.synthesizeTap(x: Int, y: Int) =
sessionRule.synthesizeTap(this, x, y)
fun GeckoSession.synthesizeMouse(downTime: Long, action: Int, x: Int, y: Int, buttonState: Int) =
sessionRule.synthesizeMouse(this, downTime, action, x, y, buttonState)
fun GeckoSession.synthesizeMouseMove(x: Int, y: Int) =
sessionRule.synthesizeMouseMove(this, x, y)

Просмотреть файл

@ -0,0 +1,113 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.geckoview.test
import android.content.ClipData
import android.os.Build
import android.os.SystemClock
import android.view.DragEvent
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
import org.hamcrest.Matchers.equalTo
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@MediumTest
class DragAndDropTest : BaseSessionTest() {
// DragEvent has no constructor, so we create it via Java reflection.
fun createDragEvent(action: Int, x: Float = 0.0F, y: Float = 0.0F): DragEvent {
val method = DragEvent::class.java.getDeclaredMethod("obtain")
method.setAccessible(true)
val dragEvent = method.invoke(null) as DragEvent
val fieldAction = DragEvent::class.java.getDeclaredField("mAction")
fieldAction.setAccessible(true)
fieldAction.set(dragEvent, action)
if (listOf(DragEvent.ACTION_DRAG_STARTED, DragEvent.ACTION_DRAG_LOCATION, DragEvent.ACTION_DROP).contains(action)) {
val fieldX = DragEvent::class.java.getDeclaredField("mX")
fieldX.setAccessible(true)
fieldX.set(dragEvent, x)
val fieldY = DragEvent::class.java.getDeclaredField("mY")
fieldY.setAccessible(true)
fieldY.set(dragEvent, y)
}
if (action == DragEvent.ACTION_DROP) {
val clipData = ClipData.newPlainText("label", "foo")
val fieldClipData = DragEvent::class.java.getDeclaredField("mClipData")
fieldClipData.setAccessible(true)
fieldClipData.set(dragEvent, clipData)
var clipDescription = clipData.getDescription()
val fieldClipDescription = DragEvent::class.java.getDeclaredField("mClipDescription")
fieldClipDescription.setAccessible(true)
fieldClipDescription.set(dragEvent, clipDescription)
}
return dragEvent
}
@WithDisplay(width = 300, height = 300)
@Test
fun dragStartTest() {
mainSession.loadTestPath(DND_HTML_PATH)
sessionRule.waitForPageStop()
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(r => document.querySelector('#drag').addEventListener('dragstart', r, { once: true }))
""".trimIndent(),
)
val downTime = SystemClock.uptimeMillis()
mainSession.synthesizeMouse(downTime, MotionEvent.ACTION_DOWN, 50, 20, MotionEvent.BUTTON_PRIMARY)
for (y in 30..50) {
mainSession.synthesizeMouse(downTime, MotionEvent.ACTION_MOVE, 50, y, MotionEvent.BUTTON_PRIMARY)
}
mainSession.synthesizeMouse(downTime, MotionEvent.ACTION_UP, 50, 50, 0)
promise.value
assertThat("drag event is started correctly", true, equalTo(true))
}
@WithDisplay(width = 300, height = 300)
@Test
fun dropFromExternalTest() {
mainSession.loadTestPath(DND_HTML_PATH)
sessionRule.waitForPageStop()
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(
r => document.querySelector('#drop').addEventListener(
'drop',
e => r(e.dataTransfer.getData('text/plain')),
{ once: true }))
""".trimIndent(),
)
// Android doesn't fire MotionEvent during drag and drop.
val dragStartEvent = createDragEvent(DragEvent.ACTION_DRAG_STARTED)
mainSession.panZoomController.onDragEvent(dragStartEvent)
val dragEnteredEvent = createDragEvent(DragEvent.ACTION_DRAG_ENTERED)
mainSession.panZoomController.onDragEvent(dragEnteredEvent)
listOf(150.0F, 250.0F).forEach {
val dragLocationEvent = createDragEvent(DragEvent.ACTION_DRAG_LOCATION, 100.0F, it)
mainSession.panZoomController.onDragEvent(dragLocationEvent)
}
val dropEvent = createDragEvent(DragEvent.ACTION_DROP, 100.0F, 250.0F)
mainSession.panZoomController.onDragEvent(dropEvent)
val dragEndedEvent = createDragEvent(DragEvent.ACTION_DRAG_ENDED)
mainSession.panZoomController.onDragEvent(dragEndedEvent)
assertThat("drop event is fired correctly", promise.value as String, equalTo("foo"))
}
}

Просмотреть файл

@ -6,6 +6,8 @@ package org.mozilla.geckoview.test
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.hamcrest.Matchers.* // ktlint-disable no-wildcard-imports
@ -1462,12 +1464,29 @@ class GeckoSessionTestRuleTest : BaseSessionTest(noErrorCollector = true) {
mainSession.waitForPageStop()
}
@WithDisplay(width = 100, height = 100)
@Test
fun synthesizeMouse() {
mainSession.loadTestPath(MOUSE_TO_RELOAD_HTML_PATH)
mainSession.waitForPageStop()
val time = SystemClock.uptimeMillis()
mainSession.evaluateJS("document.body.addEventListener('mousedown', () => { window.location.reload() })")
mainSession.synthesizeMouse(time, MotionEvent.ACTION_DOWN, 50, 50, MotionEvent.BUTTON_PRIMARY)
mainSession.waitForPageStop()
mainSession.evaluateJS("document.body.addEventListener('mouseup', () => { window.location.reload() })")
mainSession.synthesizeMouse(time, MotionEvent.ACTION_UP, 50, 50, 0)
mainSession.waitForPageStop()
}
@WithDisplay(width = 100, height = 100)
@Test
fun synthesizeMouseMove() {
mainSession.loadTestPath(MOUSE_TO_RELOAD_HTML_PATH)
mainSession.waitForPageStop()
mainSession.evaluateJS("document.body.addEventListener('mousemove', () => { window.location.reload() })")
mainSession.synthesizeMouseMove(50, 50)
mainSession.waitForPageStop()
}

Просмотреть файл

@ -2075,14 +2075,23 @@ public class GeckoSessionTestRule implements TestRule {
}
/**
* Synthesize a mouse move event at the specified location using the main session. The session
* must have been created with a display.
* Synthesize a mouse event at the specified location using the main session. The session must
* have been created with a display.
*
* @param session Target session
* @param downTime A time when any buttons are down
* @param action An action such as MotionEvent.ACTION_DOWN
* @param x X coordinate
* @param y Y coordinate
* @param buttonState A button stats such as MotionEvent.BUTTON_PRIMARY
*/
public void synthesizeMouseMove(final @NonNull GeckoSession session, final int x, final int y) {
public void synthesizeMouse(
final @NonNull GeckoSession session,
final long downTime,
final int action,
final int x,
final int y,
final int buttonState) {
final MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
pointerProperty.id = 0;
pointerProperty.toolType = MotionEvent.TOOL_TYPE_MOUSE;
@ -2096,17 +2105,16 @@ public class GeckoSessionTestRule implements TestRule {
final MotionEvent.PointerCoords[] pointerCoords =
new MotionEvent.PointerCoords[] {pointerCoord};
final long moveTime = SystemClock.uptimeMillis();
final MotionEvent moveEvent =
MotionEvent.obtain(
moveTime,
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_HOVER_MOVE,
action,
1,
pointerProperties,
pointerCoords,
0,
0,
buttonState,
1.0f,
1.0f,
0,
@ -2116,6 +2124,19 @@ public class GeckoSessionTestRule implements TestRule {
session.getPanZoomController().onTouchEvent(moveEvent);
}
/**
* Synthesize a mouse move event at the specified location using the main session. The session
* must have been created with a display.
*
* @param session Target session
* @param x X coordinate
* @param y Y coordinate
*/
public void synthesizeMouseMove(final @NonNull GeckoSession session, final int x, final int y) {
final long moveTime = SystemClock.uptimeMillis();
synthesizeMouse(session, moveTime, MotionEvent.ACTION_HOVER_MOVE, x, y, 0);
}
/**
* Simulates a press to the Home button, causing the application to go to onPause. NB: Some time
* must elapse for the event to fully occur.

Просмотреть файл

@ -0,0 +1,231 @@
/* -*- Mode: Java; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipDescription;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.mozilla.gecko.annotation.WrapForJNI;
@TargetApi(Build.VERSION_CODES.N)
public class GeckoDragAndDrop {
private static final String LOGTAG = "GeckoDragAndDrop";
private static final boolean DEBUG = false;
/** The drag/drop data is nsITransferable and stored into nsDragService. */
private static final String MIMETYPE_NATIVE = "application/x-moz-draganddrop";
private static ClipData sDragClipData;
private static float sX;
private static float sY;
private static boolean mEndingSession;
private static class DrawDragImage extends View.DragShadowBuilder {
private final Bitmap mBitmap;
public DrawDragImage(final Bitmap bitmap) {
mBitmap = bitmap;
}
@Override
public void onProvideShadowMetrics(final Point outShadowSize, final Point outShadowTouchPoint) {
if (mBitmap == null) {
super.onProvideShadowMetrics(outShadowSize, outShadowTouchPoint);
return;
}
outShadowSize.set(mBitmap.getWidth(), mBitmap.getHeight());
}
@Override
public void onDrawShadow(final Canvas canvas) {
if (mBitmap == null) {
super.onDrawShadow(canvas);
return;
}
canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
}
}
@WrapForJNI
public static class DropData {
public final String mimeType;
public final String text;
@WrapForJNI(skip = true)
public DropData() {
this.mimeType = MIMETYPE_NATIVE;
this.text = null;
}
@WrapForJNI(skip = true)
public DropData(final String mimeType, final String text) {
this.mimeType = mimeType;
this.text = text;
}
}
public static void startDragAndDrop(final View view, final Bitmap bitmap) {
view.startDragAndDrop(sDragClipData, new DrawDragImage(bitmap), null, View.DRAG_FLAG_GLOBAL);
sDragClipData = null;
}
public static void updateDragImage(final View view, final Bitmap bitmap) {
view.updateDragShadow(new DrawDragImage(bitmap));
}
public static boolean onDragEvent(@NonNull final DragEvent event) {
if (DEBUG) {
final StringBuilder sb = new StringBuilder("onDragEvent: action=");
sb.append(event.getAction())
.append(", x=")
.append(event.getX())
.append(", y=")
.append(event.getY());
Log.d(LOGTAG, sb.toString());
}
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mEndingSession = false;
sX = event.getX();
sY = event.getY();
break;
case DragEvent.ACTION_DRAG_LOCATION:
sX = event.getX();
sY = event.getY();
break;
case DragEvent.ACTION_DROP:
sX = event.getX();
sY = event.getY();
break;
case DragEvent.ACTION_DRAG_ENDED:
mEndingSession = true;
return true;
default:
break;
}
if (mEndingSession) {
return false;
}
return true;
}
public static float getLocationX() {
return sX;
}
public static float getLocationY() {
return sY;
}
/**
* Create drop data by DragEvent.ACTION_DROP. This ClipData will be stored into nsDragService as
* nsITransferable. If this type has MIMETYPE_NATIVE, this is already stored into nsDragService.
* So do nothing.
*
* @param event A DragEvent
* @return DropData that is from ClipData. If null, no data that we can convert to Gecko's type.
*/
public static DropData createDropData(final DragEvent event) {
if (event.getAction() != DragEvent.ACTION_DROP) {
return null;
}
final ClipData clip = event.getClipData();
if (clip == null || clip.getItemCount() == 0) {
return null;
}
final ClipDescription description = event.getClipDescription();
if (description.hasMimeType(MIMETYPE_NATIVE)) {
if (DEBUG) {
Log.d(LOGTAG, "Drop data is native nsITransferable. Do nothing");
}
return new DropData();
}
if (description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
final CharSequence data = clip.getItemAt(0).getHtmlText();
if (data == null) {
return null;
}
if (DEBUG) {
Log.d(LOGTAG, "Drop data is text/html");
}
return new DropData(ClipDescription.MIMETYPE_TEXT_HTML, data.toString());
}
final CharSequence text = clip.getItemAt(0).coerceToText(GeckoAppShell.getApplicationContext());
if (!TextUtils.isEmpty(text)) {
if (DEBUG) {
Log.d(LOGTAG, "Drop data is text/plain");
}
return new DropData(ClipDescription.MIMETYPE_TEXT_PLAIN, text.toString());
}
return null;
}
private static void setDragClipData(final ClipData clipData) {
sDragClipData = clipData;
}
private static @Nullable ClipData getDragClipData() {
return sDragClipData;
}
/**
* Set drag item before calling View.startDragAndDrop. This is set from nsITransferable, so it
* marks as native data.
*/
@WrapForJNI
private static void setDragData(final CharSequence text, final String htmlText) {
if (TextUtils.isEmpty(text)) {
final ClipDescription description =
new ClipDescription("drag item", new String[] {MIMETYPE_NATIVE});
final ClipData.Item item = new ClipData.Item("");
final ClipData clipData = new ClipData(description, item);
setDragClipData(clipData);
return;
}
if (TextUtils.isEmpty(htmlText)) {
final ClipDescription description =
new ClipDescription(
"drag item", new String[] {MIMETYPE_NATIVE, ClipDescription.MIMETYPE_TEXT_PLAIN});
final ClipData.Item item = new ClipData.Item(text);
final ClipData clipData = new ClipData(description, item);
setDragClipData(clipData);
return;
}
final ClipDescription description =
new ClipDescription(
"drag item",
new String[] {
MIMETYPE_NATIVE,
ClipDescription.MIMETYPE_TEXT_HTML,
ClipDescription.MIMETYPE_TEXT_PLAIN
});
final ClipData.Item item = new ClipData.Item(text, htmlText);
final ClipData clipData = new ClipData(description, item);
setDragClipData(clipData);
return;
}
@WrapForJNI
private static void endDragSession() {
mEndingSession = true;
}
}

Просмотреть файл

@ -71,6 +71,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoDragAndDrop;
import org.mozilla.gecko.GeckoThread;
import org.mozilla.gecko.IGeckoEditableParent;
import org.mozilla.gecko.MagnifiableSurfaceView;
@ -414,6 +415,16 @@ public class GeckoSession {
GeckoSession.this.setPointerIcon(defaultCursor, customCursor, x, y);
}
@WrapForJNI(calledFrom = "ui")
private void startDragAndDrop(final Bitmap bitmap) {
GeckoSession.this.startDragAndDrop(bitmap);
}
@WrapForJNI(calledFrom = "ui")
private void updateDragImage(final Bitmap bitmap) {
GeckoSession.this.updateDragImage(bitmap);
}
@Override
protected void finalize() throws Throwable {
disposeNative();
@ -7889,6 +7900,34 @@ public class GeckoSession {
}
}
/* package */ void startDragAndDrop(final Bitmap bitmap) {
ThreadUtils.assertOnUiThread();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return;
}
final View view = getTextInput().getView();
if (view == null) {
return;
}
GeckoDragAndDrop.startDragAndDrop(view, bitmap);
}
/* package */ void updateDragImage(final Bitmap bitmap) {
ThreadUtils.assertOnUiThread();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return;
}
final View view = getTextInput().getView();
if (view == null) {
return;
}
GeckoDragAndDrop.updateDragImage(view, bitmap);
}
/** GeckoSession applications implement this interface to handle media events. */
public interface MediaDelegate {

Просмотреть файл

@ -32,6 +32,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.DisplayCutout;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
@ -1233,4 +1234,13 @@ public class GeckoView extends FrameLayout implements GeckoDisplay.NewSurfacePro
}
});
}
/** Handle drag and drop event */
@Override
public boolean onDragEvent(final DragEvent event) {
if (mSession == null) {
return false;
}
return mSession.getPanZoomController().onDragEvent(event);
}
}

Просмотреть файл

@ -9,9 +9,11 @@ import android.app.UiModeManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.util.Pair;
import android.view.DragEvent;
import android.view.InputDevice;
import android.view.MotionEvent;
import androidx.annotation.AnyThread;
@ -22,6 +24,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoDragAndDrop;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.mozglue.JNIObject;
import org.mozilla.gecko.util.GeckoBundle;
@ -298,6 +301,10 @@ public class PanZoomController {
private native @InputResult int handleMouseEvent(
int action, long time, int metaState, float x, float y, int buttons);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
private native void handleDragEvent(
int action, long time, float x, float y, GeckoDragAndDrop.DropData data);
@WrapForJNI(stubName = "SetIsLongpressEnabled") // Called from test thread.
private native void nativeSetIsLongpressEnabled(boolean isLongpressEnabled);
@ -597,6 +604,32 @@ public class PanZoomController {
}
}
/**
* Process a drag event.
*
* @param event DragEvent to process.
* @return true if this event is accepted.
*/
public boolean onDragEvent(@NonNull final DragEvent event) {
ThreadUtils.assertOnUiThread();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return false;
}
if (!GeckoDragAndDrop.onDragEvent(event)) {
return false;
}
mNative.handleDragEvent(
event.getAction(),
SystemClock.uptimeMillis(),
GeckoDragAndDrop.getLocationX(),
GeckoDragAndDrop.getLocationY(),
GeckoDragAndDrop.createDropData(event));
return true;
}
private void enableEventQueue() {
if (mQueuedEvents != null) {
throw new IllegalStateException("Already have an event queue");

Просмотреть файл

@ -18,10 +18,13 @@ exclude: true
- Added [`GeckoRuntimeSettings#setTrustedRecursiveResolverMode`][124.1] to enable DNS-over-HTTPS using different resolver modes ([bug 1591533]({{bugzilla}}1591533)).
- Added [`GeckoRuntimeSettings#setTrustedRecursiveResolverUri`][124.2] to specify the DNS-over-HTTPS server to be used if DoH is enabled ([bug 1591533]({{bugzilla}}1591533)).
- Added [`GeckoRuntimeSettings#setLargeKeepaliveFactor`][124.3] to increase the keepalive timeout used for a connection ([bug 1591533]({{bugzilla}}1591533)).
- Added [`PanZoomController.onDragEvent`][124.4] to support drag and drop.
([bug 1586471]({{bugzilla}}1586471))
[124.1]: {{javadoc_uri}}/GeckoRuntimeSettings.html#setTrustedRecursiveResolverMode-int-
[124.2]: {{javadoc_uri}}/GeckoRuntimeSettings.html#setTrustedRecursiveResolverUri-java.lang.String-
[124.3]: {{javadoc_uri}}/GeckoRuntimeSettings.html#setLargeKeepaliveFactor-int-
[124.4]: {{javadoc_uri}}/PanZoomController.html#onDragEvent(android.view.DragEvent)
## v123
- For Translations, added [`checkPairDownloadSize`][123.1] and [`TranslationsException.ERROR_MODEL_LANGUAGE_REQUIRED`][123.2] as an error state.
@ -1514,4 +1517,4 @@ to allow adding gecko profiler markers.
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String)
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: 757616f5eabb19164140a9f303277496fd1a4993
[api-version]: 6ba1de66c7ab46b0ac52910c9fb76f94c2bcc5b9

Просмотреть файл

@ -0,0 +1,48 @@
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
* vim: set sw=2 ts=4 expandtab:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AndroidWidgetUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Swizzle.h"
using namespace mozilla::gfx;
namespace mozilla::widget {
// static
already_AddRefed<DataSourceSurface>
AndroidWidgetUtils::GetDataSourceSurfaceForAndroidBitmap(
gfx::SourceSurface* aSurface, const LayoutDeviceIntRect* aRect,
uint32_t aStride) {
RefPtr<DataSourceSurface> srcDataSurface = aSurface->GetDataSurface();
if (NS_WARN_IF(!srcDataSurface)) {
return nullptr;
}
DataSourceSurface::ScopedMap sourceMap(srcDataSurface,
DataSourceSurface::READ);
RefPtr<DataSourceSurface> destDataSurface =
gfx::Factory::CreateDataSourceSurfaceWithStride(
aRect ? IntSize(aRect->width, aRect->height)
: srcDataSurface->GetSize(),
SurfaceFormat::R8G8B8A8, aStride ? aStride : sourceMap.GetStride());
if (NS_WARN_IF(!destDataSurface)) {
return nullptr;
}
DataSourceSurface::ScopedMap destMap(destDataSurface,
DataSourceSurface::READ_WRITE);
SwizzleData(sourceMap.GetData(), sourceMap.GetStride(), aSurface->GetFormat(),
destMap.GetData(), destMap.GetStride(), SurfaceFormat::R8G8B8A8,
destDataSurface->GetSize());
return destDataSurface.forget();
}
} // namespace mozilla::widget

Просмотреть файл

@ -0,0 +1,37 @@
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
* vim: set sw=2 ts=4 expandtab:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_widget_AndroidWidgetUtils_h__
#define mozilla_widget_AndroidWidgetUtils_h__
#include "Units.h"
namespace mozilla {
namespace gfx {
class SourceSurface;
class DataSourceSurface;
} // namespace gfx
namespace widget {
class AndroidWidgetUtils final {
public:
typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
/**
* Return Android's bitmap object compatible data surface.
*/
static already_AddRefed<gfx::DataSourceSurface>
GetDataSourceSurfaceForAndroidBitmap(
gfx::SourceSurface* aSurface, const LayoutDeviceIntRect* aRect = nullptr,
uint32_t aStride = 0);
};
} // namespace widget
} // namespace mozilla
#endif

Просмотреть файл

@ -11,13 +11,16 @@
#include "WebExecutorSupport.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsICancelable.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIInputStream.h"
#include "nsIDNSService.h"
#include "nsIDNSListener.h"
#include "nsIDNSRecord.h"
#include "nsINSSErrorsService.h"
#include "nsContentUtils.h"
#include "nsNetUtil.h" // for NS_NewURI, NS_NewChannel, NS_NewStreamLoader
#include "nsIPrivateBrowsingChannel.h"
#include "nsIUploadChannel2.h"
@ -178,7 +181,7 @@ class LoaderListener final : public GeckoViewStreamListener {
}
void CompleteWithError(nsresult aStatus, nsIChannel* aChannel) override {
::CompleteWithError(mResult, aStatus, aChannel);
mozilla::widget::CompleteWithError(mResult, aStatus, aChannel);
}
virtual ~LoaderListener() {}

Просмотреть файл

@ -0,0 +1,3 @@
# We only use constants from DragEvent
[android.view.DragEvent = skip:true]
<field> = skip:false

Просмотреть файл

@ -12,6 +12,7 @@ with Files("**"):
generated = [
"AccessibilityEvent",
"AndroidBuild",
"AndroidDragEvent",
"AndroidGraphics",
"AndroidInputType",
"AndroidProcess",

Просмотреть файл

@ -97,4 +97,13 @@ Classes = [
'type': 'mozilla::widget::AndroidAlerts',
'headers': ['/widget/android/AndroidAlerts.h'],
},
{
'cid': '{b1abaf0e-52b2-4e65-aee1-299ea9a74230}',
'contract_ids': ['@mozilla.org/widget/parent/dragservice;1'],
'singleton': True,
'type': 'nsDragService',
'headers': ['/widget/android/nsDragService.h'],
'constructor': 'nsDragService::GetInstance',
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
},
]

Просмотреть файл

@ -41,6 +41,7 @@ classes_with_WrapForJNI = [
"GeckoAudioInfo",
"GeckoBatteryManager",
"GeckoBundle",
"GeckoDragAndDrop",
"GeckoEditableChild",
"GeckoHLSDemuxerWrapper",
"GeckoHLSResourceWrapper",
@ -108,6 +109,7 @@ EXPORTS.mozilla.widget += [
"AndroidUiThread.h",
"AndroidView.h",
"AndroidVsync.h",
"AndroidWidgetUtils.h",
"CompositorWidgetChild.h",
"CompositorWidgetParent.h",
"EventDispatcher.h",
@ -134,6 +136,7 @@ UNIFIED_SOURCES += [
"AndroidContentController.cpp",
"AndroidUiThread.cpp",
"AndroidVsync.cpp",
"AndroidWidgetUtils.cpp",
"CompositorWidgetChild.cpp",
"CompositorWidgetParent.cpp",
"EventDispatcher.cpp",
@ -145,6 +148,7 @@ UNIFIED_SOURCES += [
"nsAppShell.cpp",
"nsClipboard.cpp",
"nsDeviceContextAndroid.cpp",
"nsDragService.cpp",
"nsLookAndFeel.cpp",
"nsPrintSettingsServiceAndroid.cpp",
"nsUserIdleServiceAndroid.cpp",

Просмотреть файл

@ -10,6 +10,7 @@
#include "base/task.h"
#include "mozilla/Hal.h"
#include "gfxConfig.h"
#include "nsDragService.h"
#include "nsExceptionHandler.h"
#include "nsIScreen.h"
#include "nsWindow.h"
@ -36,6 +37,7 @@
#include "mozilla/intl/OSPreferences.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/java/GeckoAppShellNatives.h"
#include "mozilla/java/GeckoDragAndDropNatives.h"
#include "mozilla/java/GeckoResultWrappers.h"
#include "mozilla/java/GeckoThreadNatives.h"
#include "mozilla/java/XPCOMEventTargetNatives.h"

Просмотреть файл

@ -36,22 +36,15 @@ nsClipboard::~nsClipboard() {
java::GeckoAppShell::GetApplicationContext());
}
NS_IMETHODIMP
nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
int32_t aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
// static
nsresult nsClipboard::GetTextFromTransferable(nsITransferable* aTransferable,
nsString& aText,
nsString& aHTML) {
nsTArray<nsCString> flavors;
aTransferable->FlavorsTransferableCanImport(flavors);
nsAutoString html;
nsAutoString text;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
return rv;
}
for (auto& flavorStr : flavors) {
if (flavorStr.EqualsLiteral(kTextMime)) {
@ -63,7 +56,7 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
}
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(item);
if (supportsString) {
supportsString->GetData(text);
supportsString->GetData(aText);
}
} else if (flavorStr.EqualsLiteral(kHTMLMime)) {
nsCOMPtr<nsISupports> item;
@ -74,10 +67,30 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
}
nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(item);
if (supportsString) {
supportsString->GetData(html);
supportsString->GetData(aHTML);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
int32_t aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsString text;
nsString html;
nsresult rv = GetTextFromTransferable(aTransferable, text, html);
if (NS_FAILED(rv)) {
return rv;
}
if (!html.IsEmpty() &&
java::Clipboard::SetHTML(java::GeckoAppShell::GetApplicationContext(),

Просмотреть файл

@ -17,6 +17,9 @@ class nsClipboard final : public nsBaseClipboard {
NS_DECL_ISUPPORTS_INHERITED
static nsresult GetTextFromTransferable(nsITransferable* aTransferable,
nsString& aText, nsString& aHTML);
protected:
// Implement the native clipboard behavior.
NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,

Просмотреть файл

@ -0,0 +1,268 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDragService.h"
#include "AndroidGraphics.h"
#include "AndroidWidgetUtils.h"
#include "mozilla/dom/Document.h"
#include "mozilla/java/GeckoDragAndDropWrappers.h"
#include "mozilla/PresShell.h"
#include "mozilla/ScopeExit.h"
#include "nsArrayUtils.h"
#include "nsClipboard.h"
#include "nsComponentManagerUtils.h"
#include "nsIArray.h"
#include "nsITransferable.h"
#include "nsPrimitiveHelpers.h"
#include "nsViewManager.h"
#include "nsWindow.h"
NS_IMPL_ISUPPORTS_INHERITED0(nsDragService, nsBaseDragService)
using namespace mozilla;
using namespace mozilla::widget;
StaticRefPtr<nsDragService> sDragServiceInstance;
/* static */
already_AddRefed<nsDragService> nsDragService::GetInstance() {
if (!sDragServiceInstance) {
sDragServiceInstance = new nsDragService();
ClearOnShutdown(&sDragServiceInstance);
}
RefPtr<nsDragService> service = sDragServiceInstance.get();
return service.forget();
}
static nsWindow* GetWindow(dom::Document* aDocument) {
if (!aDocument) {
return nullptr;
}
PresShell* presShell = aDocument->GetPresShell();
if (!presShell) {
return nullptr;
}
RefPtr<nsViewManager> vm = presShell->GetViewManager();
if (!vm) {
return nullptr;
}
nsCOMPtr<nsIWidget> widget = vm->GetRootWidget();
if (!widget) {
return nullptr;
}
RefPtr<nsWindow> window = nsWindow::From(widget);
return window.get();
}
nsresult nsDragService::InvokeDragSessionImpl(
nsIArray* aTransferableArray, const Maybe<CSSIntRegion>& aRegion,
uint32_t aActionType) {
if (jni::GetAPIVersion() < 24) {
return NS_ERROR_NOT_AVAILABLE;
}
uint32_t count = 0;
aTransferableArray->GetLength(&count);
if (count != 1) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsITransferable> transferable =
do_QueryElementAt(aTransferableArray, 0);
nsAutoString html;
nsAutoString text;
nsresult rv = nsClipboard::GetTextFromTransferable(transferable, text, html);
if (NS_FAILED(rv)) {
return rv;
}
java::GeckoDragAndDrop::SetDragData(text, html);
if (nsWindow* window = GetWindow(mSourceDocument)) {
mTransferable = transferable;
nsBaseDragService::StartDragSession();
nsBaseDragService::OpenDragPopup();
auto bitmap = CreateDragImage(mSourceNode, aRegion);
window->StartDragAndDrop(bitmap);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItem) {
if (!aTransferable) {
return NS_ERROR_INVALID_ARG;
}
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
for (const auto& flavor : flavors) {
nsCOMPtr<nsISupports> data;
rv = mTransferable->GetTransferData(flavor.get(), getter_AddRefs(data));
if (NS_FAILED(rv)) {
continue;
}
rv = aTransferable->SetTransferData(flavor.get(), data);
if (NS_SUCCEEDED(rv)) {
return rv;
}
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsDragService::GetNumDropItems(uint32_t* aNumItems) {
if (mTransferable) {
*aNumItems = 1;
return NS_OK;
}
*aNumItems = 0;
return NS_OK;
}
NS_IMETHODIMP
nsDragService::IsDataFlavorSupported(const char* aDataFlavor, bool* _retval) {
*_retval = false;
nsDependentCString dataFlavor(aDataFlavor);
auto logging = MakeScopeExit([&] {
MOZ_DRAGSERVICE_LOG("IsDataFlavorSupported: %s is%s found", aDataFlavor,
*_retval ? "" : " not");
});
nsTArray<nsCString> flavors;
nsresult rv = mTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
return NS_OK;
}
for (const auto& flavor : flavors) {
if (dataFlavor.Equals(flavor)) {
*_retval = true;
return NS_OK;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) {
java::GeckoDragAndDrop::EndDragSession();
nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
mTransferable = nullptr;
return rv;
}
NS_IMETHODIMP
nsDragService::UpdateDragImage(nsINode* aImage, int32_t aImageX,
int32_t aImageY) {
nsBaseDragService::UpdateDragImage(aImage, aImageX, aImageY);
auto bitmap = CreateDragImage(mSourceNode, Nothing());
if (nsWindow* window = GetWindow(mSourceDocument)) {
window->UpdateDragImage(bitmap);
}
return NS_OK;
}
java::sdk::Bitmap::LocalRef nsDragService::CreateDragImage(
nsINode* aNode, const Maybe<CSSIntRegion>& aRegion) {
LayoutDeviceIntRect dragRect;
RefPtr<SourceSurface> surface;
nsPresContext* pc;
DrawDrag(aNode, aRegion, mScreenPosition, &dragRect, &surface, &pc);
if (!surface) {
return nullptr;
}
RefPtr<DataSourceSurface> destDataSurface =
AndroidWidgetUtils::GetDataSourceSurfaceForAndroidBitmap(
surface, &dragRect, dragRect.width * 4);
if (!destDataSurface) {
return nullptr;
}
DataSourceSurface::ScopedMap destMap(destDataSurface,
DataSourceSurface::READ);
java::sdk::Bitmap::LocalRef bitmap;
auto pixels = mozilla::jni::ByteBuffer::New(
reinterpret_cast<int8_t*>(destMap.GetData()),
destMap.GetStride() * destDataSurface->GetSize().height);
bitmap = java::sdk::Bitmap::CreateBitmap(
dragRect.width, dragRect.height, java::sdk::Bitmap::Config::ARGB_8888());
bitmap->CopyPixelsFromBuffer(pixels);
return bitmap;
}
void nsDragService::SetData(nsITransferable* aTransferable) {
mTransferable = aTransferable;
}
// static
void nsDragService::SetDropData(
mozilla::java::GeckoDragAndDrop::DropData::Param aDropData) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<nsDragService> dragService = nsDragService::GetInstance();
if (!dragService) {
return;
}
if (!aDropData) {
dragService->SetData(nullptr);
return;
}
nsCString mime(aDropData->MimeType()->ToCString());
if (mime.EqualsLiteral("application/x-moz-draganddrop")) {
// The drop data isn't changed.
return;
}
if (!mime.EqualsLiteral("text/plain") && !mime.EqualsLiteral("text/html")) {
// Not supported data.
dragService->SetData(nullptr);
return;
}
nsString buffer(aDropData->Text()->ToString());
if (buffer.IsEmpty()) {
dragService->SetData(nullptr);
return;
}
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
mime, buffer.get(), buffer.Length() * 2, getter_AddRefs(wrapper));
if (!wrapper) {
dragService->SetData(nullptr);
return;
}
nsCOMPtr<nsITransferable> transferable =
do_CreateInstance("@mozilla.org/widget/transferable;1");
transferable->Init(nullptr);
transferable->SetTransferData(mime.get(), wrapper);
dragService->SetData(transferable);
}

Просмотреть файл

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDragService_h__
#define nsDragService_h__
#include "nsBaseDragService.h"
#include "AndroidGraphics.h"
#include "mozilla/java/GeckoDragAndDropNatives.h"
class nsITransferable;
class nsDragService final : public nsBaseDragService {
public:
nsDragService() = default;
NS_DECL_ISUPPORTS_INHERITED
static already_AddRefed<nsDragService> GetInstance();
// nsIDragSession
NS_IMETHOD GetData(nsITransferable* aTransferable, uint32_t anItem) override;
NS_IMETHOD GetNumDropItems(uint32_t* aNumItems) override;
NS_IMETHOD IsDataFlavorSupported(const char* aDataFlavor,
bool* _retval) override;
MOZ_CAN_RUN_SCRIPT NS_IMETHOD EndDragSession(bool aDoneDrag,
uint32_t aKeyModifiers) override;
NS_IMETHOD
UpdateDragImage(nsINode* aImage, int32_t aImageX, int32_t aImageY) override;
void SetData(nsITransferable* aTransferable);
static void SetDropData(
mozilla::java::GeckoDragAndDrop::DropData::Param aDropData);
protected:
virtual ~nsDragService() = default;
// nsBaseDragService
MOZ_CAN_RUN_SCRIPT nsresult
InvokeDragSessionImpl(nsIArray* anArrayTransferables,
const mozilla::Maybe<mozilla::CSSIntRegion>& aRegion,
uint32_t aActionType) override;
private:
mozilla::java::sdk::Bitmap::LocalRef CreateDragImage(
nsINode* aNode, const mozilla::Maybe<mozilla::CSSIntRegion>& aRegion);
// our source data items
nsCOMPtr<nsITransferable> mTransferable;
};
#endif // nsDragService_h__

Просмотреть файл

@ -14,13 +14,14 @@
#include <type_traits>
#include <unistd.h>
#include "AndroidGraphics.h"
#include "AndroidBridge.h"
#include "AndroidBridgeUtilities.h"
#include "AndroidCompositorWidget.h"
#include "AndroidContentController.h"
#include "AndroidDragEvent.h"
#include "AndroidUiThread.h"
#include "AndroidView.h"
#include "AndroidWidgetUtils.h"
#include "gfxContext.h"
#include "GeckoEditableSupport.h"
#include "GeckoViewOutputStream.h"
@ -824,6 +825,19 @@ class NPZCSupport final
mListeningToVsync = aNeedVsync;
}
void HandleDragEvent(int32_t aAction, int64_t aTime, float aX, float aY,
jni::Object::Param aDropData) {
// APZ handles some drag event type on APZ thread, but it cannot handle all
// types.
MOZ_ASSERT(NS_IsMainThread());
if (auto window = mWindow.Access()) {
if (nsWindow* gkWindow = window->GetNsWindow()) {
gkWindow->OnDragEvent(aAction, aTime, aX, aY, aDropData);
}
}
}
void ConsumeMotionEventsFromResampler() {
auto outgoing = mTouchResampler.ConsumeOutgoingEvents();
while (!outgoing.empty()) {
@ -2582,6 +2596,139 @@ void GeckoViewSupport::OnUpdateSessionStore(
window->OnUpdateSessionStore(aBundle);
}
static EventMessage convertDragEventActionToGeckoEvent(int32_t aAction) {
switch (aAction) {
case java::sdk::DragEvent::ACTION_DRAG_ENTERED:
return eDragEnter;
case java::sdk::DragEvent::ACTION_DRAG_EXITED:
return eDragExit;
case java::sdk::DragEvent::ACTION_DRAG_LOCATION:
return eDragOver;
case java::sdk::DragEvent::ACTION_DROP:
return eDrop;
}
return eVoidEvent;
}
void nsWindow::OnDragEvent(int32_t aAction, int64_t aTime, float aX, float aY,
jni::Object::Param aDropData) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<nsDragService> dragService = nsDragService::GetInstance();
if (!dragService) {
return;
}
LayoutDeviceIntPoint point =
LayoutDeviceIntPoint(int32_t(floorf(aX)), int32_t(floorf(aY)));
if (aAction == java::sdk::DragEvent::ACTION_DRAG_STARTED) {
dragService->SetDragEndPoint(point);
return;
}
if (aAction == java::sdk::DragEvent::ACTION_DRAG_ENDED) {
dragService->EndDragSession(false, 0);
return;
}
EventMessage message = convertDragEventActionToGeckoEvent(aAction);
if (message == eDragEnter) {
dragService->StartDragSession();
}
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
if (dragSession) {
switch (message) {
case eDragOver:
dragService->SetDragEndPoint(point);
dragService->FireDragEventAtSource(eDrag, 0);
break;
case eDrop: {
bool canDrop = false;
dragSession->GetCanDrop(&canDrop);
if (!canDrop) {
nsCOMPtr<nsINode> sourceNode;
dragSession->GetSourceNode(getter_AddRefs(sourceNode));
if (!sourceNode) {
dragService->EndDragSession(false, 0);
}
return;
}
auto dropData =
mozilla::java::GeckoDragAndDrop::DropData::Ref::From(aDropData);
nsDragService::SetDropData(dropData);
dragService->SetDragEndPoint(point);
break;
}
default:
break;
}
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
}
WidgetDragEvent geckoEvent(true, message, this);
geckoEvent.mRefPoint = point;
geckoEvent.mTimeStamp = nsWindow::GetEventTimeStamp(aTime);
geckoEvent.mModifiers = 0; // DragEvent has no modifiers
DispatchInputEvent(&geckoEvent);
if (!dragSession) {
return;
}
switch (message) {
case eDragExit: {
nsCOMPtr<nsINode> sourceNode;
dragSession->GetSourceNode(getter_AddRefs(sourceNode));
if (!sourceNode) {
// We're leaving a window while doing a drag that was
// initiated in a different app. End the drag session,
// since we're done with it for now (until the user
// drags back into mozilla).
dragService->EndDragSession(false, 0);
}
break;
}
case eDrop:
dragService->EndDragSession(true, 0);
break;
default:
break;
}
}
void nsWindow::StartDragAndDrop(java::sdk::Bitmap::LocalRef aBitmap) {
if (mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
mLayerViewSupport.Access()}) {
const auto& compositor = lvs->GetJavaCompositor();
DispatchToUiThread(
"nsWindow::StartDragAndDrop",
[compositor = GeckoSession::Compositor::GlobalRef(compositor),
bitmap = java::sdk::Bitmap::GlobalRef(aBitmap)] {
compositor->StartDragAndDrop(bitmap);
});
}
}
void nsWindow::UpdateDragImage(java::sdk::Bitmap::LocalRef aBitmap) {
if (mozilla::jni::NativeWeakPtr<LayerViewSupport>::Accessor lvs{
mLayerViewSupport.Access()}) {
const auto& compositor = lvs->GetJavaCompositor();
DispatchToUiThread(
"nsWindow::UpdateDragImage",
[compositor = GeckoSession::Compositor::GlobalRef(compositor),
bitmap = java::sdk::Bitmap::GlobalRef(aBitmap)] {
compositor->UpdateDragImage(bitmap);
});
}
}
void nsWindow::OnSizeChanged(const gfx::IntSize& aSize) {
ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, aSize.width,
aSize.height);
@ -3083,29 +3230,7 @@ static already_AddRefed<DataSourceSurface> GetCursorImage(
return nullptr;
}
RefPtr<DataSourceSurface> srcDataSurface = surface->GetDataSurface();
if (NS_WARN_IF(!srcDataSurface)) {
return nullptr;
}
DataSourceSurface::ScopedMap sourceMap(srcDataSurface,
DataSourceSurface::READ);
destDataSurface = gfx::Factory::CreateDataSourceSurfaceWithStride(
srcDataSurface->GetSize(), SurfaceFormat::R8G8B8A8,
sourceMap.GetStride());
if (NS_WARN_IF(!destDataSurface)) {
return nullptr;
}
DataSourceSurface::ScopedMap destMap(destDataSurface,
DataSourceSurface::READ_WRITE);
SwizzleData(sourceMap.GetData(), sourceMap.GetStride(), surface->GetFormat(),
destMap.GetData(), destMap.GetStride(), SurfaceFormat::R8G8B8A8,
destDataSurface->GetSize());
return destDataSurface.forget();
return AndroidWidgetUtils::GetDataSourceSurfaceForAndroidBitmap(surface);
}
static int32_t GetCursorType(nsCursor aCursor) {

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef NSWINDOW_H_
#define NSWINDOW_H_
#include "AndroidGraphics.h"
#include "nsBaseWidget.h"
#include "gfxPoint.h"
#include "nsIUserIdleServiceInternal.h"
@ -127,6 +128,12 @@ class nsWindow final : public nsBaseWidget {
void ShowDynamicToolbar();
MOZ_CAN_RUN_SCRIPT_BOUNDARY void OnDragEvent(
int32_t aAction, int64_t aTime, float aX, float aY,
mozilla::jni::Object::Param aDropData);
void StartDragAndDrop(mozilla::java::sdk::Bitmap::LocalRef aBitmap);
void UpdateDragImage(mozilla::java::sdk::Bitmap::LocalRef aBitmap);
void DetachNatives();
mozilla::Mutex& GetDestroyMutex() { return mDestroyMutex; }