Bug 1434243 - Part 2: Add nsDisplayCompositorHitTestInfo support to FrameLayerBuilder draft
authorMiko Mynttinen <mikokm@gmail.com>
Tue, 30 Jan 2018 15:10:23 +0100
changeset 752754 ca144cd07a8cc6abd92f90e6bfb43dba0fd3ae92
parent 752753 83239d7127e35316d0b2b4f2ec319bd350a60fcd
child 752755 0c5af2e50a4de7da5d11c5923d9a51674da95637
push id98365
push userbmo:mikokm@gmail.com
push dateThu, 08 Feb 2018 21:38:37 +0000
bugs1434243
milestone60.0a1
Bug 1434243 - Part 2: Add nsDisplayCompositorHitTestInfo support to FrameLayerBuilder MozReview-Commit-ID: 5RdFJDyB6RN
gfx/thebes/gfxPrefs.h
layout/painting/FrameLayerBuilder.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -667,16 +667,17 @@ private:
   DECL_GFX_PREF(Live, "layout.display-list.retain.verify",     LayoutVerifyRetainDisplayList, bool, false);
   DECL_GFX_PREF(Live, "layout.display-list.retain.verify.order", LayoutVerifyRetainDisplayListOrder, bool, false);
   DECL_GFX_PREF(Live, "layout.display-list.rebuild-frame-limit", LayoutRebuildFrameLimit, uint32_t, 500);
   DECL_GFX_PREF(Live, "layout.display-list.dump",              LayoutDumpDisplayList, bool, false);
   DECL_GFX_PREF(Live, "layout.display-list.dump-content",      LayoutDumpDisplayListContent, bool, false);
   DECL_GFX_PREF(Live, "layout.display-list.dump-parent",       LayoutDumpDisplayListParent, bool, false);
   DECL_GFX_PREF(Live, "layout.display-list.show-rebuild-area", LayoutDisplayListShowArea, bool, false);
 
+  DECL_GFX_PREF(Once, "layout.simple-event-region-items",      SimpleEventRegionItems, bool, true);
   DECL_GFX_PREF(Live, "layout.event-regions.enabled",          LayoutEventRegionsEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
   DECL_GFX_PREF(Live, "layout.min-active-layer-size",          LayoutMinActiveLayerSize, int, 64);
   DECL_GFX_PREF(Once, "layout.paint_rects_separately",         LayoutPaintRectsSeparately, bool, true);
 
   // This and code dependent on it should be removed once containerless scrolling looks stable.
   DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers",   LayoutUseContainersForRootFrames, bool, true);
   // This pref is to be set by test code only.
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -483,20 +483,27 @@ public:
   AnimatedGeometryRoot* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; }
 
   /**
    * A region including the horizontal pan, vertical pan, and no action regions.
    */
   nsRegion CombinedTouchActionRegion();
 
   /**
-   * Add the given hit regions to the hit regions to the hit retions for this
-   * PaintedLayer.
+   * Add the given hit regions to the hit regions for this PaintedLayer.
    */
-  void AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions);
+  void AccumulateEventRegions(ContainerState* aState,
+                              nsDisplayLayerEventRegions* aEventRegions);
+
+  /**
+   * Similar to AccumulateEventRegions() but uses different display item to
+   * track hit regions.
+   */
+  void AccumulateHitTestInfo(ContainerState* aState,
+                             nsDisplayCompositorHitTestInfo* aItem);
 
   /**
    * If this represents only a nsDisplayImage, and the image type supports being
    * optimized to an ImageLayer, returns true.
    */
   bool CanOptimizeToImageLayer(nsDisplayListBuilder* aBuilder);
 
   /**
@@ -3140,16 +3147,17 @@ void ContainerState::FinishPaintedLayerD
 
     NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, paintedLayer) < 0,
                  "Layer already in list???");
     mNewChildLayers[data->mNewChildLayersIndex].mLayer = paintedLayer.forget();
   }
 
   for (auto& item : data->mAssignedDisplayItems) {
     MOZ_ASSERT(item.mItem->GetType() != DisplayItemType::TYPE_LAYER_EVENT_REGIONS);
+    MOZ_ASSERT(item.mItem->GetType() != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO);
 
     DisplayItemData* oldData =
       mLayerBuilder->GetOldLayerForFrame(item.mItem->Frame(), item.mItem->GetPerFrameKey());
     InvalidateForLayerChange(item.mItem, data->mLayer, oldData);
     mLayerBuilder->AddPaintedDisplayItem(data, item.mItem, item.mClip,
                                          *this, item.mLayerState,
                                          data->mAnimatedGeometryRootOffset,
                                          oldData);
@@ -3597,16 +3605,118 @@ PaintedLayerData::AccumulateEventRegions
 
   // Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion
   // for quick access in FindPaintedLayerFor().
   mScaledHitRegionBounds = aState->ScaleToOutsidePixels(mHitRegion.GetBounds());
   mScaledMaybeHitRegionBounds = aState->ScaleToOutsidePixels(mMaybeHitRegion.GetBounds());
 }
 
 void
+PaintedLayerData::AccumulateHitTestInfo(ContainerState* aState,
+                                        nsDisplayCompositorHitTestInfo* aItem)
+{
+  FLB_LOG_PAINTED_LAYER_DECISION(this,
+    "Accumulating hit test info %p against pld=%p\n", aItem, this);
+
+  const mozilla::DisplayItemClip& clip = aItem->GetClip();
+  const nsRect area = clip.ApplyNonRoundedIntersection(aItem->Area());
+  const mozilla::gfx::CompositorHitTestInfo hitTestInfo = aItem->HitTestInfo();
+
+  bool hasRoundedCorners = clip.GetRoundedRectCount() > 0;
+
+  // use the NS_FRAME_SIMPLE_EVENT_REGIONS to avoid calling the slightly
+  // expensive HasNonZeroCorner function if we know from a previous run that
+  // the frame has zero corners.
+  nsIFrame* frame = aItem->Frame();
+
+  bool simpleRegions = frame->HasAnyStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
+  if (!simpleRegions) {
+    if (nsLayoutUtils::HasNonZeroCorner(frame->StyleBorder()->mBorderRadius)) {
+      hasRoundedCorners = true;
+    } else {
+      frame->AddStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
+    }
+  }
+
+  if (hasRoundedCorners || (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
+    mMaybeHitRegion.OrWith(area);
+  } else {
+    mHitRegion.OrWith(area);
+  }
+
+  if (aItem->HitTestInfo() & CompositorHitTestInfo::eDispatchToContent) {
+    mDispatchToContentHitRegion.OrWith(area);
+  }
+
+  auto touchFlags = hitTestInfo & CompositorHitTestInfo::eTouchActionMask;
+  if (touchFlags) {
+    // something was disabled
+    if (touchFlags == CompositorHitTestInfo::eTouchActionMask) {
+      // everything was disabled, so touch-action:none
+      mNoActionRegion.OrWith(area);
+    } else {
+      // The event regions code does not store enough information to actually
+      // represent all the different states. Prior to the introduction of
+      // CompositorHitTestInfo here in bug 1389149, the following two cases
+      // were effectively getting collapsed:
+      //   (1) touch-action: auto
+      //   (2) touch-action: manipulation
+      // In both of these cases, none of {mNoActionRegion, mHorizontalPanRegion,
+      // mVerticalPanRegion} were modified, and so the fact that case (2) should
+      // have prevented double-tap-zooming was getting lost.
+      // With CompositorHitTestInfo we can now represent that case correctly,
+      // but only if we use CompositorHitTestInfo all the way to the compositor
+      // (i.e. in the WebRender-enabled case). In the non-WebRender case where
+      // we still use the event regions, we must collapse these two cases back
+      // together. Or add another region to the event regions to fix this
+      // properly.
+      if (touchFlags != CompositorHitTestInfo::eTouchActionDoubleTapZoomDisabled) {
+        if (!(hitTestInfo & CompositorHitTestInfo::eTouchActionPanXDisabled)) {
+          // pan-x is allowed
+          mHorizontalPanRegion.OrWith(area);
+        }
+        if (!(hitTestInfo & CompositorHitTestInfo::eTouchActionPanYDisabled)) {
+          // pan-y is allowed
+          mVerticalPanRegion.OrWith(area);
+        }
+      } else {
+        // the touch-action: manipulation case described above. To preserve the
+        // existing behaviour, don't touch either mHorizontalPanRegion or
+        // mVerticalPanRegion
+      }
+    }
+  }
+
+  // If there are multiple touch-action areas, there are multiple elements with
+  // touch-action properties. We don't know what the relationship is between
+  // those elements in terms of DOM ancestry, and so we don't know how to
+  // combine the regions properly. Instead, we just add all the areas to the
+  // dispatch-to-content region, so that the APZ knows to check with the
+  // main thread. See bug 1286957.
+  const int alreadyHadRegions = mNoActionRegion.GetNumRects() +
+    mHorizontalPanRegion.GetNumRects() +
+    mVerticalPanRegion.GetNumRects();
+
+  if (alreadyHadRegions > 1) {
+    mDispatchToContentHitRegion.OrWith(CombinedTouchActionRegion());
+  }
+
+  // Avoid quadratic performance as a result of the region growing to include
+  // and arbitrarily large number of rects, which can happen on some pages.
+  mMaybeHitRegion.SimplifyOutward(8);
+
+  // Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion
+  // for quick access in FindPaintedLayerFor().
+  mScaledHitRegionBounds =
+    aState->ScaleToOutsidePixels(mHitRegion.GetBounds());
+  mScaledMaybeHitRegionBounds =
+    aState->ScaleToOutsidePixels(mMaybeHitRegion.GetBounds());
+}
+
+void
 ContainerState::NewPaintedLayerData(PaintedLayerData* aData,
                                     AnimatedGeometryRoot* aAnimatedGeometryRoot,
                                     const ActiveScrolledRoot* aASR,
                                     const DisplayItemClipChain* aClipChain,
                                     const ActiveScrolledRoot* aScrollMetadataASR,
                                     const nsPoint& aTopLeft,
                                     const nsIFrame* aReferenceFrame,
                                     const bool aBackfaceHidden)
@@ -3970,34 +4080,58 @@ ContainerState::ProcessDisplayItems(nsDi
     if (ChooseAnimatedGeometryRoot(*aList, &lastAnimatedGeometryRoot, &lastASR)) {
       lastAGRTopLeft = (*lastAnimatedGeometryRoot)->GetOffsetToCrossDoc(mContainerReferenceFrame);
     }
   }
 
   int32_t maxLayers = gfxPrefs::MaxActiveLayers();
   int layerCount = 0;
 
+#ifdef DEBUG
+  bool hadLayerEventRegions = false;
+  bool hadCompositorHitTestInfo = false;
+#endif
+
   FlattenedDisplayItemIterator iter(mBuilder, aList);
   while (nsDisplayItem* i = iter.GetNext()) {
     nsDisplayItem* item = i;
     MOZ_ASSERT(item);
 
     DisplayItemType itemType = item->GetType();
 
     // If the item is a event regions item, but is empty (has no regions in it)
     // then we should just throw it out
     if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
+#ifdef DEBUG
+      hadLayerEventRegions = true;
+#endif
       nsDisplayLayerEventRegions* eventRegions =
         static_cast<nsDisplayLayerEventRegions*>(item);
 
       if (eventRegions->IsEmpty()) {
         continue;
       }
     }
 
+    if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
+#ifdef DEBUG
+      hadCompositorHitTestInfo = true;
+#endif
+      nsDisplayCompositorHitTestInfo* hitTestInfo =
+        static_cast<nsDisplayCompositorHitTestInfo*>(item);
+
+      if (hitTestInfo->Area().IsEmpty()) {
+        continue;
+      }
+    }
+
+    // Only allow either LayerEventRegions or CompositorHitTestInfo items.
+    MOZ_ASSERT(!(hadLayerEventRegions && hadCompositorHitTestInfo));
+
+
     // Peek ahead to the next item and see if it can be merged with the current
     // item. We create a list of consecutive items that can be merged together.
     AutoTArray<nsDisplayItem*, 1> mergedItems;
     mergedItems.AppendElement(item);
     while (nsDisplayItem* peek = iter.PeekNext()) {
       if (!item->CanMerge(peek)) {
         break;
       }
@@ -4021,16 +4155,17 @@ ContainerState::ProcessDisplayItems(nsDi
       "items in a container layer should all have the same app units per dev pixel");
 
     if (mBuilder->NeedToForceTransparentSurfaceForItem(item)) {
       aList->SetNeedsTransparentSurface();
     }
 
     if (mParameters.mForEventsAndPluginsOnly && !item->GetChildren() &&
         (itemType != DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
+         itemType != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO &&
          itemType != DisplayItemType::TYPE_PLUGIN)) {
       continue;
     }
 
     LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
     if (layerState == LAYER_INACTIVE &&
         nsDisplayItem::ForceActiveLayers()) {
       layerState = LAYER_ACTIVE;
@@ -4456,18 +4591,22 @@ ContainerState::ProcessDisplayItems(nsDi
                                                   [&](PaintedLayerData* aData) {
           NewPaintedLayerData(aData, animatedGeometryRoot, itemASR,
                               layerClipChain, scrollMetadataASR, topLeft,
                               referenceFrame, backfaceHidden);
         });
 
       if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
         nsDisplayLayerEventRegions* eventRegions =
-            static_cast<nsDisplayLayerEventRegions*>(item);
+          static_cast<nsDisplayLayerEventRegions*>(item);
         paintedLayerData->AccumulateEventRegions(this, eventRegions);
+      } else if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
+        nsDisplayCompositorHitTestInfo* hitTestInfo =
+          static_cast<nsDisplayCompositorHitTestInfo*>(item);
+        paintedLayerData->AccumulateHitTestInfo(this, hitTestInfo);
       } else {
         // check to see if the new item has rounded rect clips in common with
         // other items in the layer
         if (mManager->IsWidgetLayerManager()) {
           paintedLayerData->UpdateCommonClipCount(itemClip);
         }
         paintedLayerData->Accumulate(this, item, itemVisibleRect, itemClip, layerState, aList);
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1023,20 +1023,21 @@ nsDisplayListBuilder::nsDisplayListBuild
       mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
       mBuildingInvisibleItems(false),
       mHitTestIsForVisibility(false),
       mIsBuilding(false),
       mInInvalidSubtree(false)
 {
   MOZ_COUNT_CTOR(nsDisplayListBuilder);
 
-  mBuildCompositorHitTestInfo = gfxVars::UseWebRender()
-      && gfxPrefs::WebRenderHitTest()
-      && mAsyncPanZoomEnabled
-      && mMode == nsDisplayListBuilderMode::PAINTING;
+  const bool useWRHitTest =
+    gfxPrefs::WebRenderHitTest() && gfxVars::UseWebRender();
+
+  mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting() &&
+    (useWRHitTest || gfxPrefs::SimpleEventRegionItems());
 
   nsPresContext* pc = aReferenceFrame->PresContext();
   nsIPresShell *shell = pc->PresShell();
   if (pc->IsRenderingOnlySelection()) {
     nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell));
     if (selcon) {
       selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                            getter_AddRefs(mBoundingSelection));
@@ -1353,16 +1354,17 @@ nsDisplayListBuilder::EnterPresShell(nsI
 
 // A non-blank paint is a paint that does not just contain the canvas background.
 static bool
 DisplayListIsNonBlank(nsDisplayList* aList)
 {
   for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
     switch (i->GetType()) {
       case DisplayItemType::TYPE_LAYER_EVENT_REGIONS:
+      case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
       case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
       case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
         continue;
       case DisplayItemType::TYPE_SOLID_COLOR:
       case DisplayItemType::TYPE_BACKGROUND:
       case DisplayItemType::TYPE_BACKGROUND_COLOR:
         if (i->Frame()->IsCanvasFrame()) {
           continue;
@@ -2298,17 +2300,18 @@ nsDisplayList::ComputeVisibilityForRoot(
 }
 
 static nsRegion
 TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
 {
   bool snap;
   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
   if (aBuilder->IsForPluginGeometry() &&
-      aItem->GetType() != DisplayItemType::TYPE_LAYER_EVENT_REGIONS)
+      aItem->GetType() != DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
+      aItem->GetType() != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO)
   {
     // Treat all leaf chrome items as opaque, unless their frames are opacity:0.
     // Since opacity:0 frames generate an nsDisplayOpacity, that item will
     // not be treated as opaque here, so opacity:0 chrome content will be
     // effectively ignored, as it should be.
     // We treat leaf chrome items as opaque to ensure that they cover
     // content plugins, for security reasons.
     // Non-leaf chrome items don't render contents of their own so shouldn't
@@ -4970,57 +4973,53 @@ nsDisplayEventReceiver::CreateWebRenderC
 nsDisplayCompositorHitTestInfo::nsDisplayCompositorHitTestInfo(nsDisplayListBuilder* aBuilder,
                                                                nsIFrame* aFrame,
                                                                mozilla::gfx::CompositorHitTestInfo aHitTestInfo,
                                                                uint32_t aIndex,
                                                                const mozilla::Maybe<nsRect>& aArea)
   : nsDisplayEventReceiver(aBuilder, aFrame)
   , mHitTestInfo(aHitTestInfo)
   , mIndex(aIndex)
+  , mAppUnitsPerDevPixel(mFrame->PresContext()->AppUnitsPerDevPixel())
 {
   MOZ_COUNT_CTOR(nsDisplayCompositorHitTestInfo);
   // We should never even create this display item if we're not building
   // compositor hit-test info or if the computed hit info indicated the
   // frame is invisible to hit-testing
   MOZ_ASSERT(aBuilder->BuildCompositorHitTestInfo());
   MOZ_ASSERT(mHitTestInfo != mozilla::gfx::CompositorHitTestInfo::eInvisibleToHitTest);
 
   if (aBuilder->GetCurrentScrollbarFlags() != nsDisplayOwnLayerFlags::eNone) {
     // In the case of scrollbar frames, we use the scrollbar's target scrollframe
     // instead of the scrollframe with which the scrollbar actually moves.
     MOZ_ASSERT(mHitTestInfo & CompositorHitTestInfo::eScrollbar);
     mScrollTarget = Some(aBuilder->GetCurrentScrollbarTarget());
   }
 
-  nsRect area;
   if (aArea.isSome()) {
-    area = *aArea;
+    mArea = *aArea;
   } else {
     nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(mFrame);
     if (scrollFrame) {
       // If the frame is content of a scrollframe, then we need to pick up the
       // area corresponding to the overflow rect as well. Otherwise the parts of
       // the overflow that are not occupied by descendants get skipped and the
       // APZ code sends touch events to the content underneath instead.
       // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
-      area = mFrame->GetScrollableOverflowRect();
+      mArea = mFrame->GetScrollableOverflowRect();
     } else {
-      area = nsRect(nsPoint(0, 0), mFrame->GetSize());
+      mArea = nsRect(nsPoint(0, 0), mFrame->GetSize());
     }
 
     // Note that it's important to do this call to ToReferenceFrame here in the
     // nsDisplayCompositorHitTestInfo constructor, because then we'll hit the good
     // fast path (because aBuilder will already have the info we want cached).
     // This is as opposed to, say, calling it in CreateWebRenderCommands where
     // we would not hit the fast path.
-    area += aBuilder->ToReferenceFrame(mFrame);
-  }
-
-  if (!area.IsEmpty()) {
-    mArea = LayoutDeviceRect::FromAppUnits(area, mFrame->PresContext()->AppUnitsPerDevPixel());
+    mArea += aBuilder->ToReferenceFrame(mFrame);
   }
 }
 
 bool
 nsDisplayCompositorHitTestInfo::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                                         mozilla::wr::IpcResourceUpdateQueue& aResources,
                                                         const StackingContextHelper& aSc,
                                                         mozilla::layers::WebRenderLayerManager* aManager,
@@ -5040,17 +5039,22 @@ nsDisplayCompositorHitTestInfo::CreateWe
           if (const ActiveScrolledRoot* asr = GetActiveScrolledRoot()) {
             return asr->GetViewId();
           }
           return FrameMetrics::NULL_SCROLL_ID;
       });
 
   // Insert a transparent rectangle with the hit-test info
   aBuilder.SetHitTestInfo(scrollId, mHitTestInfo);
-  wr::LayoutRect rect = aSc.ToRelativeLayoutRect(mArea);
+
+  const LayoutDeviceRect devRect =
+    LayoutDeviceRect::FromAppUnits(mArea, mAppUnitsPerDevPixel);
+
+  const wr::LayoutRect rect = aSc.ToRelativeLayoutRect(devRect);
+
   aBuilder.PushRect(rect, rect, true, wr::ToColorF(gfx::Color()));
   aBuilder.ClearHitTestInfo();
 
   return true;
 }
 
 void
 nsDisplayCompositorHitTestInfo::WriteDebugInfo(std::stringstream& aStream)
@@ -6556,16 +6560,17 @@ CollectItemsWithOpacity(nsDisplayList* a
     if (type == DisplayItemType::TYPE_WRAP_LIST && children) {
       // The current display item has children, process them first.
       if (!CollectItemsWithOpacity(children, aArray, aMaxChildCount)) {
         return false;
       }
     }
 
     if (type == DisplayItemType::TYPE_LAYER_EVENT_REGIONS ||
+        type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
         type == DisplayItemType::TYPE_WRAP_LIST) {
       continue;
     }
 
     if (!i->CanApplyOpacity() || aArray.Length() == aMaxChildCount) {
       return false;
     }
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -4532,24 +4532,47 @@ public:
                                const StackingContextHelper& aSc,
                                mozilla::layers::WebRenderLayerManager* aManager,
                                nsDisplayListBuilder* aDisplayListBuilder) override;
   void WriteDebugInfo(std::stringstream& aStream) override;
   uint32_t GetPerFrameKey() const override;
   int32_t ZIndex() const override;
   void SetOverrideZIndex(int32_t aZIndex);
 
+  /**
+   * Returns the hit test area of this item.
+   */
+  const nsRect& Area() const { return mArea; }
+
+  /**
+   * ApplyOpacity() is overriden for opacity flattening.
+   */
+  void ApplyOpacity(nsDisplayListBuilder* aBuilder, float aOpacity,
+                    const DisplayItemClipChain* aClip) override {}
+
+  /**
+   * CanApplyOpacity() is overriden for opacity flattening.
+   */
+  bool CanApplyOpacity() const override { return true; }
+
+  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override
+  {
+    *aSnap = false;
+    return nsRect();
+  }
+
   NS_DISPLAY_DECL_NAME("CompositorHitTestInfo", TYPE_COMPOSITOR_HITTEST_INFO)
 
 private:
   mozilla::gfx::CompositorHitTestInfo mHitTestInfo;
   mozilla::Maybe<mozilla::layers::FrameMetrics::ViewID> mScrollTarget;
-  mozilla::LayoutDeviceRect mArea;
+  nsRect mArea;
   uint32_t mIndex;
   mozilla::Maybe<int32_t> mOverrideZIndex;
+  int32_t mAppUnitsPerDevPixel;
 };
 
 /**
  * A display item that tracks event-sensitive regions which will be set
  * on the ContainerLayer that eventually contains this item.
  *
  * One of these is created for each stacking context and pseudo-stacking-context.
  * It accumulates regions for event targets contributed by the border-boxes of