Skip to content

Commit

Permalink
[Content-visibility][repaint] Boxes with "content-visibility: hidden"…
Browse files Browse the repository at this point in the history
… lingers around

https://bugs.webkit.org/show_bug.cgi?id=264173

Reviewed by Simon Fraser.

When skipped content becomes hidden, a repaint should be issued to clear it, before the style change is processed.
Add repaint logic for that similar to what is done when the visibility property changes to hidden on an element.

However, absolute positioned elements are not always repainted correctly when hidden and the container
changes. First, after layout repaints will have no effect since it is skipped for layers with hidden content.
For the repaint before style change, the newly calculated clipped overflow rectangle can be empty, causing the
artefact. Fix this by always using the cached clipped overflow ("old") rectangle for out of flow elements, if
it is available, in before style change repainting.

* LayoutTests/fast/repaint/content-visibility-hidden-container-with-abs-pos-c-v-auto-child-expected.txt: Added.
* LayoutTests/fast/repaint/content-visibility-hidden-container-with-abs-pos-c-v-auto-child.html: Added.
* LayoutTests/fast/repaint/content-visibility-hidden-container-with-abs-pos-child-expected.txt: Added.
* LayoutTests/fast/repaint/content-visibility-hidden-container-with-abs-pos-child.html: Added.
* LayoutTests/fast/repaint/position-relative-container-with-abs-pos-child-expected.txt: Added.
* LayoutTests/fast/repaint/position-relative-container-with-abs-pos-child.html: Added.
* Source/WebCore/rendering/RenderElement.cpp:
(WebCore::RenderElement::repaintBeforeStyleChange):
(WebCore::RenderElement::styleWillChange):

Canonical link: https://commits.webkit.org/273602@main
  • Loading branch information
rwlbuis committed Jan 27, 2024
1 parent 10a815c commit b96b33e
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PASS window.internals.repaintRectsAsText().indexOf('50 50') is not -1
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<script>jsTestIsAsync = true;</script>
<script src="../../resources/js-test-pre.js"></script>
<head>
<title>This tests that we don't leave bits behind, when an absolute positioned content-visibility: auto child is hidden due to content-visibility: hidden.</title>
<style>
#container {
height: 100px;
width: 100px;
}

#content {
position: absolute;
height: 50px;
width: 50px;
background: green;
content-visibility: auto;
}
</style>
</head>
<body>
<div id=container>
<div id=content></div>
</div>
<script>
setTimeout(
function() {
if (window.internals)
internals.startTrackingRepaints();
document.getElementById("container").style.contentVisibility = "hidden";
document.body.offsetWidth;

if (window.internals) {
shouldNotBe("window.internals.repaintRectsAsText().indexOf('50 50')", "-1");
internals.stopTrackingRepaints();
}
finishJSTest();
}, 0);
</script>
</body>
<script src="../../resources/js-test-post.js"></script>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PASS window.internals.repaintRectsAsText().indexOf('50 50') is not -1
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<script>jsTestIsAsync = true;</script>
<script src="../../resources/js-test-pre.js"></script>
<head>
<title>This tests that we don't leave bits behind, when an absolute positioned child is hidden due to content-visibility: hidden.</title>
<style>
#container {
height: 100px;
width: 100px;
}

#content {
position: absolute;
height: 50px;
width: 50px;
background: green;
}
</style>
</head>
<body>
<div id=container>
<div id=content></div>
</div>
<script>
setTimeout(
function() {
if (window.internals)
internals.startTrackingRepaints();
document.getElementById("container").style.contentVisibility = "hidden";
document.body.offsetWidth;

if (window.internals) {
shouldNotBe("window.internals.repaintRectsAsText().indexOf('50 50')", "-1");
internals.stopTrackingRepaints();
}
finishJSTest();
}, 0);
</script>
</body>
<script src="../../resources/js-test-post.js"></script>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PASS window.internals.repaintRectsAsText().indexOf('0 0 50 50') is not -1
PASS successfullyParsed is true

TEST COMPLETE

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<script>jsTestIsAsync = true;</script>
<script src="../../resources/js-test-pre.js"></script>
<head>
<title>This tests that we don't leave bits behind, when an absolute positioned child is hidden and at the same time is positioned against a different container.</title>
<style>
#container {
height: 100px;
width: 100px;
}

#content {
position: absolute;
height: 50px;
width: 50px;
left: 0px;
top: 0px;
background: green;
}
</style>
</head>
<body>
<div id=container>
<div id=content></div>
</div>
<script>
setTimeout(
function() {
if (window.internals)
internals.startTrackingRepaints();
document.getElementById("container").style.position = "relative";
document.getElementById("content").style.visibility = "hidden";
document.body.offsetWidth;

if (window.internals) {
shouldNotBe("window.internals.repaintRectsAsText().indexOf('0 0 50 50')", "-1");
internals.stopTrackingRepaints();
}
finishJSTest();
}, 0);
</script>
</body>
<script src="../../resources/js-test-post.js"></script>
</html>
14 changes: 14 additions & 0 deletions Source/WebCore/rendering/RenderElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,14 @@ bool RenderElement::repaintBeforeStyleChange(StyleDifference diff, const RenderS
}
}

if (diff > StyleDifference::RepaintLayer && oldStyle.effectiveContentVisibility() != newStyle.effectiveContentVisibility() && isOutOfFlowPositioned()) {
if (CheckedPtr enclosingLayer = this->enclosingLayer()) {
bool rendererWillBeHidden = isSkippedContent();
if (rendererWillBeHidden && enclosingLayer->hasVisibleContent() && (this == &enclosingLayer->renderer() || enclosingLayer->renderer().style().visibility() != Visibility::Visible))
return RequiredRepaint::RendererOnly;
}
}

return RequiredRepaint::None;
}();

Expand All @@ -473,6 +481,12 @@ bool RenderElement::repaintBeforeStyleChange(StyleDifference diff, const RenderS
}

if (shouldRepaintBeforeStyleChange == RequiredRepaint::RendererOnly) {
if (isOutOfFlowPositioned() && downcast<RenderLayerModelObject>(*this).checkedLayer()->isSelfPaintingLayer()) {
if (auto cachedClippedOverflowRect = downcast<RenderLayerModelObject>(*this).checkedLayer()->cachedClippedOverflowRect()) {
repaintUsingContainer(containerForRepaint().renderer.get(), *cachedClippedOverflowRect);
return true;
}
}
repaint();
return true;
}
Expand Down

0 comments on commit b96b33e

Please sign in to comment.