Bug 774139: Forward touch events across processes. r=felipe,smaug
authorChris Jones <jones.chris.g@gmail.com>
Mon, 16 Jul 2012 14:46:29 -0700
changeset 104942 433ca26168bbaca857a2cf488382708250adb61d
parent 104941 1cbe2e404e808f688501ce57f0ddf4b73a8cd274
child 104943 7fe4e32d8ca37193fe3b489d4349de16fb552400
push id1490
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 18:29:50 +0000
treeherdermozilla-beta@f335e7dacdc1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe, smaug
bugs774139
milestone17.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 774139: Forward touch events across processes. r=felipe,smaug
content/events/src/Makefile.in
content/events/src/nsDOMTouchEvent.h
content/events/src/nsEventStateManager.cpp
content/events/src/nsEventStateManager.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
widget/gonk/nsAppShell.cpp
widget/nsGUIEvent.h
widget/nsGUIEventIPC.h
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -13,16 +13,19 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= content
 LIBRARY_NAME	= gkconevents_s
 LIBXUL_LIBRARY  = 1
 
 EXPORTS		= \
 		nsEventStateManager.h \
 		nsEventListenerManager.h \
 		nsDOMEventTargetHelper.h \
+		nsDOMEvent.h \
+		nsDOMTouchEvent.h \
+		nsDOMUIEvent.h \
 		$(NULL)
 
 CPPSRCS		= \
 		nsEventListenerManager.cpp \
 		nsEventStateManager.cpp \
 		nsDOMEvent.cpp \
 		nsDOMDataContainerEvent.cpp \
 		nsDOMUIEvent.cpp \
--- a/content/events/src/nsDOMTouchEvent.h
+++ b/content/events/src/nsDOMTouchEvent.h
@@ -82,25 +82,26 @@ public:
     mScreenPoint = nsDOMEvent::GetScreenCoords(aPresContext, aEvent, mRefPoint);
     mPointsInitialized = true;
   }
   void SetTarget(nsIDOMEventTarget *aTarget)
   {
     mTarget = aTarget;
   }
   bool Equals(nsIDOMTouch* aTouch);
-protected:
-  bool mPointsInitialized;
+
   PRInt32 mIdentifier;
   nsIntPoint mPagePoint;
   nsIntPoint mClientPoint;
   nsIntPoint mScreenPoint;
   nsIntPoint mRadius;
   float mRotationAngle;
   float mForce;
+protected:
+  bool mPointsInitialized;
 };
 
 class nsDOMTouchList MOZ_FINAL : public nsIDOMTouchList,
                                  public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMTouchList)
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1630,38 +1630,51 @@ nsEventStateManager::HandleAccessKey(nsP
 
       if (esm)
         esm->HandleAccessKey(parentPC, aEvent, aStatus, docShell,
                              eAccessKeyProcessingUp, aModifierMask);
     }
   }// if end. bubble up process
 }// end of HandleAccessKey
 
-void
-nsEventStateManager::DispatchCrossProcessEvent(nsEvent* aEvent, nsIFrameLoader* frameLoader) {
-  nsFrameLoader* fml = static_cast<nsFrameLoader*>(frameLoader);
+bool
+nsEventStateManager::DispatchCrossProcessEvent(nsEvent* aEvent,
+                                               nsIFrameLoader* aFrameLoader,
+                                               nsEventStatus *aStatus) {
+  nsFrameLoader* fml = static_cast<nsFrameLoader*>(aFrameLoader);
   PBrowserParent* remoteBrowser = fml->GetRemoteBrowser();
   TabParent* remote = static_cast<TabParent*>(remoteBrowser);
   if (!remote) {
-    return;
+    return false;
   }
 
-  if (aEvent->eventStructType == NS_MOUSE_EVENT) {
+  switch (aEvent->eventStructType) {
+  case NS_MOUSE_EVENT: {
     nsMouseEvent* mouseEvent = static_cast<nsMouseEvent*>(aEvent);
-    remote->SendRealMouseEvent(*mouseEvent);
+    return remote->SendRealMouseEvent(*mouseEvent);
+  }
+  case NS_KEY_EVENT: {
+    nsKeyEvent* keyEvent = static_cast<nsKeyEvent*>(aEvent);
+    return remote->SendRealKeyEvent(*keyEvent);
   }
-
-  if (aEvent->eventStructType == NS_KEY_EVENT) {
-    nsKeyEvent* keyEvent = static_cast<nsKeyEvent*>(aEvent);
-    remote->SendRealKeyEvent(*keyEvent);
+  case NS_MOUSE_SCROLL_EVENT: {
+    nsMouseScrollEvent* scrollEvent = static_cast<nsMouseScrollEvent*>(aEvent);
+    return remote->SendMouseScrollEvent(*scrollEvent);
   }
-
-  if (aEvent->eventStructType == NS_MOUSE_SCROLL_EVENT) {
-    nsMouseScrollEvent* scrollEvent = static_cast<nsMouseScrollEvent*>(aEvent);
-    remote->SendMouseScrollEvent(*scrollEvent);
+  case NS_TOUCH_EVENT: {
+    // Let the child process synthesize a mouse event if needed, and
+    // ensure we don't synthesize one in this process.
+    *aStatus = nsEventStatus_eConsumeNoDefault;
+    nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(aEvent);
+    return remote->SendRealTouchEvent(*touchEvent);
+  }
+  default: {
+    MOZ_NOT_REACHED("Attempt to send non-whitelisted event?");
+    return false;
+  }
   }
 }
 
 bool
 nsEventStateManager::IsRemoteTarget(nsIContent* target) {
   if (!target) {
     return false;
   }
@@ -1683,36 +1696,54 @@ nsEventStateManager::IsRemoteTarget(nsIC
     if (isBrowser) {
       return !!TabParent::GetFrom(target);
     }
   }
 
   return false;
 }
 
+bool
+CrossProcessSafeEvent(const nsEvent& aEvent)
+{
+  switch (aEvent.eventStructType) {
+  case NS_KEY_EVENT:
+  case NS_MOUSE_SCROLL_EVENT:
+    return true;
+  case NS_MOUSE_EVENT:
+    switch (aEvent.message) {
+    case NS_MOUSE_BUTTON_DOWN:
+    case NS_MOUSE_BUTTON_UP:
+    case NS_MOUSE_MOVE:
+      return true;
+    default:
+      return false;
+    }
+  case NS_TOUCH_EVENT:
+    switch (aEvent.message) {
+    case NS_TOUCH_START:
+    case NS_TOUCH_MOVE:
+    case NS_TOUCH_END:
+    case NS_TOUCH_CANCEL:
+      return true;
+    default:
+      return false;
+    }
+  default:
+    return false;
+  }
+}
 
 bool
 nsEventStateManager::HandleCrossProcessEvent(nsEvent *aEvent,
                                              nsIFrame* aTargetFrame,
                                              nsEventStatus *aStatus) {
-
-  switch (aEvent->eventStructType) {
-    case NS_KEY_EVENT:
-    case NS_MOUSE_SCROLL_EVENT:
-      break;
-    case NS_MOUSE_EVENT:
-      if (aEvent->message == NS_MOUSE_BUTTON_DOWN ||
-          aEvent->message == NS_MOUSE_BUTTON_UP ||
-          aEvent->message == NS_MOUSE_MOVE) {
-        break;
-      }
-    default:
-      return false;
+  if (!CrossProcessSafeEvent(*aEvent)) {
+    return false;
   }
-
   nsIContent* target = mCurrentTargetContent;
   if (!target && aTargetFrame) {
     target = aTargetFrame->GetContent();
   }
 
   if (*aStatus == nsEventStatus_eConsumeNoDefault ||
       !target ||
       !IsRemoteTarget(target)) {
@@ -1733,18 +1764,17 @@ nsEventStateManager::HandleCrossProcessE
   frameLoader->GetEventMode(&eventMode);
   if (eventMode == nsIFrameLoader::EVENT_MODE_DONT_FORWARD_TO_CHILD) {
     return false;
   }
 
   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aTargetFrame);
   aEvent->refPoint = pt.ToNearestPixels(mPresContext->AppUnitsPerDevPixel());
 
-  DispatchCrossProcessEvent(aEvent, frameLoader);
-  return true;
+  return DispatchCrossProcessEvent(aEvent, frameLoader, aStatus);
 }
 
 //
 // CreateClickHoldTimer
 //
 // Fire off a timer for determining if the user wants click-hold. This timer
 // is a one-shot that will be cancelled when the user moves enough to fire
 // a drag.
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -442,17 +442,18 @@ protected:
   void DoQueryScrollTargetInfo(nsQueryContentEvent* aEvent,
                                nsIFrame* aTargetFrame);
   void DoQuerySelectedText(nsQueryContentEvent* aEvent);
 
   bool RemoteQueryContentEvent(nsEvent *aEvent);
   mozilla::dom::TabParent *GetCrossProcessTarget();
   bool IsTargetCrossProcess(nsGUIEvent *aEvent);
 
-  void DispatchCrossProcessEvent(nsEvent* aEvent, nsIFrameLoader* remote);
+  bool DispatchCrossProcessEvent(nsEvent* aEvent, nsIFrameLoader* remote,
+                                 nsEventStatus *aStatus);
   bool HandleCrossProcessEvent(nsEvent *aEvent,
                                  nsIFrame* aTargetFrame,
                                  nsEventStatus *aStatus);
 
 private:
   static inline void DoStateChange(mozilla::dom::Element* aElement,
                                    nsEventStates aState, bool aAddState);
   static inline void DoStateChange(nsIContent* aContent, nsEventStates aState,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -13,31 +13,32 @@ include protocol PRenderFrame;
 include protocol POfflineCacheUpdate;
 include protocol PIndexedDB;
 
 include "mozilla/dom/TabMessageUtils.h";
 include "gfxMatrix.h";
 include "mozilla/net/NeckoMessageUtils.h";
 include "IPC/nsGUIEventIPC.h";
 
+using IPC::URI;
 using gfxMatrix;
-using IPC::URI;
+using mozilla::WindowsHandle;
+using nscolor;
 using nsCompositionEvent;
 using nsIMEUpdatePreference;
 using nsIntSize;
+using nsKeyEvent;
+using nsMouseEvent;
+using nsMouseScrollEvent;
 using nsQueryContentEvent;
 using nsRect;
 using nsSelectionEvent;
 using nsTextEvent;
-using nsMouseEvent;
-using nsMouseScrollEvent;
-using nsKeyEvent;
+using nsTouchEvent;
 using RemoteDOMEvent;
-using mozilla::WindowsHandle;
-using nscolor;
 
 namespace mozilla {
 namespace dom {
 
 rpc protocol PBrowser
 {
     manager PContent;
 
@@ -254,16 +255,17 @@ child:
                PRInt32 aButton,
                PRInt32 aClickCount,
                PRInt32 aModifiers,
                bool aIgnoreRootScrollFrame);
 
     RealMouseEvent(nsMouseEvent event);
     RealKeyEvent(nsKeyEvent event);
     MouseScrollEvent(nsMouseScrollEvent event);
+    RealTouchEvent(nsTouchEvent event);
 
     /**
      * @see nsIDOMWindowUtils sendKeyEvent.
      */
     KeyEvent(nsString aType,
              PRInt32 aKeyCode,
              PRInt32 aCharCode,
              PRInt32 aModifiers,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -660,16 +660,61 @@ TabChild::RecvRealMouseEvent(const nsMou
 bool
 TabChild::RecvMouseScrollEvent(const nsMouseScrollEvent& event)
 {
   nsMouseScrollEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
   return true;
 }
 
+bool
+TabChild::RecvRealTouchEvent(const nsTouchEvent& aEvent)
+{
+    // FIXME/bug 774458: make this behavior comply with spec
+    nsTouchEvent localEvent(aEvent);
+    nsEventStatus status = DispatchWidgetEvent(localEvent);
+    if (status == nsEventStatus_eConsumeNoDefault) {
+        return true;
+    }
+
+    // Synthesize a phony mouse event.
+    PRUint32 msg;
+    switch (aEvent.message) {
+    case NS_TOUCH_START:
+        msg = NS_MOUSE_BUTTON_DOWN;
+        break;
+    case NS_TOUCH_MOVE:
+        msg = NS_MOUSE_MOVE;
+        break;
+    case NS_TOUCH_END:
+    case NS_TOUCH_CANCEL:
+        msg = NS_MOUSE_BUTTON_UP;
+        break;
+    default:
+        MOZ_NOT_REACHED("Unknown touch event message");
+    }
+
+    nsIntPoint refPoint(0, 0);
+    if (aEvent.touches.Length()) {
+        refPoint = aEvent.touches[0]->mRefPoint;
+    }
+
+    nsMouseEvent event(true, msg, NULL,
+                       nsMouseEvent::eReal, nsMouseEvent::eNormal);
+    event.refPoint = refPoint;
+    event.time = aEvent.time;
+    event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+    event.button = nsMouseEvent::eLeftButton;
+    if (msg != NS_MOUSE_MOVE) {
+        event.clickCount = 1;
+    }
+
+    DispatchWidgetEvent(event);
+    return true;
+}
 
 bool
 TabChild::RecvRealKeyEvent(const nsKeyEvent& event)
 {
   nsKeyEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
   return true;
 }
@@ -710,26 +755,27 @@ TabChild::RecvTextEvent(const nsTextEven
 bool
 TabChild::RecvSelectionEvent(const nsSelectionEvent& event)
 {
   nsSelectionEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
   return true;
 }
 
-bool
+nsEventStatus
 TabChild::DispatchWidgetEvent(nsGUIEvent& event)
 {
   if (!mWidget)
-    return false;
+    return nsEventStatus_eConsumeNoDefault;
 
   nsEventStatus status;
   event.widget = mWidget;
-  NS_ENSURE_SUCCESS(mWidget->DispatchEvent(&event, status), false);
-  return true;
+  NS_ENSURE_SUCCESS(mWidget->DispatchEvent(&event, status),
+                    nsEventStatus_eConsumeNoDefault);
+  return status;
 }
 
 PDocumentRendererChild*
 TabChild::AllocPDocumentRenderer(const nsRect& documentRect,
                                  const gfxMatrix& transform,
                                  const nsString& bgcolor,
                                  const PRUint32& renderFlags,
                                  const bool& flushLayout,
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -170,16 +170,17 @@ public:
                                 const float&    aY,
                                 const PRInt32&  aButton,
                                 const PRInt32&  aClickCount,
                                 const PRInt32&  aModifiers,
                                 const bool&     aIgnoreRootScrollFrame);
     virtual bool RecvRealMouseEvent(const nsMouseEvent& event);
     virtual bool RecvRealKeyEvent(const nsKeyEvent& event);
     virtual bool RecvMouseScrollEvent(const nsMouseScrollEvent& event);
+    virtual bool RecvRealTouchEvent(const nsTouchEvent& event);
     virtual bool RecvKeyEvent(const nsString& aType,
                               const PRInt32&  aKeyCode,
                               const PRInt32&  aCharCode,
                               const PRInt32&  aModifiers,
                               const bool&     aPreventDefault);
     virtual bool RecvCompositionEvent(const nsCompositionEvent& event);
     virtual bool RecvTextEvent(const nsTextEvent& event);
     virtual bool RecvSelectionEvent(const nsSelectionEvent& event);
@@ -234,17 +235,17 @@ public:
 protected:
     NS_OVERRIDE
     virtual PRenderFrameChild* AllocPRenderFrame();
     NS_OVERRIDE
     virtual bool DeallocPRenderFrame(PRenderFrameChild* aFrame);
     NS_OVERRIDE
     virtual bool RecvDestroy();
 
-    bool DispatchWidgetEvent(nsGUIEvent& event);
+    nsEventStatus DispatchWidgetEvent(nsGUIEvent& event);
 
     virtual PIndexedDBChild* AllocPIndexedDB(const nsCString& aASCIIOrigin,
                                              bool* /* aAllowed */);
 
     virtual bool DeallocPIndexedDB(PIndexedDBChild* aActor);
 
 private:
     void ActorDestroy(ActorDestroyReason why);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -302,16 +302,21 @@ bool TabParent::SendMouseScrollEvent(nsM
   return PBrowserParent::SendMouseScrollEvent(event);
 }
 
 bool TabParent::SendRealKeyEvent(nsKeyEvent& event)
 {
   return PBrowserParent::SendRealKeyEvent(event);
 }
 
+bool TabParent::SendRealTouchEvent(nsTouchEvent& event)
+{
+  return PBrowserParent::SendRealTouchEvent(event);
+}
+
 bool
 TabParent::RecvSyncMessage(const nsString& aMessage,
                            const nsString& aJSON,
                            InfallibleTArray<nsString>* aJSONRetVal)
 {
   return ReceiveMessage(aMessage, true, aJSON, aJSONRetVal);
 }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -118,16 +118,17 @@ public:
                         PRInt32 aButton, PRInt32 aClickCount,
                         PRInt32 aModifiers, bool aIgnoreRootScrollFrame);
     void SendKeyEvent(const nsAString& aType, PRInt32 aKeyCode,
                       PRInt32 aCharCode, PRInt32 aModifiers,
                       bool aPreventDefault);
     bool SendRealMouseEvent(nsMouseEvent& event);
     bool SendMouseScrollEvent(nsMouseScrollEvent& event);
     bool SendRealKeyEvent(nsKeyEvent& event);
+    bool SendRealTouchEvent(nsTouchEvent& event);
 
     virtual PDocumentRendererParent*
     AllocPDocumentRenderer(const nsRect& documentRect, const gfxMatrix& transform,
                            const nsString& bgcolor,
                            const PRUint32& renderFlags, const bool& flushLayout,
                            const nsIntSize& renderSize);
     virtual bool DeallocPDocumentRenderer(PDocumentRendererParent* actor);
 
--- a/widget/gonk/nsAppShell.cpp
+++ b/widget/gonk/nsAppShell.cpp
@@ -326,16 +326,17 @@ GeckoInputDispatcher::dispatchOnce()
         data = mEventQueue.front();
         mEventQueue.pop();
         if (!mEventQueue.empty())
             gAppShell->NotifyNativeEvent();
     }
 
     switch (data.type) {
     case UserInputData::MOTION_DATA: {
+        // FIXME/bug 774458: make this behavior comply with spec
         nsEventStatus status = sendTouchEvent(data);
         if (status == nsEventStatus_eConsumeNoDefault)
             break;
 
         PRUint32 msg;
         switch (data.action & AMOTION_EVENT_ACTION_MASK) {
         case AMOTION_EVENT_ACTION_DOWN:
             msg = NS_MOUSE_BUTTON_DOWN;
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -1618,16 +1618,19 @@ public:
   }
 
   PRUint32 streamId;
 };
 
 class nsTouchEvent : public nsInputEvent
 {
 public:
+  nsTouchEvent()
+  {
+  }
   nsTouchEvent(bool isTrusted, nsTouchEvent *aEvent)
     : nsInputEvent(isTrusted,
                    aEvent->message,
                    aEvent->widget,
                    NS_TOUCH_EVENT)
   {
     modifiers = aEvent->modifiers;
     time = aEvent->time;
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsGUIEventIPC_h__
 #define nsGUIEventIPC_h__
 
 #include "IPC/IPCMessageUtils.h"
+#include "nsDOMTouchEvent.h"
 #include "nsGUIEvent.h"
 
 namespace IPC
 {
 
 template<>
 struct ParamTraits<nsEvent>
 {
@@ -145,16 +146,65 @@ struct ParamTraits<nsMouseEvent>
     aResult->reason = static_cast<nsMouseEvent::reasonType>(reason);
     aResult->context = static_cast<nsMouseEvent::contextType>(context);
     aResult->exit = static_cast<nsMouseEvent::exitType>(exit);
     return rv;
   }
 };
 
 template<>
+struct ParamTraits<nsTouchEvent>
+{
+  typedef nsTouchEvent paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, static_cast<const nsInputEvent&>(aParam));
+    // Sigh, nsDOMTouch bites us again!  We want to be able to do
+    //   WriteParam(aMsg, aParam.touches);
+    const nsTArray<nsCOMPtr<nsIDOMTouch> >& touches = aParam.touches;
+    WriteParam(aMsg, touches.Length());
+    for (uint32_t i = 0; i < touches.Length(); ++i) {
+      nsDOMTouch* touch = static_cast<nsDOMTouch*>(touches[i].get());
+      WriteParam(aMsg, touch->mIdentifier);
+      WriteParam(aMsg, touch->mRefPoint);
+      WriteParam(aMsg, touch->mRadius);
+      WriteParam(aMsg, touch->mRotationAngle);
+      WriteParam(aMsg, touch->mForce);
+    }
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    uint32_t numTouches;
+    if (!ReadParam(aMsg, aIter, static_cast<nsInputEvent*>(aResult)) ||
+        !ReadParam(aMsg, aIter, &numTouches)) {
+      return false;
+    }
+    for (uint32_t i = 0; i < numTouches; ++i) {
+        PRInt32 identifier;
+        nsIntPoint refPoint;
+        nsIntPoint radius;
+        float rotationAngle;
+        float force;
+        if (!ReadParam(aMsg, aIter, &identifier) ||
+            !ReadParam(aMsg, aIter, &refPoint) ||
+            !ReadParam(aMsg, aIter, &radius) ||
+            !ReadParam(aMsg, aIter, &rotationAngle) ||
+            !ReadParam(aMsg, aIter, &force)) {
+          return false;
+        }
+        aResult->touches.AppendElement(
+          new nsDOMTouch(identifier, refPoint, radius, rotationAngle, force));
+    }
+    return true;
+  }
+};
+
+template<>
 struct ParamTraits<nsKeyEvent>
 {
   typedef nsKeyEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, static_cast<nsInputEvent>(aParam));
     WriteParam(aMsg, aParam.keyCode);