Skip to content

Commit

Permalink
Cherry-pick 048b254. rdar://problem/110032480
Browse files Browse the repository at this point in the history
    transform-style:preserve-3d has incorrect hit-testing of negative z-index ::after.
    https://bugs.webkit.org/show_bug.cgi?id=255028

    Reviewed by Simon Fraser.

    We call hitTestList with the intention of storing the result in the temporary object 'hitLayer',
    and only mutate the final result 'candidateLayer' if the depth test passes.
    Unfortunately the 'result' variable is also part of the final output, and this gets mutated
    on hitTestList calls that don't pass the depth test.

    This creates a temporary 'tempResult' (like we do for the other sections of this function),
    and only copies back to 'result' if the depth test passes.

    * LayoutTests/transforms/3d/hit-testing/hit-preserves-3d-2-expected.txt: Added.
    * LayoutTests/transforms/3d/hit-testing/hit-preserves-3d-2.html: Added.
    * Source/WebCore/rendering/RenderLayer.cpp:
    (WebCore::RenderLayer::hitTestLayer):

    Canonical link: https://commits.webkit.org/262728@main

Identifier: 259548.791@safari-7615-branch
  • Loading branch information
mattwoodrow authored and MyahCobbs committed May 31, 2023
1 parent 1e61659 commit 9316f6e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hit Me
Hit correct target: PASSED
53 changes: 53 additions & 0 deletions LayoutTests/transforms/3d/hit-testing/hit-preserves-3d-2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!doctype html>
<title>Hit testing preserve-3d + negative z-index ::after</title>
<style>
* {
position: relative;
}

#container {
width: 200px;
height: 200px
display: flex;
background: beige;
align-items: center;
transform-style: preserve-3d;
}

#container::after {
width: 200px;
height: 200px;
content: "";
display: block;
position: absolute;
background: aliceblue;
top: 0%;
z-index: -1;
}

#hit {
display: block;
width: 200px;
height: 200px;
}

</style>
<body>
<div id="container">
<a id="hit" href="2">Hit Me</a>
</div>

<div id="results"></div>

<script type="text/javascript" charset="utf-8">
if (window.testRunner)
testRunner.dumpAsText();

var hit = document.elementFromPoint(100, 100);
var results = document.getElementById('results');
if (hit == document.getElementById('hit'))
results.innerHTML = 'Hit correct target: PASSED';
else
results.innerHTML = 'Failed to find correct target: FAIL';
</script>
</body>
44 changes: 28 additions & 16 deletions Source/WebCore/rendering/RenderLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4140,15 +4140,21 @@ RenderLayer::HitLayer RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLa
}

// Now check our overflow objects.
hitLayer = hitTestList(normalFlowLayers(), rootLayer, request, result, hitTestRect, hitTestLocation, localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer.layer) {
if (!depthSortDescendants)
return hitLayer;
if (using3DTransformsInterop) {
if (hitLayer.zOffset > candidateLayer.zOffset)
{
HitTestResult tempResult(result.hitTestLocation());
hitLayer = hitTestList(normalFlowLayers(), rootLayer, request, tempResult, hitTestRect, hitTestLocation, localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer.layer) {
if (!depthSortDescendants || !using3DTransformsInterop || hitLayer.zOffset > candidateLayer.zOffset) {
if (request.resultIsElementList())
result.append(tempResult, request);
else
result = tempResult;
candidateLayer = hitLayer;
} else
candidateLayer = hitLayer;
}

if (!depthSortDescendants)
return hitLayer;
}
}

// Collect the fragments. This will compute the clip rectangles for each layer fragment.
Expand Down Expand Up @@ -4187,15 +4193,21 @@ RenderLayer::HitLayer RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLa
}

// Now check our negative z-index children.
hitLayer = hitTestList(negativeZOrderLayers(), rootLayer, request, result, hitTestRect, hitTestLocation, localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer.layer) {
if (!depthSortDescendants)
return hitLayer;
if (using3DTransformsInterop) {
if (hitLayer.zOffset > candidateLayer.zOffset)
{
HitTestResult tempResult(result.hitTestLocation());
hitLayer = hitTestList(negativeZOrderLayers(), rootLayer, request, tempResult, hitTestRect, hitTestLocation, localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer.layer) {
if (!depthSortDescendants || !using3DTransformsInterop || hitLayer.zOffset > candidateLayer.zOffset) {
if (request.resultIsElementList())
result.append(tempResult, request);
else
result = tempResult;
candidateLayer = hitLayer;
} else
candidateLayer = hitLayer;
}

if (!depthSortDescendants)
return hitLayer;
}
}

// If we found a layer, return. Child layers, and foreground always render in front of background.
Expand Down

0 comments on commit 9316f6e

Please sign in to comment.