Bug 1264017 - Add synthesized mouse support to Android. r=rbarker
MozReview-Commit-ID: HT4U7WKTd5Z
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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[];
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user