Bug 1529458 - Only create new hit test info for positioned frame if they might be sorted into a different location. r=kats
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 11 Mar 2019 18:36:42 +0000
changeset 521418 f00a5d27f41760febe43b3ef52b727a54f082abf
parent 521417 6ded300544a9b8826b5575334dbbb7681a1015fb
child 521419 4f506f3861997bfcea12588a0a724c54b5807ec6
push id10866
push usernerli@mozilla.com
push dateTue, 12 Mar 2019 18:59:09 +0000
treeherdermozilla-beta@445c24a51727 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1529458
milestone67.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 1529458 - Only create new hit test info for positioned frame if they might be sorted into a different location. r=kats Differential Revision: https://phabricator.services.mozilla.com/D22649
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/painting/nsDisplayList.cpp
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3839,27 +3839,44 @@ void nsIFrame::BuildDisplayListForChild(
     }
 
     // A pseudo-stacking context (e.g., a positioned element with z-index auto).
     // We allow positioned descendants of the child to escape to our parent
     // stacking context's positioned descendant list, because they might be
     // z-index:non-auto
     nsDisplayListCollection pseudoStack(aBuilder);
 
+    // If this frame has z-index != 0, then the display item might get sorted
+    // into a different place in the list, and we can't rely on the previous
+    // hit test info to still be behind us. Force a new hit test info for this
+    // item, and for the item after it, so that we always have the right hit
+    // test info.
+    bool mayBeSorted = isPositioned && (ZIndex() != 0);
+
     aBuilder->BuildCompositorHitTestInfoIfNeeded(
-        child, pseudoStack.BorderBackground(), differentAGR || isPositioned);
+        child, pseudoStack.BorderBackground(), differentAGR || mayBeSorted);
 
     aBuilder->AdjustWindowDraggingRegion(child);
     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
     aBuilder->Check();
     child->BuildDisplayList(aBuilder, pseudoStack);
     aBuilder->Check();
     if (aBuilder->DisplayCaret(child, pseudoStack.Content())) {
       builtContainerItem = false;
     }
+
+    // If we forced a new hit-test info because this frame is going to be
+    // sorted, then clear the 'previous' data on the builder so that the next
+    // item also gets a new hit test info. That way we're guaranteeing hit-test
+    // info before and after each item that might get moved to a different spot.
+    if (mayBeSorted) {
+      aBuilder->SetCompositorHitTestInfo(
+          nsRect(), CompositorHitTestFlags::eVisibleToHitTest);
+    }
+
     wrapListASR = contASRTracker.GetContainerASR();
 
     list.AppendToTop(pseudoStack.BorderBackground());
     list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
     list.AppendToTop(pseudoStack.Floats());
     list.AppendToTop(pseudoStack.Content());
     list.AppendToTop(pseudoStack.Outlines());
     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
@@ -9295,16 +9312,30 @@ void nsIFrame::ComputePreserve3DChildren
         if (child->Extend3DContext(childDisp, child->StyleEffects())) {
           child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
         }
       }
     }
   }
 }
 
+int32_t nsIFrame::ZIndex() const {
+  if (!IsAbsPosContainingBlock() && !IsFlexOrGridItem()) {
+    return 0;
+  }
+
+  const nsStylePosition* position = StylePosition();
+  if (position->mZIndex.IsInteger()) {
+    return position->mZIndex.AsInteger();
+  }
+  MOZ_ASSERT(position->mZIndex.IsAuto());
+  // sort the auto and 0 elements together
+  return 0;
+}
+
 bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
   if (!mInScrollAnchorChain) {
     return false;
   }
 
   ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(this);
   if (container->AnchorNode() != this) {
     return false;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1880,16 +1880,22 @@ class nsIFrame : public nsQueryFrame {
    * Includes the overflow area of all descendants that participate in the
    * current 3d context into aOverflowAreas.
    */
   void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas);
 
   void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame);
 
   /**
+   * Returns the computed z-index for this frame, returning 0 for z-index:auto
+   * and frames that don't support z-index.
+   */
+  int32_t ZIndex() const;
+
+  /**
    * Returns whether this frame is the anchor of some ancestor scroll frame. As
    * this frame is moved, the scroll frame will apply adjustments to keep this
    * scroll frame in the same relative position.
    *
    * aOutContainer will optionally be set to the scroll anchor container for
    * this frame if this frame is an anchor.
    */
   bool IsScrollAnchor(
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -3139,31 +3139,17 @@ bool nsDisplayItem::ForceActiveLayers() 
   if (!sForceCached) {
     Preferences::AddBoolVarCache(&sForce, "layers.force-active", false);
     sForceCached = true;
   }
 
   return sForce;
 }
 
-static int32_t ZIndexForFrame(nsIFrame* aFrame) {
-  if (!aFrame->IsAbsPosContainingBlock() && !aFrame->IsFlexOrGridItem()) {
-    return 0;
-  }
-
-  const nsStylePosition* position = aFrame->StylePosition();
-  if (position->mZIndex.IsInteger()) {
-    return position->mZIndex.AsInteger();
-  }
-  MOZ_ASSERT(position->mZIndex.IsAuto());
-  // sort the auto and 0 elements together
-  return 0;
-}
-
-int32_t nsDisplayItem::ZIndex() const { return ZIndexForFrame(mFrame); }
+int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex(); }
 
 bool nsDisplayItem::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                       nsRegion* aVisibleRegion) {
   return !GetPaintRect().IsEmpty() &&
          !IsInvisibleInRect(aVisibleRegion->GetBounds());
 }
 
 bool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,