From 3a0314531e588c8a487c52e89dc756911731c663 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Tue, 30 Mar 2021 04:07:23 +0000 Subject: [PATCH] Bug 1699868 - If an event is over the overscroll gutter of a subframe, target the subframe. r=tnikkel Differential Revision: https://phabricator.services.mozilla.com/D110185 --- gfx/layers/apz/src/APZCTreeManager.cpp | 49 +++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 12024d41c2b1..0ee0917e412d 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -3014,7 +3014,7 @@ APZCTreeManager::HitTestResult APZCTreeManager::GetAPZCAtPoint( HitTestResult hit; // This walks the tree in depth-first, reverse order, so that it encounters // APZCs front-to-back on the screen. - HitTestingTreeNode* resultNode; + HitTestingTreeNode* resultNode = nullptr; HitTestingTreeNode* root = mRootNode; HitTestingTreeNode* scrollbarNode = nullptr; std::stack hitTestPoints; @@ -3025,21 +3025,49 @@ APZCTreeManager::HitTestResult APZCTreeManager::GetAPZCAtPoint( ForEachNode( root, - [&hitTestPoints, this](HitTestingTreeNode* aNode) { + [&resultNode, &hitTestPoints, &hit, this](HitTestingTreeNode* aNode) { ParentLayerPoint hitTestPointForParent = ViewAs( hitTestPoints.top(), PixelCastJustification::MovingDownToChildren); if (aNode->IsOutsideClip(hitTestPointForParent)) { // If the point being tested is outside the clip region for this node // then we don't need to test against this node or any of its // children. Just skip it and move on. - APZCTM_LOG("Point %f %f outside clip for node %p\n", - hitTestPoints.top().x, hitTestPoints.top().y, aNode); + APZCTM_LOG("Point %s outside clip for node %p\n", + ToString(hitTestPointForParent).c_str(), aNode); return TraversalFlag::Skip; } + // If this node has a transform that includes an overscroll transform, + // check if the point is inside the corresponding APZC's overscroll + // gutter. We do this here in the pre-action because we need the + // hit-test point in ParentLayer coordinates for this check. If the + // point is in the gutter, we can abort the search and target this node. + // (Note that no descendant node would be a match if we're in the + // gutter.) + const AsyncPanZoomController* sourceOfOverscrollTransform = nullptr; + auto transform = + ComputeTransformForNode(aNode, &sourceOfOverscrollTransform); + if (sourceOfOverscrollTransform && + sourceOfOverscrollTransform->IsInOverscrollGutter( + hitTestPointForParent)) { + APZCTM_LOG( + "ParentLayer point %s in overscroll gutter of APZC %p (node " + "%p)\n", + ToString(hitTestPointForParent).c_str(), aNode->GetApzc(), aNode); + resultNode = aNode; + // We want to target the overscrolled APZC, but if we're over the + // gutter then we're not over its hit or DTC regions. Use + // {eVisibleToHitTest} as the hit result because the event won't be + // sent to gecko (so DTC flags are irrelevant), and we do want + // browser default actions to work (e.g. scrolling to relieve the + // overscroll). + hit.mHitResult = {CompositorHitTestFlags::eVisibleToHitTest}; + hit.mHitOverscrollGutter = true; + return TraversalFlag::Abort; + } // First check the subtree rooted at this node, because deeper nodes // are more "in front". - Maybe hitTestPoint = aNode->Untransform( - hitTestPointForParent, ComputeTransformForNode(aNode)); + Maybe hitTestPoint = + aNode->Untransform(hitTestPointForParent, transform); APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n", ToString(hitTestPointForParent).c_str(), hitTestPoint ? ToString(hitTestPoint.ref()).c_str() : "nil"); @@ -3105,8 +3133,13 @@ APZCTreeManager::HitTestResult APZCTreeManager::GetAPZCAtPoint( hit.mLayersId = resultNode->GetLayersId(); } - hit.mHitOverscrollGutter = - hit.mTargetApzc && hit.mTargetApzc->IsInOverscrollGutter(aHitTestPoint); + // If we found an APZC that wasn't directly on the result node, we haven't + // checked if we're in its overscroll gutter, so check now. + if (hit.mTargetApzc && resultNode && + (hit.mTargetApzc != resultNode->GetApzc())) { + hit.mHitOverscrollGutter = + hit.mTargetApzc->IsInOverscrollGutter(aHitTestPoint); + } return hit; }