Skip to content

Commit d9617b1

Browse files
committed
Block reusing may remove floating object too early
https://bugs.webkit.org/show_bug.cgi?id=290862 <rdar://147215658> Reviewed by Antti Koivisto. "Reusing block" type mutations (see RenderTreeBuilder::Inline::splitFlow) followed by float removal may lead to an unexpected state where we have a float to remove, but we have already destroyed m_floatingObjects, causing us to incorrectly assume that the float no longer belongs here (markSiblingsWithFloatsForLayout) and, therefore, does not need to be removed from sibling blocks (in case it is intrusive). What happens here is: 1. tree mutation makes an anon block reused (pre block) 2. a float is removed from said anon block's subtree At #1 we call removeFloatingObjects() which simply clears and destroys m_floatingObjects on the anon block. Now at #2, when we try to remove this float from sibling block containers by calling RenderBlockFlow::markSiblingsWithFloatsForLayout, and we consult m_floatingObjects to see if there's any float associated with the block and we early return as we had already cleared this set at #1. This patch ensures that when markSiblingsWithFloatsForLayout is called with a valid float, we always try to clean up sibling content. * LayoutTests/fast/block/float-remove-after-block-collapse-crash-expected.txt: Added. * LayoutTests/fast/block/float-remove-after-block-collapse-crash.html: Added. * Source/WebCore/rendering/RenderBlockFlow.cpp: (WebCore::RenderBlockFlow::markSiblingsWithFloatsForLayout): Change for (siblings) for (set items) to for (set items) for (siblings) so that the 'for (siblings)' logic can be moved to a lambda and used when there's a valid incoming float. Canonical link: https://commits.webkit.org/293094@main
1 parent 2275c98 commit d9617b1

File tree

3 files changed

+65
-15
lines changed

3 files changed

+65
-15
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
PASS if no crash or assert.
2+
A
3+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<style>
2+
body, div, span, ol, li {
3+
-webkit-column-break-inside: avoid;
4+
border-bottom-style: groove;
5+
break-after: column;
6+
}
7+
8+
#x5 {
9+
float: left;
10+
}
11+
12+
:scope {
13+
columns: 40;
14+
writing-mode: vertical-lr;
15+
}
16+
17+
:empty {
18+
float: left;
19+
list-style: url(data:image/gif;base64,++==)
20+
}
21+
</style>
22+
<script>
23+
window.testRunner?.dumpAsText();
24+
25+
function main() {
26+
document.body.offsetHeight;
27+
x1.scrollAmount = 10;
28+
x10.reportValidity();
29+
30+
document.body.offsetHeight;
31+
x57.setAttribute("reversed","");
32+
33+
document.body.offsetHeight;
34+
document.styleSheets[0].media.appendMedium("print (foobar)");
35+
}
36+
</script>
37+
<body onload="main()">
38+
<div><div> </div><span> </span></div>
39+
<marquee id="x1" scrollamount="0"> </marquee>
40+
<span id="x5"><span><div></div></span></span>
41+
<textarea id="x10" required="">
42+
</textarea>
43+
PASS if no crash or assert.<div><li> </li>A</div>
44+
<ol id="x57"> <li></li></ol>

Source/WebCore/rendering/RenderBlockFlow.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,25 +3069,28 @@ void RenderBlockFlow::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRe
30693069

30703070
void RenderBlockFlow::markSiblingsWithFloatsForLayout(RenderBox* floatToRemove)
30713071
{
3072-
if (!m_floatingObjects)
3073-
return;
3074-
3075-
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
3076-
auto end = floatingObjectSet.end();
3077-
3078-
for (RenderObject* next = nextSibling(); next; next = next->nextSibling()) {
3079-
CheckedPtr nextBlock = dynamicDowncast<RenderBlockFlow>(*next);
3080-
if (!nextBlock || (!floatToRemove && (next->isFloatingOrOutOfFlowPositioned() || nextBlock->avoidsFloats())))
3081-
continue;
3072+
ASSERT(!floatToRemove || floatToRemove->isFloating());
30823073

3083-
for (auto it = floatingObjectSet.begin(); it != end; ++it) {
3084-
RenderBox& floatingBox = (*it)->renderer();
3085-
if (floatToRemove && &floatingBox != floatToRemove)
3074+
auto markSiblingsWithIntrusiveFloatForLayoutIfApplicable = [&](auto& floatBoxToRemove) {
3075+
for (auto* nextSibling = this->nextSibling(); nextSibling; nextSibling = nextSibling->nextSibling()) {
3076+
CheckedPtr nextSiblingBlockFlow = dynamicDowncast<RenderBlockFlow>(*nextSibling);
3077+
if (!nextSiblingBlockFlow)
30863078
continue;
3087-
if (nextBlock->containsFloat(floatingBox))
3088-
nextBlock->markAllDescendantsWithFloatsForLayout(&floatingBox);
3079+
if (nextSiblingBlockFlow->containsFloat(floatBoxToRemove))
3080+
nextSiblingBlockFlow->markAllDescendantsWithFloatsForLayout(&floatBoxToRemove);
30893081
}
3082+
};
3083+
3084+
if (floatToRemove) {
3085+
markSiblingsWithIntrusiveFloatForLayoutIfApplicable(*floatToRemove);
3086+
return;
30903087
}
3088+
3089+
if (!m_floatingObjects)
3090+
return;
3091+
3092+
for (auto& floatingObject : m_floatingObjects->set())
3093+
markSiblingsWithIntrusiveFloatForLayoutIfApplicable(floatingObject->renderer());
30913094
}
30923095

30933096
LayoutPoint RenderBlockFlow::flipFloatForWritingModeForChild(const FloatingObject& child, const LayoutPoint& point) const

0 commit comments

Comments
 (0)