Bug 1303957 Part1: Add support for PointerEvent.getCoalescedEvents. r=smaug.
authorStone Shih <sshih@mozilla.com>
Wed, 20 Sep 2017 13:00:57 +0800
changeset 450995 0152c7d3651e74f78d10e98b00675075ae3ab2a3
parent 450994 80c087207c12814bbbce83abb7a4be552b406a49
child 450996 767cb7462ef858b377678af010d2111fa35deacb
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1303957
milestone59.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 1303957 Part1: Add support for PointerEvent.getCoalescedEvents. r=smaug. We implement PointerEvent.getCoalescedEvents as 1. Clone the widget events we coalesced. 2. Convert them to dom::PointerEvent when user calls getCoalescedEvents. MozReview-Commit-ID: 8IKw4PbUsDD
dom/events/PointerEvent.cpp
dom/events/PointerEvent.h
dom/ipc/CoalescedMouseData.cpp
dom/webidl/PointerEvent.webidl
testing/web-platform/meta/pointerevents/extension/idlharness.html.ini
testing/web-platform/meta/pointerevents/extension/pointerevent_constructor.html.ini
widget/MouseEvents.h
--- a/dom/events/PointerEvent.cpp
+++ b/dom/events/PointerEvent.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/.
  *
  * Portions Copyright 2013 Microsoft Open Technologies, Inc. */
 
 #include "mozilla/dom/PointerEvent.h"
+#include "mozilla/dom/PointerEventBinding.h"
 #include "mozilla/MouseEvents.h"
 #include "prtime.h"
 
 namespace mozilla {
 namespace dom {
 
 PointerEvent::PointerEvent(EventTarget* aOwner,
                            nsPresContext* aPresContext,
@@ -32,16 +33,23 @@ PointerEvent::PointerEvent(EventTarget* 
     mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
     mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
   }
   // 5.2 Pointer Event types, for all pointer events, |detail| attribute SHOULD
   // be 0.
   mDetail = 0;
 }
 
+JSObject*
+PointerEvent::WrapObjectInternal(JSContext* aCx,
+                                 JS::Handle<JSObject*> aGivenProto)
+{
+  return PointerEventBinding::Wrap(aCx, this, aGivenProto);
+}
+
 static uint16_t
 ConvertStringToPointerType(const nsAString& aPointerTypeArg)
 {
   if (aPointerTypeArg.EqualsLiteral("mouse")) {
     return nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
   }
   if (aPointerTypeArg.EqualsLiteral("pen")) {
     return nsIDOMMouseEvent::MOZ_SOURCE_PEN;
@@ -96,32 +104,51 @@ PointerEvent::Constructor(EventTarget* a
   widgetEvent->tangentialPressure = aParam.mTangentialPressure;
   widgetEvent->tiltX = aParam.mTiltX;
   widgetEvent->tiltY = aParam.mTiltY;
   widgetEvent->twist = aParam.mTwist;
   widgetEvent->inputSource = ConvertStringToPointerType(aParam.mPointerType);
   widgetEvent->mIsPrimary = aParam.mIsPrimary;
   widgetEvent->buttons = aParam.mButtons;
 
+  if (!aParam.mCoalescedEvents.IsEmpty()) {
+    e->mCoalescedEvents.AppendElements(aParam.mCoalescedEvents);
+  }
   e->SetTrusted(trusted);
   e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 // static
 already_AddRefed<PointerEvent>
 PointerEvent::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aType,
                           const PointerEventInit& aParam,
                           ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
   return Constructor(owner, aType, aParam);
 }
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(PointerEvent)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PointerEvent, MouseEvent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCoalescedEvents)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PointerEvent, MouseEvent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCoalescedEvents)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PointerEvent)
+NS_INTERFACE_MAP_END_INHERITING(MouseEvent)
+
+NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent)
+NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent)
+
 void
 PointerEvent::GetPointerType(nsAString& aPointerType)
 {
   ConvertPointerTypeToString(mEvent->AsPointerEvent()->inputSource, aPointerType);
 }
 
 int32_t
 PointerEvent::PointerId()
@@ -172,16 +199,50 @@ PointerEvent::Twist()
 }
 
 bool
 PointerEvent::IsPrimary()
 {
   return mEvent->AsPointerEvent()->mIsPrimary;
 }
 
+void
+PointerEvent::GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents)
+{
+  WidgetPointerEvent* widgetEvent = mEvent->AsPointerEvent();
+  if (mCoalescedEvents.IsEmpty() && widgetEvent &&
+      widgetEvent->mCoalescedWidgetEvents &&
+      !widgetEvent->mCoalescedWidgetEvents->mEvents.IsEmpty()) {
+    for (WidgetPointerEvent& event :
+         widgetEvent->mCoalescedWidgetEvents->mEvents) {
+      RefPtr<PointerEvent> domEvent =
+        NS_NewDOMPointerEvent(nullptr, nullptr, &event);
+
+      // The coalesced widget mouse events shouldn't have been dispatched.
+      MOZ_ASSERT(!domEvent->mEvent->mTarget);
+      // The event target should be the same as the dispatched event's target.
+      domEvent->mEvent->mTarget = mEvent->mTarget;
+
+      // JS could hold reference to dom events. We have to ask dom event to
+      // duplicate its private data to avoid the widget event is destroyed.
+      domEvent->DuplicatePrivateData();
+      mCoalescedEvents.AppendElement(domEvent);
+    }
+  }
+  if (mEvent->mTarget) {
+    for (RefPtr<PointerEvent>& pointerEvent : mCoalescedEvents) {
+      // Only set event target when it's null.
+      if (!pointerEvent->mEvent->mTarget) {
+        pointerEvent->mEvent->mTarget = mEvent->mTarget;
+      }
+    }
+  }
+  aPointerEvents.AppendElements(mCoalescedEvents);
+}
+
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 already_AddRefed<PointerEvent>
 NS_NewDOMPointerEvent(EventTarget* aOwner,
--- a/dom/events/PointerEvent.h
+++ b/dom/events/PointerEvent.h
@@ -12,27 +12,31 @@
 #include "mozilla/dom/MouseEvent.h"
 #include "mozilla/dom/PointerEventBinding.h"
 
 class nsPresContext;
 
 namespace mozilla {
 namespace dom {
 
+struct PointerEventInit;
+
 class PointerEvent : public MouseEvent
 {
 public:
   PointerEvent(EventTarget* aOwner,
                nsPresContext* aPresContext,
                WidgetPointerEvent* aEvent);
 
-  virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
-  {
-    return PointerEventBinding::Wrap(aCx, this, aGivenProto);
-  }
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PointerEvent, MouseEvent)
+
+  virtual JSObject* WrapObjectInternal(
+                      JSContext* aCx,
+                      JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<PointerEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const PointerEventInit& aParam,
               ErrorResult& aRv);
 
   static already_AddRefed<PointerEvent>
@@ -45,16 +49,23 @@ public:
   int32_t Height();
   float Pressure();
   float TangentialPressure();
   int32_t TiltX();
   int32_t TiltY();
   int32_t Twist();
   bool IsPrimary();
   void GetPointerType(nsAString& aPointerType);
+  void GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents);
+
+protected:
+  ~PointerEvent() {}
+
+private:
+  nsTArray<RefPtr<PointerEvent>> mCoalescedEvents;
 };
 
 void ConvertPointerTypeToString(uint16_t aPointerTypeSrc, nsAString& aPointerTypeDest);
 
 } // namespace dom
 } // namespace mozilla
 
 already_AddRefed<mozilla::dom::PointerEvent>
--- a/dom/ipc/CoalescedMouseData.cpp
+++ b/dom/ipc/CoalescedMouseData.cpp
@@ -15,36 +15,54 @@ void
 CoalescedMouseData::Coalesce(const WidgetMouseEvent& aEvent,
                              const ScrollableLayerGuid& aGuid,
                              const uint64_t& aInputBlockId)
 {
   if (IsEmpty()) {
     mCoalescedInputEvent = MakeUnique<WidgetMouseEvent>(aEvent);
     mGuid = aGuid;
     mInputBlockId = aInputBlockId;
+    MOZ_ASSERT(!mCoalescedInputEvent->mCoalescedWidgetEvents);
   } else {
     MOZ_ASSERT(mGuid == aGuid);
     MOZ_ASSERT(mInputBlockId == aInputBlockId);
     MOZ_ASSERT(mCoalescedInputEvent->mModifiers == aEvent.mModifiers);
     MOZ_ASSERT(mCoalescedInputEvent->mReason == aEvent.mReason);
     MOZ_ASSERT(mCoalescedInputEvent->inputSource == aEvent.inputSource);
     MOZ_ASSERT(mCoalescedInputEvent->button == aEvent.button);
     MOZ_ASSERT(mCoalescedInputEvent->buttons == aEvent.buttons);
     mCoalescedInputEvent->mTimeStamp = aEvent.mTimeStamp;
     mCoalescedInputEvent->mRefPoint = aEvent.mRefPoint;
     mCoalescedInputEvent->pressure = aEvent.pressure;
     mCoalescedInputEvent->AssignPointerHelperData(aEvent);
   }
+
+  if (aEvent.mMessage == eMouseMove &&
+      PointerEventHandler::IsPointerEventEnabled()) {
+    // PointerEvent::getCoalescedEvents is only applied to pointermove events.
+    if (!mCoalescedInputEvent->mCoalescedWidgetEvents) {
+      mCoalescedInputEvent->mCoalescedWidgetEvents =
+        new WidgetPointerEventHolder();
+    }
+    // Append current event in mCoalescedWidgetEvents. We use them to generate
+    // DOM events when content calls PointerEvent::getCoalescedEvents.
+    WidgetPointerEvent* event = mCoalescedInputEvent->mCoalescedWidgetEvents
+                                  ->mEvents.AppendElement(aEvent);
+
+    event->mFlags.mBubbles = false;
+    event->mFlags.mCancelable = false;
+  }
 }
 
 bool
 CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent,
                              const ScrollableLayerGuid& aGuid,
                              const uint64_t& aInputBlockId)
 {
+  MOZ_ASSERT(aEvent.mMessage == eMouseMove);
   return !mCoalescedInputEvent ||
          (mCoalescedInputEvent->mModifiers == aEvent.mModifiers &&
           mCoalescedInputEvent->inputSource == aEvent.inputSource &&
           mCoalescedInputEvent->pointerId == aEvent.pointerId &&
           mCoalescedInputEvent->button == aEvent.button &&
           mCoalescedInputEvent->buttons == aEvent.buttons &&
           mGuid == aGuid &&
           mInputBlockId == aInputBlockId);
--- a/dom/webidl/PointerEvent.webidl
+++ b/dom/webidl/PointerEvent.webidl
@@ -18,24 +18,25 @@ interface PointerEvent : MouseEvent
   readonly attribute long height;
   readonly attribute float pressure;
   readonly attribute float tangentialPressure;
   readonly attribute long tiltX;
   readonly attribute long tiltY;
   readonly attribute long twist;
   readonly attribute DOMString pointerType;
   readonly attribute boolean isPrimary;
+  sequence<PointerEvent> getCoalescedEvents();
 };
 
 dictionary PointerEventInit : MouseEventInit
 {
   long pointerId = 0;
   long width = 1;
   long height = 1;
   float pressure = 0;
   float tangentialPressure = 0;
   long tiltX = 0;
   long tiltY = 0;
   long twist = 0;
   DOMString pointerType = "";
   boolean isPrimary = false;
+  sequence<PointerEvent> coalescedEvents = [];
 };
-
--- a/testing/web-platform/meta/pointerevents/extension/idlharness.html.ini
+++ b/testing/web-platform/meta/pointerevents/extension/idlharness.html.ini
@@ -1,6 +1,3 @@
 [idlharness.html]
   type: testharness
   prefs: [dom.w3c_pointer_events.enabled:true]
-  [PointerEvent interface: operation getCoalescedEvents()]
-    expected: FAIL
-
--- a/testing/web-platform/meta/pointerevents/extension/pointerevent_constructor.html.ini
+++ b/testing/web-platform/meta/pointerevents/extension/pointerevent_constructor.html.ini
@@ -1,5 +1,4 @@
 [pointerevent_constructor.html]
   type: testharness
-  [PointerEvent: Constructor test]
-    expected: FAIL
+  prefs: [dom.w3c_pointer_events.enabled:true]
 
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -31,29 +31,41 @@ enum nsDragDropEventStatus
 
 namespace mozilla {
 
 namespace dom {
   class PBrowserParent;
   class PBrowserChild;
 } // namespace dom
 
+class WidgetPointerEvent;
+class WidgetPointerEventHolder final
+{
+public:
+  nsTArray<WidgetPointerEvent> mEvents;
+  NS_INLINE_DECL_REFCOUNTING(WidgetPointerEventHolder)
+
+private:
+  virtual ~WidgetPointerEventHolder() {}
+};
+
 /******************************************************************************
  * mozilla::WidgetPointerHelper
  ******************************************************************************/
 
 class WidgetPointerHelper
 {
 public:
   uint32_t pointerId;
   uint32_t tiltX;
   uint32_t tiltY;
   uint32_t twist;
   float tangentialPressure;
   bool convertToPointer;
+  RefPtr<WidgetPointerEventHolder> mCoalescedWidgetEvents;
 
   WidgetPointerHelper()
     : pointerId(0)
     , tiltX(0)
     , tiltY(0)
     , twist(0)
     , tangentialPressure(0)
     , convertToPointer(true)
@@ -66,24 +78,39 @@ public:
     , tiltX(aTiltX)
     , tiltY(aTiltY)
     , twist(aTwist)
     , tangentialPressure(aTangentialPressure)
     , convertToPointer(true)
   {
   }
 
-  void AssignPointerHelperData(const WidgetPointerHelper& aEvent)
+  explicit WidgetPointerHelper(const WidgetPointerHelper& aHelper)
+    : pointerId(aHelper.pointerId)
+    , tiltX(aHelper.tiltX)
+    , tiltY(aHelper.tiltY)
+    , twist(aHelper.twist)
+    , tangentialPressure(aHelper.tangentialPressure)
+    , convertToPointer(aHelper.convertToPointer)
+    , mCoalescedWidgetEvents(aHelper.mCoalescedWidgetEvents)
+  {
+  }
+
+  void AssignPointerHelperData(const WidgetPointerHelper& aEvent,
+                               bool aCopyCoalescedEvents = false)
   {
     pointerId = aEvent.pointerId;
     tiltX = aEvent.tiltX;
     tiltY = aEvent.tiltY;
     twist = aEvent.twist;
     tangentialPressure = aEvent.tangentialPressure;
     convertToPointer = aEvent.convertToPointer;
+    if (aCopyCoalescedEvents) {
+      mCoalescedWidgetEvents = aEvent.mCoalescedWidgetEvents;
+    }
   }
 };
 
 /******************************************************************************
  * mozilla::WidgetMouseEventBase
  ******************************************************************************/
 
 class WidgetMouseEventBase : public WidgetInputEvent
@@ -313,17 +340,17 @@ public:
   // mClickCount may be non-zero value when mMessage is eMouseDown, eMouseUp,
   // eMouseClick or eMouseDoubleClick. The number is count of mouse clicks.
   // Otherwise, this must be 0.
   uint32_t mClickCount;
 
   void AssignMouseEventData(const WidgetMouseEvent& aEvent, bool aCopyTargets)
   {
     AssignMouseEventBaseData(aEvent, aCopyTargets);
-    AssignPointerHelperData(aEvent);
+    AssignPointerHelperData(aEvent, /* aCopyCoalescedEvents */ true);
 
     mIgnoreRootScrollFrame = aEvent.mIgnoreRootScrollFrame;
     mClickCount = aEvent.mClickCount;
   }
 
   /**
    * Returns true if the event is a context menu event caused by key.
    */