Bug 1264017 - Add synthesized mouse support to Android. r=rbarker

MozReview-Commit-ID: HT4U7WKTd5Z
This commit is contained in:
Kartikaya Gupta
2016-05-16 12:17:17 -04:00
parent 94e6a966bd
commit 3ef033f656
7 changed files with 167 additions and 22 deletions

View File

@@ -7,6 +7,10 @@ function getPlatform() {
if (navigator.platform.indexOf("Mac") == 0) {
return "mac";
}
// Check for Android before Linux
if (navigator.appVersion.indexOf("Android") >= 0) {
return "android"
}
if (navigator.platform.indexOf("Linux") == 0) {
return "linux";
}
@@ -49,7 +53,9 @@ function nativeMouseDownEventMsg() {
case "windows": return 2; // MOUSEEVENTF_LEFTDOWN
case "mac": return 1; // NSLeftMouseDown
case "linux": return 4; // GDK_BUTTON_PRESS
case "android": return 5; // ACTION_POINTER_DOWN
}
throw "Native mouse-down events not supported on platform " + getPlatform();
}
function nativeMouseMoveEventMsg() {
@@ -57,8 +63,9 @@ function nativeMouseMoveEventMsg() {
case "windows": return 1; // MOUSEEVENTF_MOVE
case "mac": return 5; // NSMouseMoved
case "linux": return 3; // GDK_MOTION_NOTIFY
case "android": return 7; // ACTION_HOVER_MOVE
}
throw "Native wheel events not supported on platform " + getPlatform();
throw "Native mouse-move events not supported on platform " + getPlatform();
}
function nativeMouseUpEventMsg() {
@@ -66,7 +73,9 @@ function nativeMouseUpEventMsg() {
case "windows": return 4; // MOUSEEVENTF_LEFTUP
case "mac": return 2; // NSLeftMouseUp
case "linux": return 7; // GDK_BUTTON_RELEASE
case "android": return 6; // ACTION_POINTER_UP
}
throw "Native mouse-up events not supported on platform " + getPlatform();
}
// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,

View File

@@ -45,5 +45,3 @@ skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
[test_scroll_window.html]
skip-if = (toolkit == 'android') # wheel events not supported on mobile
[test_click.html]
# Very similar to test_tap, but we don't yet have mouse event synthesization on android
skip-if = (os == 'android')

View File

@@ -713,7 +713,14 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
}
class PointerInfo {
// We reserve one pointer ID for the mouse, so that tests don't have
// to worry about tracking pointer IDs if they just want to test mouse
// event synthesization. If somebody tries to use this ID for a
// synthesized touch event we'll throw an exception.
public static final int RESERVED_MOUSE_POINTER_ID = 100000;
public int pointerId;
public int source;
public int screenX;
public int screenY;
public double pressure;
@@ -746,34 +753,63 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
return -1;
}
int addPointer(int pointerId) {
int addPointer(int pointerId, int source) {
PointerInfo info = new PointerInfo();
info.pointerId = pointerId;
info.source = source;
pointers.add(info);
return pointers.size() - 1;
}
int[] getPointerIds() {
int[] ids = new int[pointers.size()];
for (int i = 0; i < ids.length; i++) {
ids[i] = pointers.get(i).pointerId;
int getPointerCount(int source) {
int count = 0;
for (int i = 0; i < pointers.size(); i++) {
if (pointers.get(i).source == source) {
count++;
}
return ids;
}
return count;
}
MotionEvent.PointerCoords[] getPointerCoords() {
MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointers.size()];
for (int i = 0; i < coords.length; i++) {
coords[i] = pointers.get(i).getCoords();
MotionEvent.PointerProperties[] getPointerProperties(int source) {
MotionEvent.PointerProperties[] props = new MotionEvent.PointerProperties[getPointerCount(source)];
int index = 0;
for (int i = 0; i < pointers.size(); i++) {
if (pointers.get(i).source == source) {
MotionEvent.PointerProperties p = new MotionEvent.PointerProperties();
p.id = pointers.get(i).pointerId;
switch (source) {
case InputDevice.SOURCE_TOUCHSCREEN:
p.toolType = MotionEvent.TOOL_TYPE_FINGER;
break;
case InputDevice.SOURCE_MOUSE:
p.toolType = MotionEvent.TOOL_TYPE_MOUSE;
break;
}
props[index++] = p;
}
}
return props;
}
MotionEvent.PointerCoords[] getPointerCoords(int source) {
MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[getPointerCount(source)];
int index = 0;
for (int i = 0; i < pointers.size(); i++) {
if (pointers.get(i).source == source) {
coords[index++] = pointers.get(i).getCoords();
}
}
return coords;
}
}
@WrapForJNI
public void synthesizeNativeTouchPoint(int pointerId, int eventType, int screenX,
int screenY, double pressure, int orientation)
private void synthesizeNativePointer(int source, int pointerId,
int eventType, int screenX, int screenY, double pressure,
int orientation)
{
Log.d(LOGTAG, "Synthesizing pointer from " + source + " id " + pointerId + " at " + screenX + ", " + screenY);
if (mPointerState == null) {
mPointerState = new SynthesizedEventState();
}
@@ -802,7 +838,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
case MotionEvent.ACTION_POINTER_DOWN:
if (pointerIndex < 0) {
// Adding a new pointer
pointerIndex = mPointerState.addPointer(pointerId);
pointerIndex = mPointerState.addPointer(pointerId, source);
if (pointerIndex == 0) {
// first pointer
eventType = MotionEvent.ACTION_DOWN;
@@ -813,6 +849,18 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
eventType = MotionEvent.ACTION_MOVE;
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
if (pointerIndex < 0) {
// Mouse-move a pointer without it going "down". However
// in order to send the right MotionEvent without a lot of
// duplicated code, we add the pointer to mPointerState,
// and then remove it at the bottom of this function.
pointerIndex = mPointerState.addPointer(pointerId, source);
} else {
// We're moving an existing mouse pointer that went down.
eventType = MotionEvent.ACTION_MOVE;
}
break;
}
// Update the pointer with the new info
@@ -826,10 +874,23 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
int action = (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
action &= MotionEvent.ACTION_POINTER_INDEX_MASK;
action |= (eventType & MotionEvent.ACTION_MASK);
final MotionEvent event = MotionEvent.obtain(mPointerState.downTime,
SystemClock.uptimeMillis(), action, mPointerState.pointers.size(),
mPointerState.getPointerIds(), mPointerState.getPointerCoords(),
0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
boolean isButtonDown = (source == InputDevice.SOURCE_MOUSE) &&
(eventType == MotionEvent.ACTION_DOWN || eventType == MotionEvent.ACTION_MOVE);
final MotionEvent event = MotionEvent.obtain(
/*downTime*/ mPointerState.downTime,
/*eventTime*/ SystemClock.uptimeMillis(),
/*action*/ action,
/*pointerCount*/ mPointerState.getPointerCount(source),
/*pointerProperties*/ mPointerState.getPointerProperties(source),
/*pointerCoords*/ mPointerState.getPointerCoords(source),
/*metaState*/ 0,
/*buttonState*/ (isButtonDown ? MotionEvent.BUTTON_PRIMARY : 0),
/*xPrecision*/ 0,
/*yPrecision*/ 0,
/*deviceId*/ 0,
/*edgeFlags*/ 0,
/*source*/ source,
/*flags*/ 0);
mView.post(new Runnable() {
@Override
public void run() {
@@ -841,12 +902,30 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
// Forget about removed pointers
if (eventType == MotionEvent.ACTION_POINTER_UP ||
eventType == MotionEvent.ACTION_UP ||
eventType == MotionEvent.ACTION_CANCEL)
eventType == MotionEvent.ACTION_CANCEL ||
eventType == MotionEvent.ACTION_HOVER_MOVE)
{
mPointerState.pointers.remove(pointerIndex);
}
}
@WrapForJNI
public void synthesizeNativeTouchPoint(int pointerId, int eventType, int screenX,
int screenY, double pressure, int orientation)
{
if (pointerId == PointerInfo.RESERVED_MOUSE_POINTER_ID) {
throw new IllegalArgumentException("Use a different pointer ID in your test, this one is reserved for mouse");
}
synthesizeNativePointer(InputDevice.SOURCE_TOUCHSCREEN, pointerId,
eventType, screenX, screenY, pressure, orientation);
}
@WrapForJNI
public void synthesizeNativeMouseEvent(int eventType, int screenX, int screenY) {
synthesizeNativePointer(InputDevice.SOURCE_MOUSE, PointerInfo.RESERVED_MOUSE_POINTER_ID,
eventType, screenX, screenY, 0, 0);
}
@WrapForJNI(allowMultithread = true)
public LayerRenderer.Frame createFrame() {
// Create the shaders and textures if necessary.

View File

@@ -1444,6 +1444,14 @@ auto GeckoLayerClient::SyncViewportInfo(int32_t a0, int32_t a1, int32_t a2, int3
return mozilla::jni::Method<SyncViewportInfo_t>::Call(GeckoLayerClient::mCtx, nullptr, a0, a1, a2, a3, a4, a5, a6);
}
constexpr char GeckoLayerClient::SynthesizeNativeMouseEvent_t::name[];
constexpr char GeckoLayerClient::SynthesizeNativeMouseEvent_t::signature[];
auto GeckoLayerClient::SynthesizeNativeMouseEvent(int32_t a0, int32_t a1, int32_t a2) const -> void
{
return mozilla::jni::Method<SynthesizeNativeMouseEvent_t>::Call(GeckoLayerClient::mCtx, nullptr, a0, a1, a2);
}
constexpr char GeckoLayerClient::SynthesizeNativeTouchPoint_t::name[];
constexpr char GeckoLayerClient::SynthesizeNativeTouchPoint_t::signature[];

View File

@@ -3438,6 +3438,24 @@ public:
auto SyncViewportInfo(int32_t, int32_t, int32_t, int32_t, float, bool, int32_t) const -> mozilla::jni::Object::LocalRef;
struct SynthesizeNativeMouseEvent_t {
typedef GeckoLayerClient Owner;
typedef void ReturnType;
typedef void SetterType;
typedef mozilla::jni::Args<
int32_t,
int32_t,
int32_t> Args;
static constexpr char name[] = "synthesizeNativeMouseEvent";
static constexpr char signature[] =
"(III)V";
static const bool isStatic = false;
static const mozilla::jni::ExceptionMode exceptionMode =
mozilla::jni::ExceptionMode::ABORT;
};
auto SynthesizeNativeMouseEvent(int32_t, int32_t, int32_t) const -> void;
struct SynthesizeNativeTouchPoint_t {
typedef GeckoLayerClient Owner;
typedef void ReturnType;

View File

@@ -3405,6 +3405,33 @@ nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
return NS_OK;
}
nsresult
nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
uint32_t aNativeMessage,
uint32_t aModifierFlags,
nsIObserver* aObserver)
{
mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
MOZ_ASSERT(mGLControllerSupport);
GeckoLayerClient::LocalRef client = mGLControllerSupport->GetLayerClient();
client->SynthesizeNativeMouseEvent(aNativeMessage, aPoint.x, aPoint.y);
return NS_OK;
}
nsresult
nsWindow::SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
nsIObserver* aObserver)
{
mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
MOZ_ASSERT(mGLControllerSupport);
GeckoLayerClient::LocalRef client = mGLControllerSupport->GetLayerClient();
client->SynthesizeNativeMouseEvent(sdk::MotionEvent::ACTION_HOVER_MOVE, aPoint.x, aPoint.y);
return NS_OK;
}
void
nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager,

View File

@@ -200,6 +200,12 @@ public:
double aPointerPressure,
uint32_t aPointerOrientation,
nsIObserver* aObserver) override;
nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
uint32_t aNativeMessage,
uint32_t aModifierFlags,
nsIObserver* aObserver) override;
nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
nsIObserver* aObserver) override;
protected:
void BringToFront();