Merge "Add support for sending touch events in DRT."
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f2dab6e..16ecab7 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -342,6 +342,7 @@
      * 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 @@
 
         // 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);
@@ -6634,6 +6635,16 @@
     }
 
     /**
+     * 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 f33b01d..50451e7 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 java.util.HashMap;
 
 public class CallbackProxy extends Handler implements EventSender, LayoutTestController {
-    
+
     private EventSender mEventSender;
     private LayoutTestController mLayoutTestController;
 
@@ -37,6 +38,15 @@
     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 @@
             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 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 82fd8d8..23cc8f5 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/EventSender.java
@@ -26,4 +26,14 @@
         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 32219fa..452368e 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -96,6 +96,8 @@
         // 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 5763b85..e8a66c1 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -705,6 +705,7 @@
         mDumpDatabaseCallbacks = false;
         mCanOpenWindows = false;
         mEventSender.resetMouse();
+        mEventSender.clearTouchPoints();
         mPageFinished = false;
         mOneHundredPercentComplete = false;
         mDumpWebKitData = false;
@@ -769,6 +770,12 @@
 
         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 eea6346..996eaba 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 @@
 		}
 		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<TouchPoint> mTouchPoints;
 }