Bug 1272429 - When hit-testing layers nested inside scrollable content inside fixed-pos items, make sure to hit the scrollable layers. r?botond draft
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 12 May 2016 17:30:37 -0400
changeset 366449 579b69be2bcc253f53cc5aee92b82707b504db80
parent 366346 c3f5e6079284a7b7053c41f05d0fe06ff031db03
child 520797 f540922932643828a318e1e23de48f201c98cfaf
push id18007
push userkgupta@mozilla.com
push dateThu, 12 May 2016 21:31:04 +0000
reviewersbotond
bugs1272429
milestone49.0a1
Bug 1272429 - When hit-testing layers nested inside scrollable content inside fixed-pos items, make sure to hit the scrollable layers. r?botond MozReview-Commit-ID: 2lpK6Jp17s5
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/HitTestingTreeNode.cpp
gfx/layers/apz/src/HitTestingTreeNode.h
gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1771,31 +1771,26 @@ APZCTreeManager::GetAPZCAtPoint(HitTesti
       if (aOutHitScrollbar) {
         for (HitTestingTreeNode* n = resultNode; n; n = n->GetParent()) {
           if (n->IsScrollbarNode()) {
             *aOutHitScrollbar = true;
           }
         }
       }
 
-      AsyncPanZoomController* result = nullptr;
-
-      FrameMetrics::ViewID fpTarget =
-          resultNode->GetNearestAncestorFixedPosTargetWithSameLayersId();
-      if (fpTarget != FrameMetrics::NULL_SCROLL_ID) {
+      FrameMetrics::ViewID fpTarget = FrameMetrics::NULL_SCROLL_ID;
+      AsyncPanZoomController* result = resultNode->GetNearestContainingApzcOrFixedPosTargetWithSameLayersId(&fpTarget);
+      APZCTM_LOG("Found target %p using ancestor lookup\n", result);
+      if (!result && fpTarget != FrameMetrics::NULL_SCROLL_ID) {
         ScrollableLayerGuid guid(resultNode->GetLayersId(), 0, fpTarget);
         RefPtr<HitTestingTreeNode> hitNode = GetTargetNode(guid, &GuidComparatorIgnoringPresShell);
         result = hitNode ? hitNode->GetApzc() : nullptr;
         APZCTM_LOG("Found target %p using fixed-pos lookup on %" PRIu64 "\n", result, fpTarget);
       }
       if (!result) {
-        result = resultNode->GetNearestContainingApzcWithSameLayersId();
-        APZCTM_LOG("Found target %p using ancestor lookup\n", result);
-      }
-      if (!result) {
         result = FindRootApzcForLayersId(resultNode->GetLayersId());
         MOZ_ASSERT(result);
         APZCTM_LOG("Found target %p using root lookup\n", result);
       }
       APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
           result, resultNode, *aOutHitResult);
       return result;
   }
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -128,29 +128,16 @@ HitTestingTreeNode::IsScrollbarNode() co
 }
 
 void
 HitTestingTreeNode::SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget)
 {
   mFixedPosTarget = aFixedPosTarget;
 }
 
-FrameMetrics::ViewID
-HitTestingTreeNode::GetNearestAncestorFixedPosTargetWithSameLayersId() const
-{
-  for (const HitTestingTreeNode* n = this;
-       n && n->mLayersId == mLayersId;
-       n = n->GetParent()) {
-    if (n->mFixedPosTarget != FrameMetrics::NULL_SCROLL_ID) {
-      return n->mFixedPosTarget;
-    }
-  }
-  return FrameMetrics::NULL_SCROLL_ID;
-}
-
 void
 HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling)
 {
   mPrevSibling = aSibling;
   if (aSibling) {
     aSibling->mParent = mParent;
 
     if (aSibling->GetApzc()) {
@@ -211,24 +198,29 @@ HitTestingTreeNode::GetNearestContaining
     if (n->GetApzc()) {
       return n->GetApzc();
     }
   }
   return nullptr;
 }
 
 AsyncPanZoomController*
-HitTestingTreeNode::GetNearestContainingApzcWithSameLayersId() const
+HitTestingTreeNode::GetNearestContainingApzcOrFixedPosTargetWithSameLayersId(
+    FrameMetrics::ViewID* aOutFixedPosTarget) const
 {
   for (const HitTestingTreeNode* n = this;
        n && n->mLayersId == mLayersId;
        n = n->GetParent()) {
     if (n->GetApzc()) {
       return n->GetApzc();
     }
+    if (aOutFixedPosTarget && n->mFixedPosTarget != FrameMetrics::NULL_SCROLL_ID) {
+      *aOutFixedPosTarget = n->mFixedPosTarget;
+      return nullptr;
+    }
   }
   return nullptr;
 }
 
 bool
 HitTestingTreeNode::IsPrimaryHolder() const
 {
   return mIsPrimaryApzcHolder;
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -73,17 +73,18 @@ public:
   HitTestingTreeNode* GetLastChild() const;
   HitTestingTreeNode* GetPrevSibling() const;
   HitTestingTreeNode* GetParent() const;
 
   /* APZC related methods */
 
   AsyncPanZoomController* GetApzc() const;
   AsyncPanZoomController* GetNearestContainingApzc() const;
-  AsyncPanZoomController* GetNearestContainingApzcWithSameLayersId() const;
+  AsyncPanZoomController* GetNearestContainingApzcOrFixedPosTargetWithSameLayersId(
+      FrameMetrics::ViewID* aOutFixedPosTarget) const;
   bool IsPrimaryHolder() const;
   uint64_t GetLayersId() const;
 
   /* Hit test related methods */
 
   void SetHitTestData(const EventRegions& aRegions,
                       const CSSTransformMatrix& aTransform,
                       const Maybe<ParentLayerIntRegion>& aClipRegion,
@@ -98,17 +99,16 @@ public:
                         bool aIsScrollContainer);
   bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
   int32_t GetScrollSize() const;
   bool IsScrollbarNode() const;
 
   /* Fixed pos info */
 
   void SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget);
-  FrameMetrics::ViewID GetNearestAncestorFixedPosTargetWithSameLayersId() const;
 
   /* Convert aPoint into the LayerPixel space for the layer corresponding to
    * this node. */
   Maybe<LayerPoint> Untransform(const ParentLayerPoint& aPoint) const;
   /* Assuming aPoint is inside the clip region for this node, check which of the
    * event region spaces it falls inside. */
   HitTestResult HitTest(const ParentLayerPoint& aPoint) const;
   /* Returns the mOverride flag. */
--- a/gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
+++ b/gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
@@ -32,16 +32,28 @@ function* runTest() {
   yield scrollWheelOver(iframeWin.document.body, 50, 150);
   ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:sticky element");
 
   // same, but using the iframe's position:fixed element
   scrollPos = iframeWin.scrollY;
   yield scrollWheelOver(iframeWin.document.body, 250, 150);
   ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:fixed element");
 
+  // same, but scrolling the scrollable frame *inside* the position:fixed item
+  var fpos = document.getElementById('fpos_scrollable');
+  scrollPos = fpos.scrollTop;
+  yield scrollWheelOver(fpos, 50, 150);
+  ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled");
+  // wait for it to layerize fully and then try again
+  yield waitForAllPaints(driveTest);
+  yield flushApzRepaints(driveTest);
+  scrollPos = fpos.scrollTop;
+  yield scrollWheelOver(fpos, 50, 150);
+  ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled after layerization");
+
   // same, but using the top-level window's position:sticky element
   scrollPos = window.scrollY;
   yield scrollWheelOver(document.body, 50, 150);
   ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:sticky element");
 
   // same, but using the top-level window's position:fixed element
   scrollPos = window.scrollY;
   yield scrollWheelOver(document.body, 250, 150);
@@ -51,26 +63,28 @@ function* runTest() {
 var gTestContinuation = null;
 function driveTest() {
   if (!gTestContinuation) {
     gTestContinuation = runTest();
   }
   var ret = gTestContinuation.next();
   if (ret.done) {
     window.opener.testDone();
-  } else {
-    is(ret.value, true, "Test continuation chunk was successful");
   }
 }
 
 window.onload = function() {
   waitForAllPaints(function() {
     flushApzRepaints(driveTest);
   });
 }
   </script>
 </head>
 <body style="height:5000px; margin:0">
   <div style="position:sticky; width: 100px; height: 300px; top: 0; background-color:red">sticky</div>
   <div style="position:fixed; width: 100px; height: 300px; top: 0; left: 200px; background-color: green">fixed</div>
   <iframe id='iframe' width="300" height="400" src="data:text/html,<body style='height:5000px; margin:0'><div style='position:sticky; width:100px; height:300px; top: 0; background-color:red'>sticky</div><div style='position:fixed; right:0; top: 0; width:100px; height:300px; background-color:green'>fixed</div>"></iframe>
+
+  <div id="fpos_scrollable" style="position:fixed; width: 100px; height: 300px; top: 0; left: 400px; background-color: red; overflow:scroll">
+   <div style="background-color: blue; height: 1000px; margin: 3px">scrollable content inside a fixed-pos item</div>
+  </div>
 </body>
 </head>