summaryrefslogtreecommitdiff
path: root/core/java/android/webkit/WebViewInputDispatcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/webkit/WebViewInputDispatcher.java')
-rw-r--r--core/java/android/webkit/WebViewInputDispatcher.java1293
1 files changed, 0 insertions, 1293 deletions
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
deleted file mode 100644
index f64547f255db..000000000000
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ /dev/null
@@ -1,1293 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-/**
- * Perform asynchronous dispatch of input events in a {@link WebView}.
- *
- * This dispatcher is shared by the UI thread ({@link WebViewClassic}) and web kit
- * thread ({@link WebViewCore}). The UI thread enqueues events for
- * processing, waits for the web kit thread to handle them, and then performs
- * additional processing depending on the outcome.
- *
- * How it works:
- *
- * 1. The web view thread receives an input event from the input system on the UI
- * thread in its {@link WebViewClassic#onTouchEvent} handler. It sends the input event
- * to the dispatcher, then immediately returns true to the input system to indicate that
- * it will handle the event.
- *
- * 2. The web kit thread is notified that an event has been enqueued. Meanwhile additional
- * events may be enqueued from the UI thread. In some cases, the dispatcher may decide to
- * coalesce motion events into larger batches or to cancel events that have been
- * sitting in the queue for too long.
- *
- * 3. The web kit thread wakes up and handles all input events that are waiting for it.
- * After processing each input event, it informs the dispatcher whether the web application
- * has decided to handle the event itself and to prevent default event handling.
- *
- * 4. If web kit indicates that it wants to prevent default event handling, then web kit
- * consumes the remainder of the gesture and web view receives a cancel event if
- * needed. Otherwise, the web view handles the gesture on the UI thread normally.
- *
- * 5. If the web kit thread takes too long to handle an input event, then it loses the
- * right to handle it. The dispatcher synthesizes a cancellation event for web kit and
- * then tells the web view on the UI thread to handle the event that timed out along
- * with the rest of the gesture.
- *
- * One thing to keep in mind about the dispatcher is that what goes into the dispatcher
- * is not necessarily what the web kit or UI thread will see. As mentioned above, the
- * dispatcher may tweak the input event stream to improve responsiveness. Both web view and
- * web kit are guaranteed to perceive a consistent stream of input events but
- * they might not always see the same events (especially if one decides
- * to prevent the other from handling a particular gesture).
- *
- * This implementation very deliberately does not refer to the {@link WebViewClassic}
- * or {@link WebViewCore} classes, preferring to communicate with them only via
- * interfaces to avoid unintentional coupling to their implementation details.
- *
- * Currently, the input dispatcher only handles pointer events (includes touch,
- * hover and scroll events). In principle, it could be extended to handle trackball
- * and key events if needed.
- *
- * @hide
- */
-final class WebViewInputDispatcher {
- private static final String TAG = "WebViewInputDispatcher";
- private static final boolean DEBUG = false;
- // This enables batching of MotionEvents. It will combine multiple MotionEvents
- // together into a single MotionEvent if more events come in while we are
- // still waiting on the processing of a previous event.
- // If this is set to false, we will instead opt to drop ACTION_MOVE
- // events we cannot keep up with.
- // TODO: If batching proves to be working well, remove this
- private static final boolean ENABLE_EVENT_BATCHING = true;
-
- private final Object mLock = new Object();
-
- // Pool of queued input events. (guarded by mLock)
- private static final int MAX_DISPATCH_EVENT_POOL_SIZE = 10;
- private DispatchEvent mDispatchEventPool;
- private int mDispatchEventPoolSize;
-
- // Posted state, tracks events posted to the dispatcher. (guarded by mLock)
- private final TouchStream mPostTouchStream = new TouchStream();
- private boolean mPostSendTouchEventsToWebKit;
- private boolean mPostDoNotSendTouchEventsToWebKitUntilNextGesture;
- private boolean mPostLongPressScheduled;
- private boolean mPostClickScheduled;
- private boolean mPostShowTapHighlightScheduled;
- private boolean mPostHideTapHighlightScheduled;
- private int mPostLastWebKitXOffset;
- private int mPostLastWebKitYOffset;
- private float mPostLastWebKitScale;
-
- // State for event tracking (click, longpress, double tap, etc..)
- private boolean mIsDoubleTapCandidate;
- private boolean mIsTapCandidate;
- private float mInitialDownX;
- private float mInitialDownY;
- private float mTouchSlopSquared;
- private float mDoubleTapSlopSquared;
-
- // Web kit state, tracks events observed by web kit. (guarded by mLock)
- private final DispatchEventQueue mWebKitDispatchEventQueue = new DispatchEventQueue();
- private final TouchStream mWebKitTouchStream = new TouchStream();
- private final WebKitCallbacks mWebKitCallbacks;
- private final WebKitHandler mWebKitHandler;
- private boolean mWebKitDispatchScheduled;
- private boolean mWebKitTimeoutScheduled;
- private long mWebKitTimeoutTime;
-
- // UI state, tracks events observed by the UI. (guarded by mLock)
- private final DispatchEventQueue mUiDispatchEventQueue = new DispatchEventQueue();
- private final TouchStream mUiTouchStream = new TouchStream();
- private final UiCallbacks mUiCallbacks;
- private final UiHandler mUiHandler;
- private boolean mUiDispatchScheduled;
-
- // Give up on web kit handling of input events when this timeout expires.
- private static final long WEBKIT_TIMEOUT_MILLIS = 200;
- private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
- private static final int LONG_PRESS_TIMEOUT =
- ViewConfiguration.getLongPressTimeout() + TAP_TIMEOUT;
- private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
- private static final int PRESSED_STATE_DURATION = ViewConfiguration.getPressedStateDuration();
-
- /**
- * Event type: Indicates a touch event type.
- *
- * This event is delivered together with a {@link MotionEvent} with one of the
- * following actions: {@link MotionEvent#ACTION_DOWN}, {@link MotionEvent#ACTION_MOVE},
- * {@link MotionEvent#ACTION_UP}, {@link MotionEvent#ACTION_POINTER_DOWN},
- * {@link MotionEvent#ACTION_POINTER_UP}, {@link MotionEvent#ACTION_CANCEL}.
- */
- public static final int EVENT_TYPE_TOUCH = 0;
-
- /**
- * Event type: Indicates a hover event type.
- *
- * This event is delivered together with a {@link MotionEvent} with one of the
- * following actions: {@link MotionEvent#ACTION_HOVER_ENTER},
- * {@link MotionEvent#ACTION_HOVER_MOVE}, {@link MotionEvent#ACTION_HOVER_MOVE}.
- */
- public static final int EVENT_TYPE_HOVER = 1;
-
- /**
- * Event type: Indicates a scroll event type.
- *
- * This event is delivered together with a {@link MotionEvent} with action
- * {@link MotionEvent#ACTION_SCROLL}.
- */
- public static final int EVENT_TYPE_SCROLL = 2;
-
- /**
- * Event type: Indicates a long-press event type.
- *
- * This event is delivered in the middle of a sequence of {@link #EVENT_TYPE_TOUCH} events.
- * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_MOVE}
- * that indicates the current touch coordinates of the long-press.
- *
- * This event is sent when the current touch gesture has been held longer than
- * the long-press interval.
- */
- public static final int EVENT_TYPE_LONG_PRESS = 3;
-
- /**
- * Event type: Indicates a click event type.
- *
- * This event is delivered after a sequence of {@link #EVENT_TYPE_TOUCH} events that
- * comprise a complete gesture ending with {@link MotionEvent#ACTION_UP}.
- * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_UP}
- * that indicates the location of the click.
- *
- * This event is sent shortly after the end of a touch after the double-tap
- * interval has expired to indicate a click.
- */
- public static final int EVENT_TYPE_CLICK = 4;
-
- /**
- * Event type: Indicates a double-tap event type.
- *
- * This event is delivered after a sequence of {@link #EVENT_TYPE_TOUCH} events that
- * comprise a complete gesture ending with {@link MotionEvent#ACTION_UP}.
- * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_UP}
- * that indicates the location of the double-tap.
- *
- * This event is sent immediately after a sequence of two touches separated
- * in time by no more than the double-tap interval and separated in space
- * by no more than the double-tap slop.
- */
- public static final int EVENT_TYPE_DOUBLE_TAP = 5;
-
- /**
- * Event type: Indicates that a hit test should be performed
- */
- public static final int EVENT_TYPE_HIT_TEST = 6;
-
- /**
- * Flag: This event is private to this queue. Do not forward it.
- */
- public static final int FLAG_PRIVATE = 1 << 0;
-
- /**
- * Flag: This event is currently being processed by web kit.
- * If a timeout occurs, make a copy of it before forwarding the event to another queue.
- */
- public static final int FLAG_WEBKIT_IN_PROGRESS = 1 << 1;
-
- /**
- * Flag: A timeout occurred while waiting for web kit to process this input event.
- */
- public static final int FLAG_WEBKIT_TIMEOUT = 1 << 2;
-
- /**
- * Flag: Indicates that the event was transformed for delivery to web kit.
- * The event must be transformed back before being delivered to the UI.
- */
- public static final int FLAG_WEBKIT_TRANSFORMED_EVENT = 1 << 3;
-
- public WebViewInputDispatcher(UiCallbacks uiCallbacks, WebKitCallbacks webKitCallbacks) {
- this.mUiCallbacks = uiCallbacks;
- mUiHandler = new UiHandler(uiCallbacks.getUiLooper());
-
- this.mWebKitCallbacks = webKitCallbacks;
- mWebKitHandler = new WebKitHandler(webKitCallbacks.getWebKitLooper());
-
- ViewConfiguration config = ViewConfiguration.get(mUiCallbacks.getContext());
- mDoubleTapSlopSquared = config.getScaledDoubleTapSlop();
- mDoubleTapSlopSquared = (mDoubleTapSlopSquared * mDoubleTapSlopSquared);
- mTouchSlopSquared = config.getScaledTouchSlop();
- mTouchSlopSquared = (mTouchSlopSquared * mTouchSlopSquared);
- }
-
- /**
- * Sets whether web kit wants to receive touch events.
- *
- * @param enable True to enable dispatching of touch events to web kit, otherwise
- * web kit will be skipped.
- */
- public void setWebKitWantsTouchEvents(boolean enable) {
- if (DEBUG) {
- Log.d(TAG, "webkitWantsTouchEvents: " + enable);
- }
- synchronized (mLock) {
- if (mPostSendTouchEventsToWebKit != enable) {
- if (!enable) {
- enqueueWebKitCancelTouchEventIfNeededLocked();
- }
- mPostSendTouchEventsToWebKit = enable;
- }
- }
- }
-
- /**
- * Posts a pointer event to the dispatch queue.
- *
- * @param event The event to post.
- * @param webKitXOffset X offset to apply to events before dispatching them to web kit.
- * @param webKitYOffset Y offset to apply to events before dispatching them to web kit.
- * @param webKitScale The scale factor to apply to translated events before dispatching
- * them to web kit.
- * @return True if the dispatcher will handle the event, false if the event is unsupported.
- */
- public boolean postPointerEvent(MotionEvent event,
- int webKitXOffset, int webKitYOffset, float webKitScale) {
- if (event == null) {
- throw new IllegalArgumentException("event cannot be null");
- }
-
- if (DEBUG) {
- Log.d(TAG, "postPointerEvent: " + event);
- }
-
- final int action = event.getActionMasked();
- final int eventType;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_CANCEL:
- eventType = EVENT_TYPE_TOUCH;
- break;
- case MotionEvent.ACTION_SCROLL:
- eventType = EVENT_TYPE_SCROLL;
- break;
- case MotionEvent.ACTION_HOVER_ENTER:
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_HOVER_EXIT:
- eventType = EVENT_TYPE_HOVER;
- break;
- default:
- return false; // currently unsupported event type
- }
-
- synchronized (mLock) {
- // Ensure that the event is consistent and should be delivered.
- MotionEvent eventToEnqueue = event;
- if (eventType == EVENT_TYPE_TOUCH) {
- eventToEnqueue = mPostTouchStream.update(event);
- if (eventToEnqueue == null) {
- if (DEBUG) {
- Log.d(TAG, "postPointerEvent: dropped event " + event);
- }
- unscheduleLongPressLocked();
- unscheduleClickLocked();
- hideTapCandidateLocked();
- return false;
- }
-
- if (action == MotionEvent.ACTION_DOWN && mPostSendTouchEventsToWebKit) {
- if (mUiCallbacks.shouldInterceptTouchEvent(eventToEnqueue)) {
- mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true;
- } else if (mPostDoNotSendTouchEventsToWebKitUntilNextGesture) {
- // Recover from a previous web kit timeout.
- mPostDoNotSendTouchEventsToWebKitUntilNextGesture = false;
- }
- }
- }
-
- // Copy the event because we need to retain ownership.
- if (eventToEnqueue == event) {
- eventToEnqueue = event.copy();
- }
-
- DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, eventType, 0,
- webKitXOffset, webKitYOffset, webKitScale);
- updateStateTrackersLocked(d, event);
- enqueueEventLocked(d);
- }
- return true;
- }
-
- private void scheduleLongPressLocked() {
- unscheduleLongPressLocked();
- mPostLongPressScheduled = true;
- mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_LONG_PRESS,
- LONG_PRESS_TIMEOUT);
- }
-
- private void unscheduleLongPressLocked() {
- if (mPostLongPressScheduled) {
- mPostLongPressScheduled = false;
- mUiHandler.removeMessages(UiHandler.MSG_LONG_PRESS);
- }
- }
-
- private void postLongPress() {
- synchronized (mLock) {
- if (!mPostLongPressScheduled) {
- return;
- }
- mPostLongPressScheduled = false;
-
- MotionEvent event = mPostTouchStream.getLastEvent();
- if (event == null) {
- return;
- }
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_POINTER_UP:
- break;
- default:
- return;
- }
-
- MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
- eventToEnqueue.setAction(MotionEvent.ACTION_MOVE);
- DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_LONG_PRESS, 0,
- mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
- enqueueEventLocked(d);
- }
- }
-
- private void hideTapCandidateLocked() {
- unscheduleHideTapHighlightLocked();
- unscheduleShowTapHighlightLocked();
- mUiCallbacks.showTapHighlight(false);
- }
-
- private void showTapCandidateLocked() {
- unscheduleHideTapHighlightLocked();
- unscheduleShowTapHighlightLocked();
- mUiCallbacks.showTapHighlight(true);
- }
-
- private void scheduleShowTapHighlightLocked() {
- unscheduleShowTapHighlightLocked();
- mPostShowTapHighlightScheduled = true;
- mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_SHOW_TAP_HIGHLIGHT,
- TAP_TIMEOUT);
- }
-
- private void unscheduleShowTapHighlightLocked() {
- if (mPostShowTapHighlightScheduled) {
- mPostShowTapHighlightScheduled = false;
- mUiHandler.removeMessages(UiHandler.MSG_SHOW_TAP_HIGHLIGHT);
- }
- }
-
- private void scheduleHideTapHighlightLocked() {
- unscheduleHideTapHighlightLocked();
- mPostHideTapHighlightScheduled = true;
- mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_HIDE_TAP_HIGHLIGHT,
- PRESSED_STATE_DURATION);
- }
-
- private void unscheduleHideTapHighlightLocked() {
- if (mPostHideTapHighlightScheduled) {
- mPostHideTapHighlightScheduled = false;
- mUiHandler.removeMessages(UiHandler.MSG_HIDE_TAP_HIGHLIGHT);
- }
- }
-
- private void postShowTapHighlight(boolean show) {
- synchronized (mLock) {
- if (show) {
- if (!mPostShowTapHighlightScheduled) {
- return;
- }
- mPostShowTapHighlightScheduled = false;
- } else {
- if (!mPostHideTapHighlightScheduled) {
- return;
- }
- mPostHideTapHighlightScheduled = false;
- }
- mUiCallbacks.showTapHighlight(show);
- }
- }
-
- private void scheduleClickLocked() {
- unscheduleClickLocked();
- mPostClickScheduled = true;
- mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_CLICK, DOUBLE_TAP_TIMEOUT);
- }
-
- private void unscheduleClickLocked() {
- if (mPostClickScheduled) {
- mPostClickScheduled = false;
- mUiHandler.removeMessages(UiHandler.MSG_CLICK);
- }
- }
-
- private void postClick() {
- synchronized (mLock) {
- if (!mPostClickScheduled) {
- return;
- }
- mPostClickScheduled = false;
-
- MotionEvent event = mPostTouchStream.getLastEvent();
- if (event == null || event.getAction() != MotionEvent.ACTION_UP) {
- return;
- }
-
- showTapCandidateLocked();
- MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
- DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_CLICK, 0,
- mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
- enqueueEventLocked(d);
- }
- }
-
- private void checkForDoubleTapOnDownLocked(MotionEvent event) {
- mIsDoubleTapCandidate = false;
- if (!mPostClickScheduled) {
- return;
- }
- int deltaX = (int) mInitialDownX - (int) event.getX();
- int deltaY = (int) mInitialDownY - (int) event.getY();
- if ((deltaX * deltaX + deltaY * deltaY) < mDoubleTapSlopSquared) {
- unscheduleClickLocked();
- mIsDoubleTapCandidate = true;
- }
- }
-
- private boolean isClickCandidateLocked(MotionEvent event) {
- if (event == null
- || event.getActionMasked() != MotionEvent.ACTION_UP
- || !mIsTapCandidate) {
- return false;
- }
- long downDuration = event.getEventTime() - event.getDownTime();
- return downDuration < LONG_PRESS_TIMEOUT;
- }
-
- private void enqueueDoubleTapLocked(MotionEvent event) {
- MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
- DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_DOUBLE_TAP, 0,
- mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
- enqueueEventLocked(d);
- }
-
- private void enqueueHitTestLocked(MotionEvent event) {
- mUiCallbacks.clearPreviousHitTest();
- MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
- DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_HIT_TEST, 0,
- mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
- enqueueEventLocked(d);
- }
-
- private void checkForSlopLocked(MotionEvent event) {
- if (!mIsTapCandidate) {
- return;
- }
- int deltaX = (int) mInitialDownX - (int) event.getX();
- int deltaY = (int) mInitialDownY - (int) event.getY();
- if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquared) {
- unscheduleLongPressLocked();
- mIsTapCandidate = false;
- hideTapCandidateLocked();
- }
- }
-
- private void updateStateTrackersLocked(DispatchEvent d, MotionEvent event) {
- mPostLastWebKitXOffset = d.mWebKitXOffset;
- mPostLastWebKitYOffset = d.mWebKitYOffset;
- mPostLastWebKitScale = d.mWebKitScale;
- int action = event != null ? event.getAction() : MotionEvent.ACTION_CANCEL;
- if (d.mEventType != EVENT_TYPE_TOUCH) {
- return;
- }
-
- if (action == MotionEvent.ACTION_CANCEL
- || event.getPointerCount() > 1) {
- unscheduleLongPressLocked();
- unscheduleClickLocked();
- hideTapCandidateLocked();
- mIsDoubleTapCandidate = false;
- mIsTapCandidate = false;
- hideTapCandidateLocked();
- } else if (action == MotionEvent.ACTION_DOWN) {
- checkForDoubleTapOnDownLocked(event);
- scheduleLongPressLocked();
- mIsTapCandidate = true;
- mInitialDownX = event.getX();
- mInitialDownY = event.getY();
- enqueueHitTestLocked(event);
- if (mIsDoubleTapCandidate) {
- hideTapCandidateLocked();
- } else {
- scheduleShowTapHighlightLocked();
- }
- } else if (action == MotionEvent.ACTION_UP) {
- unscheduleLongPressLocked();
- if (isClickCandidateLocked(event)) {
- if (mIsDoubleTapCandidate) {
- hideTapCandidateLocked();
- enqueueDoubleTapLocked(event);
- } else {
- scheduleClickLocked();
- }
- } else {
- hideTapCandidateLocked();
- }
- } else if (action == MotionEvent.ACTION_MOVE) {
- checkForSlopLocked(event);
- }
- }
-
- /**
- * Dispatches pending web kit events.
- * Must only be called from the web kit thread.
- *
- * This method may be used to flush the queue of pending input events
- * immediately. This method may help to reduce input dispatch latency
- * if called before certain expensive operations such as drawing.
- */
- public void dispatchWebKitEvents() {
- dispatchWebKitEvents(false);
- }
-
- private void dispatchWebKitEvents(boolean calledFromHandler) {
- for (;;) {
- // Get the next event, but leave it in the queue so we can move it to the UI
- // queue if a timeout occurs.
- DispatchEvent d;
- MotionEvent event;
- final int eventType;
- int flags;
- synchronized (mLock) {
- if (!ENABLE_EVENT_BATCHING) {
- drainStaleWebKitEventsLocked();
- }
- d = mWebKitDispatchEventQueue.mHead;
- if (d == null) {
- if (mWebKitDispatchScheduled) {
- mWebKitDispatchScheduled = false;
- if (!calledFromHandler) {
- mWebKitHandler.removeMessages(
- WebKitHandler.MSG_DISPATCH_WEBKIT_EVENTS);
- }
- }
- return;
- }
-
- event = d.mEvent;
- if (event != null) {
- event.offsetLocation(d.mWebKitXOffset, d.mWebKitYOffset);
- event.scale(d.mWebKitScale);
- d.mFlags |= FLAG_WEBKIT_TRANSFORMED_EVENT;
- }
-
- eventType = d.mEventType;
- if (eventType == EVENT_TYPE_TOUCH) {
- event = mWebKitTouchStream.update(event);
- if (DEBUG && event == null && d.mEvent != null) {
- Log.d(TAG, "dispatchWebKitEvents: dropped event " + d.mEvent);
- }
- }
-
- d.mFlags |= FLAG_WEBKIT_IN_PROGRESS;
- flags = d.mFlags;
- }
-
- // Handle the event.
- final boolean preventDefault;
- if (event == null) {
- preventDefault = false;
- } else {
- preventDefault = dispatchWebKitEvent(event, eventType, flags);
- }
-
- synchronized (mLock) {
- flags = d.mFlags;
- d.mFlags = flags & ~FLAG_WEBKIT_IN_PROGRESS;
- boolean recycleEvent = event != d.mEvent;
-
- if ((flags & FLAG_WEBKIT_TIMEOUT) != 0) {
- // A timeout occurred!
- recycleDispatchEventLocked(d);
- } else {
- // Web kit finished in a timely manner. Dequeue the event.
- assert mWebKitDispatchEventQueue.mHead == d;
- mWebKitDispatchEventQueue.dequeue();
-
- updateWebKitTimeoutLocked();
-
- if ((flags & FLAG_PRIVATE) != 0) {
- // Event was intended for web kit only. All done.
- recycleDispatchEventLocked(d);
- } else if (preventDefault) {
- // Web kit has decided to consume the event!
- if (d.mEventType == EVENT_TYPE_TOUCH) {
- enqueueUiCancelTouchEventIfNeededLocked();
- unscheduleLongPressLocked();
- }
- } else {
- // Web kit is being friendly. Pass the event to the UI.
- enqueueUiEventUnbatchedLocked(d);
- }
- }
-
- if (event != null && recycleEvent) {
- event.recycle();
- }
-
- if (eventType == EVENT_TYPE_CLICK) {
- scheduleHideTapHighlightLocked();
- }
- }
- }
- }
-
- // Runs on web kit thread.
- private boolean dispatchWebKitEvent(MotionEvent event, int eventType, int flags) {
- if (DEBUG) {
- Log.d(TAG, "dispatchWebKitEvent: event=" + event
- + ", eventType=" + eventType + ", flags=" + flags);
- }
- boolean preventDefault = mWebKitCallbacks.dispatchWebKitEvent(
- this, event, eventType, flags);
- if (DEBUG) {
- Log.d(TAG, "dispatchWebKitEvent: preventDefault=" + preventDefault);
- }
- return preventDefault;
- }
-
- private boolean isMoveEventLocked(DispatchEvent d) {
- return d.mEvent != null
- && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE;
- }
-
- private void drainStaleWebKitEventsLocked() {
- DispatchEvent d = mWebKitDispatchEventQueue.mHead;
- while (d != null && d.mNext != null
- && isMoveEventLocked(d)
- && isMoveEventLocked(d.mNext)) {
- DispatchEvent next = d.mNext;
- skipWebKitEventLocked(d);
- d = next;
- }
- mWebKitDispatchEventQueue.mHead = d;
- }
-
- // Called by WebKit when it doesn't care about the rest of the touch stream
- public void skipWebkitForRemainingTouchStream() {
- // Just treat this like a timeout
- handleWebKitTimeout();
- }
-
- // Runs on UI thread in response to the web kit thread appearing to be unresponsive.
- private void handleWebKitTimeout() {
- synchronized (mLock) {
- if (!mWebKitTimeoutScheduled) {
- return;
- }
- mWebKitTimeoutScheduled = false;
-
- if (DEBUG) {
- Log.d(TAG, "handleWebKitTimeout: timeout occurred!");
- }
-
- // Drain the web kit event queue.
- DispatchEvent d = mWebKitDispatchEventQueue.dequeueList();
-
- // If web kit was processing an event (must be at the head of the list because
- // it can only do one at a time), then clone it or ignore it.
- if ((d.mFlags & FLAG_WEBKIT_IN_PROGRESS) != 0) {
- d.mFlags |= FLAG_WEBKIT_TIMEOUT;
- if ((d.mFlags & FLAG_PRIVATE) != 0) {
- d = d.mNext; // the event is private to web kit, ignore it
- } else {
- d = copyDispatchEventLocked(d);
- d.mFlags &= ~FLAG_WEBKIT_IN_PROGRESS;
- }
- }
-
- // Enqueue all non-private events for handling by the UI thread.
- while (d != null) {
- DispatchEvent next = d.mNext;
- skipWebKitEventLocked(d);
- d = next;
- }
-
- // Tell web kit to cancel all pending touches.
- // This also prevents us from sending web kit any more touches until the
- // next gesture begins. (As required to ensure touch event stream consistency.)
- enqueueWebKitCancelTouchEventIfNeededLocked();
- }
- }
-
- private void skipWebKitEventLocked(DispatchEvent d) {
- d.mNext = null;
- if ((d.mFlags & FLAG_PRIVATE) != 0) {
- recycleDispatchEventLocked(d);
- } else {
- d.mFlags |= FLAG_WEBKIT_TIMEOUT;
- enqueueUiEventUnbatchedLocked(d);
- }
- }
-
- /**
- * Dispatches pending UI events.
- * Must only be called from the UI thread.
- *
- * This method may be used to flush the queue of pending input events
- * immediately. This method may help to reduce input dispatch latency
- * if called before certain expensive operations such as drawing.
- */
- public void dispatchUiEvents() {
- dispatchUiEvents(false);
- }
-
- private void dispatchUiEvents(boolean calledFromHandler) {
- for (;;) {
- MotionEvent event;
- final int eventType;
- final int flags;
- synchronized (mLock) {
- DispatchEvent d = mUiDispatchEventQueue.dequeue();
- if (d == null) {
- if (mUiDispatchScheduled) {
- mUiDispatchScheduled = false;
- if (!calledFromHandler) {
- mUiHandler.removeMessages(UiHandler.MSG_DISPATCH_UI_EVENTS);
- }
- }
- return;
- }
-
- event = d.mEvent;
- if (event != null && (d.mFlags & FLAG_WEBKIT_TRANSFORMED_EVENT) != 0) {
- event.scale(1.0f / d.mWebKitScale);
- event.offsetLocation(-d.mWebKitXOffset, -d.mWebKitYOffset);
- d.mFlags &= ~FLAG_WEBKIT_TRANSFORMED_EVENT;
- }
-
- eventType = d.mEventType;
- if (eventType == EVENT_TYPE_TOUCH) {
- event = mUiTouchStream.update(event);
- if (DEBUG && event == null && d.mEvent != null) {
- Log.d(TAG, "dispatchUiEvents: dropped event " + d.mEvent);
- }
- }
-
- flags = d.mFlags;
-
- if (event == d.mEvent) {
- d.mEvent = null; // retain ownership of event, don't recycle it yet
- }
- recycleDispatchEventLocked(d);
-
- if (eventType == EVENT_TYPE_CLICK) {
- scheduleHideTapHighlightLocked();
- }
- }
-
- // Handle the event.
- if (event != null) {
- dispatchUiEvent(event, eventType, flags);
- event.recycle();
- }
- }
- }
-
- // Runs on UI thread.
- private void dispatchUiEvent(MotionEvent event, int eventType, int flags) {
- if (DEBUG) {
- Log.d(TAG, "dispatchUiEvent: event=" + event
- + ", eventType=" + eventType + ", flags=" + flags);
- }
- mUiCallbacks.dispatchUiEvent(event, eventType, flags);
- }
-
- private void enqueueEventLocked(DispatchEvent d) {
- if (!shouldSkipWebKit(d)) {
- enqueueWebKitEventLocked(d);
- } else {
- enqueueUiEventLocked(d);
- }
- }
-
- private boolean shouldSkipWebKit(DispatchEvent d) {
- switch (d.mEventType) {
- case EVENT_TYPE_CLICK:
- case EVENT_TYPE_HOVER:
- case EVENT_TYPE_SCROLL:
- case EVENT_TYPE_HIT_TEST:
- return false;
- case EVENT_TYPE_TOUCH:
- // TODO: This should be cleaned up. We now have WebViewInputDispatcher
- // and WebViewClassic both checking for slop and doing their own
- // thing - they should be consolidated. And by consolidated, I mean
- // WebViewClassic's version should just be deleted.
- // The reason this is done is because webpages seem to expect
- // that they only get an ontouchmove if the slop has been exceeded.
- if (mIsTapCandidate && d.mEvent != null
- && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
- return true;
- }
- return !mPostSendTouchEventsToWebKit
- || mPostDoNotSendTouchEventsToWebKitUntilNextGesture;
- }
- return true;
- }
-
- private void enqueueWebKitCancelTouchEventIfNeededLocked() {
- // We want to cancel touch events that were delivered to web kit.
- // Enqueue a null event at the end of the queue if needed.
- if (mWebKitTouchStream.isCancelNeeded() || !mWebKitDispatchEventQueue.isEmpty()) {
- DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE,
- 0, 0, 1.0f);
- enqueueWebKitEventUnbatchedLocked(d);
- mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true;
- }
- }
-
- private void enqueueWebKitEventLocked(DispatchEvent d) {
- if (batchEventLocked(d, mWebKitDispatchEventQueue.mTail)) {
- if (DEBUG) {
- Log.d(TAG, "enqueueWebKitEventLocked: batched event " + d.mEvent);
- }
- recycleDispatchEventLocked(d);
- } else {
- enqueueWebKitEventUnbatchedLocked(d);
- }
- }
-
- private void enqueueWebKitEventUnbatchedLocked(DispatchEvent d) {
- if (DEBUG) {
- Log.d(TAG, "enqueueWebKitEventUnbatchedLocked: enqueued event " + d.mEvent);
- }
- mWebKitDispatchEventQueue.enqueue(d);
- scheduleWebKitDispatchLocked();
- updateWebKitTimeoutLocked();
- }
-
- private void scheduleWebKitDispatchLocked() {
- if (!mWebKitDispatchScheduled) {
- mWebKitHandler.sendEmptyMessage(WebKitHandler.MSG_DISPATCH_WEBKIT_EVENTS);
- mWebKitDispatchScheduled = true;
- }
- }
-
- private void updateWebKitTimeoutLocked() {
- DispatchEvent d = mWebKitDispatchEventQueue.mHead;
- if (d != null && mWebKitTimeoutScheduled && mWebKitTimeoutTime == d.mTimeoutTime) {
- return;
- }
- if (mWebKitTimeoutScheduled) {
- mUiHandler.removeMessages(UiHandler.MSG_WEBKIT_TIMEOUT);
- mWebKitTimeoutScheduled = false;
- }
- if (d != null) {
- mUiHandler.sendEmptyMessageAtTime(UiHandler.MSG_WEBKIT_TIMEOUT, d.mTimeoutTime);
- mWebKitTimeoutScheduled = true;
- mWebKitTimeoutTime = d.mTimeoutTime;
- }
- }
-
- private void enqueueUiCancelTouchEventIfNeededLocked() {
- // We want to cancel touch events that were delivered to the UI.
- // Enqueue a null event at the end of the queue if needed.
- if (mUiTouchStream.isCancelNeeded() || !mUiDispatchEventQueue.isEmpty()) {
- DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE,
- 0, 0, 1.0f);
- enqueueUiEventUnbatchedLocked(d);
- }
- }
-
- private void enqueueUiEventLocked(DispatchEvent d) {
- if (batchEventLocked(d, mUiDispatchEventQueue.mTail)) {
- if (DEBUG) {
- Log.d(TAG, "enqueueUiEventLocked: batched event " + d.mEvent);
- }
- recycleDispatchEventLocked(d);
- } else {
- enqueueUiEventUnbatchedLocked(d);
- }
- }
-
- private void enqueueUiEventUnbatchedLocked(DispatchEvent d) {
- if (DEBUG) {
- Log.d(TAG, "enqueueUiEventUnbatchedLocked: enqueued event " + d.mEvent);
- }
- mUiDispatchEventQueue.enqueue(d);
- scheduleUiDispatchLocked();
- }
-
- private void scheduleUiDispatchLocked() {
- if (!mUiDispatchScheduled) {
- mUiHandler.sendEmptyMessage(UiHandler.MSG_DISPATCH_UI_EVENTS);
- mUiDispatchScheduled = true;
- }
- }
-
- private boolean batchEventLocked(DispatchEvent in, DispatchEvent tail) {
- if (!ENABLE_EVENT_BATCHING) {
- return false;
- }
- if (tail != null && tail.mEvent != null && in.mEvent != null
- && in.mEventType == tail.mEventType
- && in.mFlags == tail.mFlags
- && in.mWebKitXOffset == tail.mWebKitXOffset
- && in.mWebKitYOffset == tail.mWebKitYOffset
- && in.mWebKitScale == tail.mWebKitScale) {
- return tail.mEvent.addBatch(in.mEvent);
- }
- return false;
- }
-
- private DispatchEvent obtainDispatchEventLocked(MotionEvent event,
- int eventType, int flags, int webKitXOffset, int webKitYOffset, float webKitScale) {
- DispatchEvent d = obtainUninitializedDispatchEventLocked();
- d.mEvent = event;
- d.mEventType = eventType;
- d.mFlags = flags;
- d.mTimeoutTime = SystemClock.uptimeMillis() + WEBKIT_TIMEOUT_MILLIS;
- d.mWebKitXOffset = webKitXOffset;
- d.mWebKitYOffset = webKitYOffset;
- d.mWebKitScale = webKitScale;
- if (DEBUG) {
- Log.d(TAG, "Timeout time: " + (d.mTimeoutTime - SystemClock.uptimeMillis()));
- }
- return d;
- }
-
- private DispatchEvent copyDispatchEventLocked(DispatchEvent d) {
- DispatchEvent copy = obtainUninitializedDispatchEventLocked();
- if (d.mEvent != null) {
- copy.mEvent = d.mEvent.copy();
- }
- copy.mEventType = d.mEventType;
- copy.mFlags = d.mFlags;
- copy.mTimeoutTime = d.mTimeoutTime;
- copy.mWebKitXOffset = d.mWebKitXOffset;
- copy.mWebKitYOffset = d.mWebKitYOffset;
- copy.mWebKitScale = d.mWebKitScale;
- copy.mNext = d.mNext;
- return copy;
- }
-
- private DispatchEvent obtainUninitializedDispatchEventLocked() {
- DispatchEvent d = mDispatchEventPool;
- if (d != null) {
- mDispatchEventPoolSize -= 1;
- mDispatchEventPool = d.mNext;
- d.mNext = null;
- } else {
- d = new DispatchEvent();
- }
- return d;
- }
-
- private void recycleDispatchEventLocked(DispatchEvent d) {
- if (d.mEvent != null) {
- d.mEvent.recycle();
- d.mEvent = null;
- }
-
- if (mDispatchEventPoolSize < MAX_DISPATCH_EVENT_POOL_SIZE) {
- mDispatchEventPoolSize += 1;
- d.mNext = mDispatchEventPool;
- mDispatchEventPool = d;
- }
- }
-
- /* Implemented by {@link WebViewClassic} to perform operations on the UI thread. */
- public static interface UiCallbacks {
- /**
- * Gets the UI thread's looper.
- * @return The looper.
- */
- public Looper getUiLooper();
-
- /**
- * Gets the UI's context
- * @return The context
- */
- public Context getContext();
-
- /**
- * Dispatches an event to the UI.
- * @param event The event.
- * @param eventType The event type.
- * @param flags The event's dispatch flags.
- */
- public void dispatchUiEvent(MotionEvent event, int eventType, int flags);
-
- /**
- * Asks the UI thread whether this touch event stream should be
- * intercepted based on the touch down event.
- * @param event The touch down event.
- * @return true if the UI stream wants the touch stream without going
- * through webkit or false otherwise.
- */
- public boolean shouldInterceptTouchEvent(MotionEvent event);
-
- /**
- * Inform's the UI that it should show the tap highlight
- * @param show True if it should show the highlight, false if it should hide it
- */
- public void showTapHighlight(boolean show);
-
- /**
- * Called when we are sending a new EVENT_TYPE_HIT_TEST to WebKit, so
- * previous hit tests should be cleared as they are obsolete.
- */
- public void clearPreviousHitTest();
- }
-
- /* Implemented by {@link WebViewCore} to perform operations on the web kit thread. */
- public static interface WebKitCallbacks {
- /**
- * Gets the web kit thread's looper.
- * @return The looper.
- */
- public Looper getWebKitLooper();
-
- /**
- * Dispatches an event to web kit.
- * @param dispatcher The WebViewInputDispatcher sending the event
- * @param event The event.
- * @param eventType The event type.
- * @param flags The event's dispatch flags.
- * @return True if web kit wants to prevent default event handling.
- */
- public boolean dispatchWebKitEvent(WebViewInputDispatcher dispatcher,
- MotionEvent event, int eventType, int flags);
- }
-
- // Runs on UI thread.
- private final class UiHandler extends Handler {
- public static final int MSG_DISPATCH_UI_EVENTS = 1;
- public static final int MSG_WEBKIT_TIMEOUT = 2;
- public static final int MSG_LONG_PRESS = 3;
- public static final int MSG_CLICK = 4;
- public static final int MSG_SHOW_TAP_HIGHLIGHT = 5;
- public static final int MSG_HIDE_TAP_HIGHLIGHT = 6;
-
- public UiHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DISPATCH_UI_EVENTS:
- dispatchUiEvents(true);
- break;
- case MSG_WEBKIT_TIMEOUT:
- handleWebKitTimeout();
- break;
- case MSG_LONG_PRESS:
- postLongPress();
- break;
- case MSG_CLICK:
- postClick();
- break;
- case MSG_SHOW_TAP_HIGHLIGHT:
- postShowTapHighlight(true);
- break;
- case MSG_HIDE_TAP_HIGHLIGHT:
- postShowTapHighlight(false);
- break;
- default:
- throw new IllegalStateException("Unknown message type: " + msg.what);
- }
- }
- }
-
- // Runs on web kit thread.
- private final class WebKitHandler extends Handler {
- public static final int MSG_DISPATCH_WEBKIT_EVENTS = 1;
-
- public WebKitHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DISPATCH_WEBKIT_EVENTS:
- dispatchWebKitEvents(true);
- break;
- default:
- throw new IllegalStateException("Unknown message type: " + msg.what);
- }
- }
- }
-
- private static final class DispatchEvent {
- public DispatchEvent mNext;
-
- public MotionEvent mEvent;
- public int mEventType;
- public int mFlags;
- public long mTimeoutTime;
- public int mWebKitXOffset;
- public int mWebKitYOffset;
- public float mWebKitScale;
- }
-
- private static final class DispatchEventQueue {
- public DispatchEvent mHead;
- public DispatchEvent mTail;
-
- public boolean isEmpty() {
- return mHead != null;
- }
-
- public void enqueue(DispatchEvent d) {
- if (mHead == null) {
- mHead = d;
- mTail = d;
- } else {
- mTail.mNext = d;
- mTail = d;
- }
- }
-
- public DispatchEvent dequeue() {
- DispatchEvent d = mHead;
- if (d != null) {
- DispatchEvent next = d.mNext;
- if (next == null) {
- mHead = null;
- mTail = null;
- } else {
- mHead = next;
- d.mNext = null;
- }
- }
- return d;
- }
-
- public DispatchEvent dequeueList() {
- DispatchEvent d = mHead;
- if (d != null) {
- mHead = null;
- mTail = null;
- }
- return d;
- }
- }
-
- /**
- * Keeps track of a stream of touch events so that we can discard touch
- * events that would make the stream inconsistent.
- */
- private static final class TouchStream {
- private MotionEvent mLastEvent;
-
- /**
- * Gets the last touch event that was delivered.
- * @return The last touch event, or null if none.
- */
- public MotionEvent getLastEvent() {
- return mLastEvent;
- }
-
- /**
- * Updates the touch event stream.
- * @param event The event that we intend to send, or null to cancel the
- * touch event stream.
- * @return The event that we should actually send, or null if no event should
- * be sent because the proposed event would make the stream inconsistent.
- */
- public MotionEvent update(MotionEvent event) {
- if (event == null) {
- if (isCancelNeeded()) {
- event = mLastEvent;
- if (event != null) {
- event.setAction(MotionEvent.ACTION_CANCEL);
- mLastEvent = null;
- }
- }
- return event;
- }
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_POINTER_UP:
- if (mLastEvent == null
- || mLastEvent.getAction() == MotionEvent.ACTION_UP) {
- return null;
- }
- updateLastEvent(event);
- return event;
-
- case MotionEvent.ACTION_DOWN:
- updateLastEvent(event);
- return event;
-
- case MotionEvent.ACTION_CANCEL:
- if (mLastEvent == null) {
- return null;
- }
- updateLastEvent(null);
- return event;
-
- default:
- return null;
- }
- }
-
- /**
- * Returns true if there is a gesture in progress that may need to be canceled.
- * @return True if cancel is needed.
- */
- public boolean isCancelNeeded() {
- return mLastEvent != null && mLastEvent.getAction() != MotionEvent.ACTION_UP;
- }
-
- private void updateLastEvent(MotionEvent event) {
- if (mLastEvent != null) {
- mLastEvent.recycle();
- }
- mLastEvent = event != null ? MotionEvent.obtainNoHistory(event) : null;
- }
- }
-} \ No newline at end of file