Bug 920036 - Send touch inputs through the APZ before sending them to the gecko thread. r=mwu,dvander,smaug
authorKartikaya Gupta <kgupta@mozilla.com>
Sat, 10 Jan 2015 13:54:24 -0500
changeset 249049 8cae7aeb3a741d0bd7019dd60a0a888266bf6170
parent 249048 22f8aee2e0e050bb14d80c9fd2e5ba96c2702d05
child 249050 8dba8e04babefd3a776b6573b1cb5dbdcb2028ee
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwu, dvander, smaug
bugs920036
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 920036 - Send touch inputs through the APZ before sending them to the gecko thread. r=mwu,dvander,smaug
dom/ipc/TabParent.cpp
widget/gonk/GeckoTouchDispatcher.cpp
widget/gonk/nsWindow.cpp
widget/gonk/nsWindow.h
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2172,17 +2172,21 @@ TabParent::UseAsyncPanZoom()
           GetScrollingBehavior() == ASYNC_PAN_ZOOM);
 }
 
 nsEventStatus
 TabParent::MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent,
                                           ScrollableLayerGuid* aOutTargetGuid,
                                           uint64_t* aOutInputBlockId)
 {
-  if (aEvent.mClass == eWheelEventClass) {
+  if (aEvent.mClass == eWheelEventClass
+#ifdef MOZ_WIDGET_GONK
+      || aEvent.mClass == eTouchEventClass
+#endif
+     ) {
     // Wheel events must be sent to APZ directly from the widget. New APZ-
     // aware events should follow suit and move there as well. However, we
     // do need to inform the child process of the correct target and block
     // id.
     if (aOutTargetGuid) {
       *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid();
     }
     if (aOutInputBlockId) {
--- a/widget/gonk/GeckoTouchDispatcher.cpp
+++ b/widget/gonk/GeckoTouchDispatcher.cpp
@@ -19,17 +19,16 @@
 #include "GeckoProfiler.h"
 #include "GeckoTouchDispatcher.h"
 #include "InputData.h"
 #include "ProfilerMarkers.h"
 #include "base/basictypes.h"
 #include "gfxPrefs.h"
 #include "libui/Input.h"
 #include "mozilla/ClearOnShutdown.h"
-#include "mozilla/MouseEvents.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "nsAppShell.h"
 #include "nsDebug.h"
 #include "nsThreadUtils.h"
@@ -328,31 +327,16 @@ GeckoTouchDispatcher::ResampleTouchMoves
 
   // Both mTimeStamp and mTime are being updated to sampleTime here.
   // mTime needs to be updated using a delta since TimeStamp doesn't
   // provide a way to obtain a raw value.
   aOutTouch.mTime += (sampleTime - aOutTouch.mTimeStamp).ToMilliseconds();
   aOutTouch.mTimeStamp = sampleTime;
 }
 
-// Some touch events get sent as mouse events. If APZ doesn't capture the event
-// and if a touch only has 1 touch input, we can send a mouse event.
-void
-GeckoTouchDispatcher::DispatchMouseEvent(MultiTouchInput& aMultiTouch,
-                                         bool aForwardToChildren)
-{
-  WidgetMouseEvent mouseEvent = aMultiTouch.ToWidgetMouseEvent(nullptr);
-  if (mouseEvent.message == NS_EVENT_NULL) {
-    return;
-  }
-
-  mouseEvent.mFlags.mNoCrossProcessBoundaryForwarding = !aForwardToChildren;
-  nsWindow::DispatchInputEvent(mouseEvent);
-}
-
 static bool
 IsExpired(const MultiTouchInput& aTouch)
 {
   // No pending events, the filter state can be updated.
   uint64_t timeNowMs = systemTime(SYSTEM_TIME_MONOTONIC) / 1000000;
   return (timeNowMs - aTouch.mTime) > kInputExpirationThresholdMs;
 }
 void
@@ -367,19 +351,17 @@ GeckoTouchDispatcher::DispatchTouchEvent
              aMultiTouch.mTouches.Length() == 1) {
     mTouchEventsFiltered = IsExpired(aMultiTouch);
   }
 
   if (mTouchEventsFiltered) {
     return;
   }
 
-  bool captured = false;
-  WidgetTouchEvent event = aMultiTouch.ToWidgetTouchEvent(nullptr);
-  nsEventStatus status = nsWindow::DispatchInputEvent(event, &captured);
+  nsWindow::DispatchTouchInput(aMultiTouch);
 
   if (mEnabledUniformityInfo && profiler_is_active()) {
     const char* touchAction = "Invalid";
     switch (aMultiTouch.mType) {
       case MultiTouchInput::MULTITOUCH_START:
         touchAction = "Touch_Event_Down";
         break;
       case MultiTouchInput::MULTITOUCH_MOVE:
@@ -390,16 +372,11 @@ GeckoTouchDispatcher::DispatchTouchEvent
         touchAction = "Touch_Event_Up";
         break;
     }
 
     const ScreenIntPoint& touchPoint = aMultiTouch.mTouches[0].mScreenPoint;
     TouchDataPayload* payload = new TouchDataPayload(touchPoint);
     PROFILER_MARKER_PAYLOAD(touchAction, payload);
   }
-
-  if (!captured && (aMultiTouch.mTouches.Length() == 1)) {
-    bool forwardToChildren = status != nsEventStatus_eConsumeNoDefault;
-    DispatchMouseEvent(aMultiTouch, forwardToChildren);
-  }
 }
 
 } // namespace mozilla
--- a/widget/gonk/nsWindow.cpp
+++ b/widget/gonk/nsWindow.cpp
@@ -42,16 +42,19 @@
 #include "ClientLayerManager.h"
 #include "BasicLayers.h"
 #include "libdisplay/GonkDisplay.h"
 #include "pixelflinger/format.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorParent.h"
+#include "mozilla/layers/InputAPZContext.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/TouchEvents.h"
 #include "nsThreadUtils.h"
 #include "HwcComposer2D.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
 #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args)
 #define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args)
 
 #define IS_TOPLEVEL() (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog)
@@ -222,43 +225,99 @@ nsWindow::NotifyVsync(TimeStamp aVsyncTi
     CompositorVsyncDispatcher* vsyncDispatcher = gFocusedWindow->GetCompositorVsyncDispatcher();
     // During bootup, there is a delay between when the nsWindow is created
     // and when the Compositor is created, but vsync is already turned on
     if (vsyncDispatcher) {
       vsyncDispatcher->NotifyVsync(aVsyncTimestamp);
     }
 }
 
-nsEventStatus
-nsWindow::DispatchInputEvent(WidgetGUIEvent& aEvent, bool* aWasCaptured)
+/*static*/ nsEventStatus
+nsWindow::DispatchInputEvent(WidgetGUIEvent& aEvent)
 {
-    if (aWasCaptured) {
-        *aWasCaptured = false;
-    }
     if (!gFocusedWindow) {
         return nsEventStatus_eIgnore;
     }
 
     gFocusedWindow->UserActivity();
 
+    nsEventStatus status;
     aEvent.widget = gFocusedWindow;
+    gFocusedWindow->DispatchEvent(&aEvent, status);
+    return status;
+}
 
+/*static*/ void
+nsWindow::DispatchTouchInput(MultiTouchInput& aInput)
+{
+    if (!gFocusedWindow) {
+        return;
+    }
+
+    gFocusedWindow->UserActivity();
+    gFocusedWindow->DispatchTouchInputViaAPZ(aInput);
+}
+
+void
+nsWindow::DispatchTouchInputViaAPZ(MultiTouchInput& aInput)
+{
+    if (!mAPZC) {
+        // In general mAPZC should not be null, but during initial setup
+        // it might be, so we handle that case by ignoring touch input there.
+        return;
+    }
+
+    // First send it through the APZ code
+    mozilla::layers::ScrollableLayerGuid guid;
+    uint64_t inputBlockId;
+    nsEventStatus rv = mAPZC->ReceiveInputEvent(aInput, &guid, &inputBlockId);
+    // If the APZ says to drop it, then we drop it
+    if (rv == nsEventStatus_eConsumeNoDefault) {
+        return;
+    }
+
+    // Convert it to an event we can send to Gecko
+    WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
+
+    // If there is an event capturing child process, send it directly there.
+    // This happens if we already sent a touchstart event through the root
+    // process hit test and it ended up going to a child process. The event
+    // capturing process should get all subsequent touch events in the same
+    // event block. In this case the TryCapture call below will return true,
+    // and the child process will take care of responding to the event as needed
+    // so we don't need to do anything else here.
     if (TabParent* capturer = TabParent::GetEventCapturer()) {
-        bool captured = capturer->TryCapture(aEvent);
-        if (aWasCaptured) {
-            *aWasCaptured = captured;
-        }
-        if (captured) {
-            return nsEventStatus_eConsumeNoDefault;
+        InputAPZContext context(guid, inputBlockId);
+        if (capturer->TryCapture(event)) {
+            return;
         }
     }
 
-    nsEventStatus status;
-    gFocusedWindow->DispatchEvent(&aEvent, status);
-    return status;
+    // If it didn't get captured, dispatch the event into the gecko root process
+    // for "normal" flow. The event might get sent to the child process still,
+    // but if it doesn't we need to notify the APZ of various things. All of
+    // that happens in DispatchEventForAPZ
+    rv = DispatchEventForAPZ(&event, guid, inputBlockId);
+
+    // Finally, if the touch event had only one touch point, generate a mouse
+    // event for it and send it through the gecko root process.
+    // Technically we should not need to do this if the touch event was routed
+    // to the child process, but that seems to expose a bug in B2G where the
+    // keyboard doesn't go away in some cases.
+    // Also for now we're dispatching mouse events from all touch events because
+    // we need this for click events to work in the chrome process. Once we have
+    // APZ and ChromeProcessController::HandleSingleTap working for the chrome
+    // process we shouldn't need to do this at all.
+    if (event.touches.Length() == 1) {
+        WidgetMouseEvent mouseEvent = aInput.ToWidgetMouseEvent(this);
+        if (mouseEvent.message != NS_EVENT_NULL) {
+            mouseEvent.mFlags.mNoCrossProcessBoundaryForwarding = (rv == nsEventStatus_eConsumeNoDefault);
+            DispatchEvent(&mouseEvent, rv);
+        }
+    }
 }
 
 NS_IMETHODIMP
 nsWindow::Create(nsIWidget *aParent,
                  void *aNativeParent,
                  const nsIntRect &aRect,
                  nsDeviceContext *aContext,
                  nsWidgetInitData *aInitData)
--- a/widget/gonk/nsWindow.h
+++ b/widget/gonk/nsWindow.h
@@ -11,16 +11,17 @@
  * 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.
  */
 
 #ifndef nsWindow_h
 #define nsWindow_h
 
+#include "InputData.h"
 #include "nsBaseWidget.h"
 #include "nsRegion.h"
 #include "nsIIdleServiceInternal.h"
 #include "Units.h"
 
 extern nsIntRect gScreenBounds;
 
 namespace mozilla {
@@ -46,18 +47,18 @@ struct InputContextAction;
 class nsWindow : public nsBaseWidget
 {
 public:
     nsWindow();
     virtual ~nsWindow();
 
     static void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp);
     static void DoDraw(void);
-    static nsEventStatus DispatchInputEvent(mozilla::WidgetGUIEvent& aEvent,
-                                            bool* aWasCaptured = nullptr);
+    static nsEventStatus DispatchInputEvent(mozilla::WidgetGUIEvent& aEvent);
+    static void DispatchTouchInput(mozilla::MultiTouchInput& aInput);
 
     NS_IMETHOD Create(nsIWidget *aParent,
                       void *aNativeParent,
                       const nsIntRect &aRect,
                       nsDeviceContext *aContext,
                       nsWidgetInitData *aInitData);
     NS_IMETHOD Destroy(void);
 
@@ -82,16 +83,17 @@ public:
     NS_IMETHOD ConfigureChildren(const nsTArray<nsIWidget::Configuration>&);
     NS_IMETHOD Invalidate(const nsIntRect &aRect);
     virtual void* GetNativeData(uint32_t aDataType);
     NS_IMETHOD SetTitle(const nsAString& aTitle)
     {
         return NS_OK;
     }
     virtual nsIntPoint WidgetToScreenOffset();
+    void DispatchTouchInputViaAPZ(mozilla::MultiTouchInput& aInput);
     NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
                              nsEventStatus& aStatus);
     NS_IMETHOD CaptureRollupEvents(nsIRollupListener *aListener,
                                    bool aDoCapture)
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
     NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);