Bug 1419834 - Add hit-testing results for MozMouseHittest events to APZTestData. r?botond,mrbkap draft
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 29 Nov 2017 23:16:26 -0500
changeset 705557 53f6a135dd94c08dc08f4f0c35ba0686633a0f5e
parent 705556 372df5ff4c4af48848125fb3e105ca7e8b60a327
child 705558 9203ffbd0ad154aa84529a1e4a48a047494dd8a7
push id91520
push userkgupta@mozilla.com
push dateThu, 30 Nov 2017 11:03:41 +0000
reviewersbotond, mrbkap
bugs1419834
milestone59.0a1
Bug 1419834 - Add hit-testing results for MozMouseHittest events to APZTestData. r?botond,mrbkap This allows us to fire MozMouseHittest events from tests and then read the hittest result from the compositor APZTestData. The MozMouseHittest event was chosen in particular because the existing uses of it are similar in nature - it is a dummy event that is used to determine what elements a particular coordinate targets. It is also an event that is never generated by the OS and so using this event gives us more control over what ends up in the APZTestData. MozReview-Commit-ID: KHjIX7EpK2A
dom/webidl/APZTestData.webidl
gfx/ipc/GfxMessageUtils.h
gfx/layers/apz/public/IAPZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/testutil/APZTestData.cpp
gfx/layers/apz/testutil/APZTestData.h
gfx/src/CompositorHitTestInfo.h
widget/InputData.cpp
widget/InputData.h
--- a/dom/webidl/APZTestData.webidl
+++ b/dom/webidl/APZTestData.webidl
@@ -25,20 +25,43 @@ dictionary ScrollFrameData {
 
 // All the scrollable frames associated with a given paint or repaint request.
 // The paint or repaint request is identified by a sequence number.
 dictionary APZBucket {
   unsigned long sequenceNumber;
   sequence<ScrollFrameData> scrollFrames;
 };
 
+[Pref="apz.test.logging_enabled"]
+callback interface APZHitResultFlags {
+  // These constants should be kept in sync with mozilla::gfx::CompositorHitTestInfo
+  const unsigned short INVISIBLE = 0;
+  const unsigned short VISIBLE = 0x0001;
+  const unsigned short DISPATCH_TO_CONTENT = 0x0002;
+  const unsigned short PAN_X_DISABLED = 0x0004;
+  const unsigned short PAN_Y_DISABLED = 0x0008;
+  const unsigned short PINCH_ZOOM_DISABLED = 0x0010;
+  const unsigned short DOUBLE_TAP_ZOOM_DISABLED = 0x0020;
+  const unsigned short SCROLLBAR = 0x0040;
+  const unsigned short SCROLLBAR_THUMB = 0x0080;
+  const unsigned short SCROLLBAR_VERTICAL = 0x0100;
+};
+
+dictionary APZHitResult {
+  float screenX;
+  float screenY;
+  unsigned short hitResult; // combination of the APZHitResultFlags.* flags
+  unsigned long long scrollId;
+};
+
 // All the paints and repaint requests. This is the top-level data structure.
 dictionary APZTestData {
   sequence<APZBucket> paints;
   sequence<APZBucket> repaintRequests;
+  sequence<APZHitResult> hitResults;
 };
 
 // A frame uniformity measurement for every scrollable layer
 dictionary FrameUniformity {
   unsigned long layerAddress;
   float frameUniformity;
 };
 
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -15,16 +15,17 @@
 #include "chrome/common/ipc_message_utils.h"
 #include "gfxFeature.h"
 #include "gfxFallback.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "gfxTelemetry.h"
 #include "gfxTypes.h"
 #include "ipc/IPCMessageUtils.h"
+#include "mozilla/gfx/CompositorHitTestInfo.h"
 #include "mozilla/gfx/Matrix.h"
 #include "nsRect.h"
 #include "nsRegion.h"
 #include "mozilla/Array.h"
 
 #include <stdint.h>
 
 #ifdef _MSC_VER
@@ -917,12 +918,18 @@ struct ParamTraits<mozilla::Array<T, Len
       if (!ReadParam<T>(aMsg, aIter, &aResult->operator[](i))) {
         return false;
       }
     }
     return true;
   }
 };
 
+template <>
+struct ParamTraits<mozilla::gfx::CompositorHitTestInfo>
+  : public BitFlagsEnumSerializer<mozilla::gfx::CompositorHitTestInfo,
+                                  mozilla::gfx::CompositorHitTestInfo::ALL_BITS>
+{
+};
 
 } /* namespace IPC */
 
 #endif /* __GFXMESSAGEUTILS_H__ */
--- a/gfx/layers/apz/public/IAPZCTreeManager.cpp
+++ b/gfx/layers/apz/public/IAPZCTreeManager.cpp
@@ -19,17 +19,18 @@ namespace mozilla {
 namespace layers {
 
 static bool
 WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
 {
   return aEvent.mMessage == eMouseMove ||
          aEvent.mMessage == eMouseDown ||
          aEvent.mMessage == eMouseUp ||
-         aEvent.mMessage == eDragEnd;
+         aEvent.mMessage == eDragEnd ||
+         (gfxPrefs::TestEventsAsyncEnabled() && aEvent.mMessage == eMouseHitTest);
 }
 
 /* static */ bool
 IAPZCTreeManager::WillHandleWheelEvent(WidgetWheelEvent* aEvent)
 {
   return EventStateManager::WheelEventIsScrollAction(aEvent) &&
          (aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE ||
           aEvent->mDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL ||
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1053,16 +1053,23 @@ APZCTreeManager::ReceiveInputEvent(Input
       { // scope lock
         MutexAutoLock lock(mTreeLock);
         if (!apzc && mRootNode) {
           apzc = mRootNode->GetApzc();
         }
       }
 
       if (apzc) {
+        if (gfxPrefs::APZTestLoggingEnabled() && mouseInput.mType == MouseInput::MOUSE_HITTEST) {
+          ScrollableLayerGuid guid = apzc->GetGuid();
+          if (LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(guid.mLayersId)) {
+            state->mApzTestData.RecordHitResult(mouseInput.mOrigin, hitResult, guid.mScrollId);
+          }
+        }
+
         bool targetConfirmed = (hitResult != CompositorHitTestInfo::eInvisibleToHitTest)
                             && !(hitResult & CompositorHitTestInfo::eDispatchToContent);
         bool apzDragEnabled = gfxPrefs::APZDragEnabled();
         if (apzDragEnabled && hitScrollbar) {
           // If scrollbar dragging is enabled and we hit a scrollbar, wait
           // for the main-thread confirmation because it contains drag metrics
           // that we need.
           targetConfirmed = false;
--- a/gfx/layers/apz/testutil/APZTestData.cpp
+++ b/gfx/layers/apz/testutil/APZTestData.cpp
@@ -18,20 +18,31 @@ struct APZTestDataToJSConverter {
                                dom::Sequence<KeyValuePair>& aOutTo,
                                void (*aElementConverter)(const Key&, const Value&, KeyValuePair&)) {
     for (auto it = aFrom.begin(); it != aFrom.end(); ++it) {
       aOutTo.AppendElement(fallible);
       aElementConverter(it->first, it->second, aOutTo.LastElement());
     }
   }
 
+  template <typename Src, typename Target>
+  static void ConvertList(const nsTArray<Src>& aFrom,
+                          dom::Sequence<Target>& aOutTo,
+                          void (*aElementConverter)(const Src&, Target&)) {
+    for (auto it = aFrom.begin(); it != aFrom.end(); ++it) {
+      aOutTo.AppendElement(fallible);
+      aElementConverter(*it, aOutTo.LastElement());
+    }
+  }
+
   static void ConvertAPZTestData(const APZTestData& aFrom,
                                  dom::APZTestData& aOutTo) {
     ConvertMap(aFrom.mPaints, aOutTo.mPaints.Construct(), ConvertBucket);
     ConvertMap(aFrom.mRepaintRequests, aOutTo.mRepaintRequests.Construct(), ConvertBucket);
+    ConvertList(aFrom.mHitResults, aOutTo.mHitResults.Construct(), ConvertHitResult);
   }
 
   static void ConvertBucket(const SequenceNumber& aKey,
                             const APZTestData::Bucket& aValue,
                             dom::APZBucket& aOutKeyValuePair) {
     aOutKeyValuePair.mSequenceNumber.Construct() = aKey;
     ConvertMap(aValue, aOutKeyValuePair.mScrollFrames.Construct(), ConvertScrollFrameData);
   }
@@ -48,16 +59,26 @@ struct APZTestDataToJSConverter {
                            dom::ScrollFrameDataEntry& aOutKeyValuePair) {
     ConvertString(aKey, aOutKeyValuePair.mKey.Construct());
     ConvertString(aValue, aOutKeyValuePair.mValue.Construct());
   }
 
   static void ConvertString(const std::string& aFrom, nsString& aOutTo) {
     aOutTo = NS_ConvertUTF8toUTF16(aFrom.c_str(), aFrom.size());
   }
+
+  static void ConvertHitResult(const APZTestData::HitResult& aResult,
+                               dom::APZHitResult& aOutHitResult) {
+    aOutHitResult.mScreenX.Construct() = aResult.point.x;
+    aOutHitResult.mScreenY.Construct() = aResult.point.y;
+    static_assert(sizeof(aResult.result) == sizeof(uint16_t),
+        "Expected CompositorHitTestInfo to be 16-bit");
+    aOutHitResult.mHitResult.Construct() = static_cast<uint16_t>(aResult.result);
+    aOutHitResult.mScrollId.Construct() = aResult.scrollId;
+  }
 };
 
 bool
 APZTestData::ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext) const
 {
   dom::APZTestData result;
   APZTestDataToJSConverter::ConvertAPZTestData(*this, result);
   return dom::ToJSValue(aContext, result, aOutValue);
--- a/gfx/layers/apz/testutil/APZTestData.h
+++ b/gfx/layers/apz/testutil/APZTestData.h
@@ -7,37 +7,42 @@
 #ifndef mozilla_layers_APZTestData_h
 #define mozilla_layers_APZTestData_h
 
 #include <map>
 
 #include "gfxPrefs.h"
 #include "FrameMetrics.h"
 #include "nsDebug.h"             // for NS_WARNING
+#include "nsTArray.h"
 #include "mozilla/Assertions.h"  // for MOZ_ASSERT
 #include "mozilla/DebugOnly.h"   // for DebugOnly
 #include "mozilla/ToString.h"    // for ToString
+#include "mozilla/gfx/CompositorHitTestInfo.h"
 #include "ipc/IPCMessageUtils.h"
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace layers {
 
 typedef uint32_t SequenceNumber;
 
 /**
  * This structure is used to store information logged by various gecko
  * components for later examination by test code.
- * It consists of a bucket for every paint (initiated on the client side),
+ * It contains a bucket for every paint (initiated on the client side),
  * and every repaint request (initiated on the compositor side by
  * AsyncPanZoomController::RequestContentRepait), which are identified by
  * sequence numbers, and within that, a set of arbitrary string key/value
  * pairs for every scrollable frame, identified by a scroll id.
  * There are two instances of this data structure for every content thread:
  * one on the client side and one of the compositor side.
+ * It also contains a list of hit-test results for MozMouseHittest events
+ * dispatched during testing. This list is only populated on the compositor
+ * instance of this class.
  */
 // TODO(botond):
 //  - Improve warnings/asserts.
 //  - Add ability to associate a repaint request triggered during a layers update
 //    with the sequence number of the paint that caused the layers update.
 class APZTestData {
   typedef FrameMetrics::ViewID ViewID;
   friend struct IPC::ParamTraits<APZTestData>;
@@ -61,31 +66,43 @@ public:
     MOZ_ASSERT(((InsertResultT&)insertResult).second, "Already have a repaint request with this sequence number");
   }
   void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
                                     ViewID aScrollId,
                                     const std::string& aKey,
                                     const std::string& aValue) {
     LogTestDataImpl(mRepaintRequests, aSequenceNumber, aScrollId, aKey, aValue);
   }
+  void RecordHitResult(const ScreenPoint& aPoint,
+                       const mozilla::gfx::CompositorHitTestInfo& aResult,
+                       const ViewID& aScrollId)
+  {
+    mHitResults.AppendElement(HitResult { aPoint, aResult, aScrollId });
+  }
 
   // Convert this object to a JS representation.
   bool ToJS(JS::MutableHandleValue aOutValue, JSContext* aContext) const;
 
   // Use dummy derived structures wrapping the tyepdefs to work around a type
   // name length limit in MSVC.
   typedef std::map<std::string, std::string> ScrollFrameDataBase;
   struct ScrollFrameData : ScrollFrameDataBase {};
   typedef std::map<ViewID, ScrollFrameData> BucketBase;
   struct Bucket : BucketBase {};
   typedef std::map<SequenceNumber, Bucket> DataStoreBase;
   struct DataStore : DataStoreBase {};
+  struct HitResult {
+    ScreenPoint point;
+    mozilla::gfx::CompositorHitTestInfo result;
+    ViewID scrollId;
+  };
 private:
   DataStore mPaints;
   DataStore mRepaintRequests;
+  nsTArray<HitResult> mHitResults;
 
   void LogTestDataImpl(DataStore& aDataStore,
                        SequenceNumber aSequenceNumber,
                        ViewID aScrollId,
                        const std::string& aKey,
                        const std::string& aValue) {
     auto bucketIterator = aDataStore.find(aSequenceNumber);
     if (bucketIterator == aDataStore.end()) {
@@ -139,33 +156,55 @@ template <>
 struct ParamTraits<mozilla::layers::APZTestData>
 {
   typedef mozilla::layers::APZTestData paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mPaints);
     WriteParam(aMsg, aParam.mRepaintRequests);
+    WriteParam(aMsg, aParam.mHitResults);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     return (ReadParam(aMsg, aIter, &aResult->mPaints) &&
-            ReadParam(aMsg, aIter, &aResult->mRepaintRequests));
+            ReadParam(aMsg, aIter, &aResult->mRepaintRequests) &&
+            ReadParam(aMsg, aIter, &aResult->mHitResults));
   }
 };
 
 template <>
 struct ParamTraits<mozilla::layers::APZTestData::ScrollFrameData>
   : ParamTraits<mozilla::layers::APZTestData::ScrollFrameDataBase> {};
 
 template <>
 struct ParamTraits<mozilla::layers::APZTestData::Bucket>
   : ParamTraits<mozilla::layers::APZTestData::BucketBase> {};
 
 template <>
 struct ParamTraits<mozilla::layers::APZTestData::DataStore>
   : ParamTraits<mozilla::layers::APZTestData::DataStoreBase> {};
 
+template <>
+struct ParamTraits<mozilla::layers::APZTestData::HitResult>
+{
+  typedef mozilla::layers::APZTestData::HitResult paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.point);
+    WriteParam(aMsg, aParam.result);
+    WriteParam(aMsg, aParam.scrollId);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return (ReadParam(aMsg, aIter, &aResult->point) &&
+            ReadParam(aMsg, aIter, &aResult->result) &&
+            ReadParam(aMsg, aIter, &aResult->scrollId));
+  }
+};
+
 } // namespace IPC
 
 
 #endif /* mozilla_layers_APZTestData_h */
--- a/gfx/src/CompositorHitTestInfo.h
+++ b/gfx/src/CompositorHitTestInfo.h
@@ -40,16 +40,20 @@ enum class CompositorHitTestInfo : uint1
   eScrollbar = 1 << 6,
   // The frame is a scrollthumb. If this is set then eScrollbar will also be
   // set, unless gecko somehow generates a scroll thumb without a containing
   // scrollbar.
   eScrollbarThumb = 1 << 7,
   // If eScrollbar is set, this flag indicates if the scrollbar is a vertical
   // one (if set) or a horizontal one (if not set)
   eScrollbarVertical = 1 << 8,
+
+  // Used for IPDL serialization. This bitmask should include all the bits
+  // that are defined in the enum.
+  ALL_BITS = (1 << 9) - 1,
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CompositorHitTestInfo)
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_COMPOSITORHITTESTINFO_H_ */
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -374,16 +374,19 @@ MouseInput::MouseInput(const WidgetMouse
       mType = MOUSE_DRAG_END;
       break;
     case eMouseEnterIntoWidget:
       mType = MOUSE_WIDGET_ENTER;
       break;
     case eMouseExitFromWidget:
       mType = MOUSE_WIDGET_EXIT;
       break;
+    case eMouseHitTest:
+      mType = MOUSE_HITTEST;
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Mouse event type not supported");
       break;
   }
 
   mOrigin =
     ScreenPoint(ViewAs<ScreenPixel>(aMouseEvent.mRefPoint,
       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
@@ -434,16 +437,19 @@ MouseInput::ToWidgetMouseEvent(nsIWidget
       msg = eDragEnd;
       break;
     case MOUSE_WIDGET_ENTER:
       msg = eMouseEnterIntoWidget;
       break;
     case MOUSE_WIDGET_EXIT:
       msg = eMouseExitFromWidget;
       break;
+    case MOUSE_HITTEST:
+      msg = eMouseHitTest;
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Did not assign a type to WidgetMouseEvent in MouseInput");
       break;
   }
 
   WidgetMouseEvent event(true, msg, aWidget, WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
 
   if (msg == eVoidEvent) {
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -239,17 +239,18 @@ public:
     MouseType, (
       MOUSE_NONE,
       MOUSE_MOVE,
       MOUSE_DOWN,
       MOUSE_UP,
       MOUSE_DRAG_START,
       MOUSE_DRAG_END,
       MOUSE_WIDGET_ENTER,
-      MOUSE_WIDGET_EXIT
+      MOUSE_WIDGET_EXIT,
+      MOUSE_HITTEST
   ));
 
   MOZ_DEFINE_ENUM_AT_CLASS_SCOPE(
     ButtonType, (
       LEFT_BUTTON,
       MIDDLE_BUTTON,
       RIGHT_BUTTON,
       NONE