From ecbc65cf8f2ea3bdd311f954e3927b46fca068ff Mon Sep 17 00:00:00 2001 From: Ben Murdoch Date: Wed, 13 Jan 2010 10:54:56 +0000 Subject: [PATCH] Add support for sending touch events in DRT. As part of this, make it possible for DRT to configure the timeout threshold between sending touch events to WebCore as the Layout Tests only synthesize single events, not a stream. Because of this, they often get dropped by the WebView for coming too quickly. Skip the multi touch test as we don't support multi touch in the Browser. Change-Id: I7b9830f43181fea33206825b49ef2e294269b4dd --- core/java/android/webkit/WebView.java | 13 +- .../android/dumprendertree/CallbackProxy.java | 97 +++++++++- .../android/dumprendertree/EventSender.java | 10 + .../android/dumprendertree/FileFilter.java | 2 + .../dumprendertree/TestShellActivity.java | 7 + .../dumprendertree/WebViewEventSender.java | 173 +++++++++++++++++- 6 files changed, 294 insertions(+), 8 deletions(-) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index f2dab6e03b086..16ecab7566019 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -342,6 +342,7 @@ public class WebView extends AbsoluteLayout * choice. Maybe make this in the buildspec later. */ private static final int TOUCH_SENT_INTERVAL = 50; + private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL; /** * Helper class to get velocity for fling @@ -4343,7 +4344,7 @@ public class WebView extends AbsoluteLayout // pass the touch events from UI thread to WebCore thread if (mForwardTouchEvents && (action != MotionEvent.ACTION_MOVE - || eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) { + || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) { WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); ted.mAction = action; ted.mX = viewToContentX((int) x + mScrollX); @@ -6633,6 +6634,16 @@ public class WebView extends AbsoluteLayout mWebViewCore.drawContentPicture(canvas, 0, false, false); } + /** + * Set the time to wait between passing touches to WebCore. See also the + * TOUCH_SENT_INTERVAL member for further discussion. + * + * @hide This is only used by the DRT test application. + */ + public void setTouchInterval(int interval) { + mCurrentTouchInterval = interval; + } + /** * Update our cache with updatedText. * @param updatedText The new text to put in our cache. diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java index f33b01dd278fa..50451e75ae416 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java @@ -16,6 +16,7 @@ package com.android.dumprendertree; +import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.webkit.MockGeolocation; @@ -24,7 +25,7 @@ import android.webkit.WebStorage; import java.util.HashMap; public class CallbackProxy extends Handler implements EventSender, LayoutTestController { - + private EventSender mEventSender; private LayoutTestController mLayoutTestController; @@ -37,6 +38,15 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon private static final int EVENT_MOUSE_DOWN = 7; private static final int EVENT_MOUSE_MOVE = 8; private static final int EVENT_MOUSE_UP = 9; + private static final int EVENT_TOUCH_START = 10; + private static final int EVENT_TOUCH_MOVE = 11; + private static final int EVENT_TOUCH_END = 12; + private static final int EVENT_TOUCH_CANCEL = 13; + private static final int EVENT_ADD_TOUCH_POINT = 14; + private static final int EVENT_UPDATE_TOUCH_POINT = 15; + private static final int EVENT_RELEASE_TOUCH_POINT = 16; + private static final int EVENT_CLEAR_TOUCH_POINTS = 17; + private static final int EVENT_CANCEL_TOUCH_POINT = 18; private static final int LAYOUT_CLEAR_LIST = 20; private static final int LAYOUT_DISPLAY = 21; @@ -107,6 +117,46 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon mEventSender.mouseUp(); break; + case EVENT_TOUCH_START: + mEventSender.touchStart(); + break; + + case EVENT_TOUCH_MOVE: + mEventSender.touchMove(); + break; + + case EVENT_TOUCH_END: + mEventSender.touchEnd(); + break; + + case EVENT_TOUCH_CANCEL: + mEventSender.touchCancel(); + break; + + case EVENT_ADD_TOUCH_POINT: + mEventSender.addTouchPoint(msg.arg1, msg.arg2); + break; + + case EVENT_UPDATE_TOUCH_POINT: + Bundle args = (Bundle) msg.obj; + int x = args.getInt("x"); + int y = args.getInt("y"); + int id = args.getInt("id"); + mEventSender.updateTouchPoint(id, x, y); + break; + + case EVENT_RELEASE_TOUCH_POINT: + mEventSender.releaseTouchPoint(msg.arg1); + break; + + case EVENT_CLEAR_TOUCH_POINTS: + mEventSender.clearTouchPoints(); + break; + + case EVENT_CANCEL_TOUCH_POINT: + mEventSender.cancelTouchPoint(msg.arg1); + break; + case LAYOUT_CLEAR_LIST: mLayoutTestController.clearBackForwardList(); break; @@ -252,6 +302,51 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon public void mouseUp() { obtainMessage(EVENT_MOUSE_UP).sendToTarget(); } + + public void touchStart() { + obtainMessage(EVENT_TOUCH_START).sendToTarget(); + } + + public void addTouchPoint(int x, int y) { + obtainMessage(EVENT_ADD_TOUCH_POINT, x, y).sendToTarget(); + } + + public void updateTouchPoint(int id, int x, int y) { + Bundle map = new Bundle(); + map.putInt("x", x); + map.putInt("y", y); + map.putInt("id", id); + obtainMessage(EVENT_UPDATE_TOUCH_POINT, map).sendToTarget(); + } + + public void setTouchModifier(String modifier, boolean enabled) { + // TODO(benm): Android doesn't support key modifiers on touch events yet. + } + + public void touchMove() { + obtainMessage(EVENT_TOUCH_MOVE).sendToTarget(); + } + + public void releaseTouchPoint(int id) { + obtainMessage(EVENT_RELEASE_TOUCH_POINT, id, 0).sendToTarget(); + } + + public void touchEnd() { + obtainMessage(EVENT_TOUCH_END).sendToTarget(); + } + + public void touchCancel() { + obtainMessage(EVENT_TOUCH_CANCEL).sendToTarget(); + } + + + public void clearTouchPoints() { + obtainMessage(EVENT_CLEAR_TOUCH_POINTS).sendToTarget(); + } + + public void cancelTouchPoint(int id) { + obtainMessage(EVENT_CANCEL_TOUCH_POINT, id, 0).sendToTarget(); + } // LayoutTestController Methods diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java index 82fd8d8d61de0..23cc8f531bb00 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java @@ -26,4 +26,14 @@ public interface EventSender { public void keyDown (String character); public void enableDOMUIEventLogging(int DOMNode); public void fireKeyboardEventsToElement(int DOMNode); + public void touchStart(); + public void touchMove(); + public void touchEnd(); + public void touchCancel(); + public void addTouchPoint(int x, int y); + public void updateTouchPoint(int id, int x, int y); + public void setTouchModifier(String modifier, boolean enabled); + public void releaseTouchPoint(int id); + public void clearTouchPoints(); + public void cancelTouchPoint(int id); } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index 32219fad63975..452368eb309a0 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -96,6 +96,8 @@ public class FileFilter { // tests expect "LayoutTests" in their output. "storage/domstorage/localstorage/iframe-events.html", "storage/domstorage/sessionstorage/iframe-events.html", + // We do not support multi touch events. + "fast/events/touch/basic-multi-touch-events.html", // below tests (failed or crashes) are filtered out temporarily due to prioritizing "editing/selection/move-left-right.html", }; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java index 5763b85e04b3b..e8a66c19b6089 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java @@ -705,6 +705,7 @@ public class TestShellActivity extends Activity implements LayoutTestController mDumpDatabaseCallbacks = false; mCanOpenWindows = false; mEventSender.resetMouse(); + mEventSender.clearTouchPoints(); mPageFinished = false; mOneHundredPercentComplete = false; mDumpWebKitData = false; @@ -769,6 +770,12 @@ public class TestShellActivity extends Activity implements LayoutTestController webview.setWebChromeClient(mChromeClient); webview.setWebViewClient(mViewClient); + // Setting a touch interval of -1 effectively disables the optimisation in WebView + // that stops repeated touch events flooding WebCore. The Event Sender only sends a + // single event rather than a stream of events (like what would generally happen in + // a real use of touch events in a WebView) and so if the WebView drops the event, + // the test will fail as the test expects one callback for every touch it synthesizes. + webview.setTouchInterval(-1); } private WebView mWebView; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java index eea6346369783..996eaba0b73ee 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java @@ -16,17 +16,25 @@ package com.android.dumprendertree; -import android.webkit.WebView; -import android.view.KeyEvent; +import android.os.Handler; +import android.os.SystemClock; import android.util.*; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.webkit.WebView; +import java.lang.InterruptedException; import java.util.Arrays; +import java.util.Vector; public class WebViewEventSender implements EventSender { + + private static final String LOGTAG = "WebViewEventSender"; - WebViewEventSender(WebView webView) { - mWebView = webView; - } + WebViewEventSender(WebView webView) { + mWebView = webView; + mTouchPoints = new Vector(); + } public void resetMouse() { mouseX = mouseY = 0; @@ -186,9 +194,162 @@ public class WebViewEventSender implements EventSender { } return KeyEvent.KEYCODE_UNKNOWN; } - + + public void touchStart() { + // We only support single touch so examine the first touch point only. + // If multi touch is enabled in the future, we need to re-examine this to send + // all the touch points with the event. + TouchPoint tp = mTouchPoints.get(0); + + if (tp == null) { + return; + } + + tp.setDownTime(SystemClock.uptimeMillis()); + MotionEvent event = MotionEvent.obtain(tp.downTime(), tp.downTime(), + MotionEvent.ACTION_DOWN, tp.getX(), tp.getY(), 0); + mWebView.onTouchEvent(event); + } + + public void touchMove() { + TouchPoint tp = mTouchPoints.get(0); + + if (tp == null) { + return; + } + + if (!tp.hasMoved()) { + return; + } + + MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(), + MotionEvent.ACTION_MOVE, tp.getX(), tp.getY(), 0); + mWebView.onTouchEvent(event); + + tp.setMoved(false); + } + + public void touchEnd() { + TouchPoint tp = mTouchPoints.get(0); + + if (tp == null) { + return; + } + + MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(), + MotionEvent.ACTION_UP, tp.getX(), tp.getY(), 0); + mWebView.onTouchEvent(event); + + if (tp.isReleased()) { + mTouchPoints.remove(0); + } + } + + public void touchCancel() { + TouchPoint tp = mTouchPoints.get(0); + if (tp == null) { + return; + } + + if (tp.cancelled()) { + MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(), + MotionEvent.ACTION_CANCEL, tp.getX(), tp.getY(), 0); + mWebView.onTouchEvent(event); + } + } + + public void cancelTouchPoint(int id) { + TouchPoint tp = mTouchPoints.get(0); + if (tp == null) { + return; + } + + tp.cancel(); + } + + public void addTouchPoint(int x, int y) { + mTouchPoints.add(new TouchPoint(contentsToWindowX(x), contentsToWindowY(y))); + if (mTouchPoints.size() > 1) { + Log.w(LOGTAG, "Adding more than one touch point, but multi touch is not supported!"); + } + } + + public void updateTouchPoint(int id, int x, int y) { + TouchPoint tp = mTouchPoints.get(0); + if (tp == null) { + return; + } + + tp.update(contentsToWindowX(x), contentsToWindowY(y)); + tp.setMoved(true); + } + + public void setTouchModifier(String modifier, boolean enabled) { + // TODO(benm): This needs implementing when Android supports sending key modifiers + // in touch events. + } + + public void releaseTouchPoint(int id) { + TouchPoint tp = mTouchPoints.get(0); + if (tp == null) { + return; + } + + tp.release(); + } + + public void clearTouchPoints() { + mTouchPoints.clear(); + } + + private int contentsToWindowX(int x) { + return (int) (x * mWebView.getScale()) - mWebView.getScrollX(); + } + + private int contentsToWindowY(int y) { + return (int) (y * mWebView.getScale()) - mWebView.getScrollY(); + } + private WebView mWebView = null; private int mouseX; private int mouseY; + private class TouchPoint { + private int mX; + private int mY; + private long mDownTime; + private boolean mReleased; + private boolean mMoved; + private boolean mCancelled; + + public TouchPoint(int x, int y) { + mX = x; + mY = y; + mReleased = false; + mMoved = false; + mCancelled = false; + } + + public void setDownTime(long downTime) { mDownTime = downTime; } + public long downTime() { return mDownTime; } + public void cancel() { mCancelled = true; } + + public boolean cancelled() { return mCancelled; } + + public void release() { mReleased = true; } + public boolean isReleased() { return mReleased; } + + public void setMoved(boolean moved) { mMoved = moved; } + public boolean hasMoved() { return mMoved; } + + public int getX() { return mX; } + public int getY() { return mY; } + + public void update(int x, int y) { + mX = x; + mY = y; + } + }; + + private Vector mTouchPoints; }