Skip to content

Commit cf77854

Browse files
committed
[LFC][IFC] Add support for out-of-flow box static positioning
https://bugs.webkit.org/show_bug.cgi?id=229103 Reviewed by Antti Koivisto. Source/WebCore: This patch is in preparation for enabling positioned content for IFC handling. In this patch we compute the static position for out-of-flow content inside the inline formatting context. As per spec, the static position of an out-of-flow box is computed as if the position was set to static. However it does not mean that the out-of-flow box should be involved in the inline layout process. Instead we figure out this static position after the inline layout by looking at the previous sibling (or parent) box's geometry and place the out-of-flow box at the logical right position. * layout/formattingContexts/inline/InlineFormattingContext.cpp: (WebCore::Layout::InlineFormattingContext::layoutInFlowContent): (WebCore::Layout::InlineFormattingContext::lineLayoutForIntergration): (WebCore::Layout::InlineFormattingContext::computeStaticPositionForOutOfFlowContent): (WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthConstraints): (WebCore::Layout::InlineFormattingContext::collectContentIfNeeded): (WebCore::Layout::InlineFormattingContext::collectInlineContentIfNeeded): Deleted. * layout/formattingContexts/inline/InlineFormattingContext.h: LayoutTests: * fast/inline/out-of-flow-with-static-position-in-ifc-expected.html: Added. * fast/inline/out-of-flow-with-static-position-in-ifc.html: Added. Canonical link: https://commits.webkit.org/240814@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@281424 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent b9f401b commit cf77854

7 files changed

Lines changed: 227 additions & 12 deletions

File tree

LayoutTests/ChangeLog

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
2021-08-22 Alan Bujtas <zalan@apple.com>
2+
3+
[LFC][IFC] Add support for out-of-flow box static positioning
4+
https://bugs.webkit.org/show_bug.cgi?id=229103
5+
6+
Reviewed by Antti Koivisto.
7+
8+
* fast/inline/out-of-flow-with-static-position-in-ifc-expected.html: Added.
9+
* fast/inline/out-of-flow-with-static-position-in-ifc.html: Added.
10+
111
2021-08-22 Alan Bujtas <zalan@apple.com>
212

313
[LFC][IFC] Add support for vertical-align: sub

LayoutTests/TestExpectations

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5031,6 +5031,7 @@ webkit.org/b/225247 [ Debug ] fast/multicol/span/float-becomes-spanner-crash.htm
50315031

50325032
webkit.org/b/226002 fast/layoutformattingcontext/table-simple-row-height.html [ Skip ]
50335033
webkit.org/b/226364 fast/layoutformattingcontext/table-with-percent-columns-and-spacing.html [ Skip ]
5034+
fast/layoutformattingcontext/absolute-positioned-box-with-inline-sibling.html [ Skip ]
50345035
[ Debug ] fast/layoutformattingcontext/table-fixed-width-with-max-distribution.html [ Skip ]
50355036
[ Debug ] fast/layoutformattingcontext/table-space-distribution-simple-mismatching.html [ Skip ]
50365037

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<style>
2+
body {
3+
margin: 0px;
4+
font-family: Ahem;
5+
}
6+
7+
div {
8+
line-height: 40px;
9+
}
10+
11+
img {
12+
position: absolute;
13+
width: 10px;
14+
height: 10px;
15+
background-color: green;
16+
}
17+
</style>
18+
<!-- out-of-flow boxes with static positioning in IFC -->
19+
<div>text</div>
20+
<div>text</div>
21+
<div>text</div>
22+
<div>text</div>
23+
<div><span></span>text</div>
24+
<div><span><span></span></span>text</div>
25+
<div><span>text</span>text</div>
26+
<div><span>text</span>text</div>
27+
<div><br></div>
28+
<div><br>text</div>
29+
<div><br><br></div>
30+
<div>text<br></div>
31+
<div>text<br></div>
32+
<img style="left: 0px; top: 0px;">
33+
<img style="left: 0px; top: 40px;">
34+
<img style="left: 64px; top: 80px;">
35+
<img style="left: 0px; top: 120px;"><img style="left: 64px; top: 120px;">
36+
<img style="left: 0px; top: 160px;"><img style="left: 64px; top: 160px;">
37+
<img style="left: 0px; top: 200px;"><img style="left: 64px; top: 200px;">
38+
<img style="left: 64px; top: 240px;"><img style="left: 128px; top: 240px;">
39+
<img style="left: 64px; top: 280px;"><img style="left: 128px; top: 280px;">
40+
<img style="left: 0px; top: 360px;">
41+
<img style="left: 64px; top: 400px;">
42+
<img style="left: 0px; top: 520px;">
43+
<img style="left: 0px; top: 560px;">
44+
<img style="left: 0px; top: 600px;">
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<style>
2+
body {
3+
margin: 0px;
4+
font-family: Ahem;
5+
}
6+
7+
div {
8+
line-height: 40px;
9+
}
10+
11+
img {
12+
position: absolute;
13+
width: 10px;
14+
height: 10px;
15+
background-color: green;
16+
}
17+
</style>
18+
<!-- out-of-flow boxes with static positioning in IFC -->
19+
<div><img>text</div>
20+
<div><img><img>text</div>
21+
<div>text<img></div>
22+
<div><img>text<img></div>
23+
<div><span><img></span>text<img></div>
24+
<div><span><span><img></span></span>text<img></div>
25+
<div><span>text<img></span>text<img></div>
26+
<div><span>text</span><img>text<img></div>
27+
<div><br><img></div>
28+
<div><br>text<img></div>
29+
<div><br><br><img></div>
30+
<div>text<br><img></div>
31+
<div>text<br><img><img></div>

Source/WebCore/ChangeLog

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
2021-08-22 Alan Bujtas <zalan@apple.com>
2+
3+
[LFC][IFC] Add support for out-of-flow box static positioning
4+
https://bugs.webkit.org/show_bug.cgi?id=229103
5+
6+
Reviewed by Antti Koivisto.
7+
8+
This patch is in preparation for enabling positioned content for IFC handling.
9+
10+
In this patch we compute the static position for out-of-flow content inside the inline formatting context.
11+
As per spec, the static position of an out-of-flow box is computed as if the position was set to static.
12+
However it does not mean that the out-of-flow box should be involved in the inline layout process.
13+
Instead we figure out this static position after the inline layout by looking at
14+
the previous sibling (or parent) box's geometry and place the out-of-flow box at the logical right position.
15+
16+
* layout/formattingContexts/inline/InlineFormattingContext.cpp:
17+
(WebCore::Layout::InlineFormattingContext::layoutInFlowContent):
18+
(WebCore::Layout::InlineFormattingContext::lineLayoutForIntergration):
19+
(WebCore::Layout::InlineFormattingContext::computeStaticPositionForOutOfFlowContent):
20+
(WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthConstraints):
21+
(WebCore::Layout::InlineFormattingContext::collectContentIfNeeded):
22+
(WebCore::Layout::InlineFormattingContext::collectInlineContentIfNeeded): Deleted.
23+
* layout/formattingContexts/inline/InlineFormattingContext.h:
24+
125
2021-08-22 Myles C. Maxfield <mmaxfield@apple.com>
226

327
REGRESSION(r281389): canUseSimplifiedTextMeasuring() needs to match with WidthIterator::applyCSSVisibilityRules()

Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp

Lines changed: 114 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,19 +136,21 @@ void InlineFormattingContext::layoutInFlowContent(InvalidationState& invalidatio
136136
layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
137137
}
138138

139-
collectInlineContentIfNeeded();
139+
collectContentIfNeeded();
140140

141141
auto& inlineItems = formattingState().inlineItems();
142142
lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
143+
computeStaticPositionForOutOfFlowContent(formattingState().outOfFlowBoxes());
143144
LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
144145
}
145146

146147
void InlineFormattingContext::lineLayoutForIntergration(InvalidationState& invalidationState, const ConstraintsForInFlowContent& constraints)
147148
{
148149
invalidateFormattingState(invalidationState);
149-
collectInlineContentIfNeeded();
150+
collectContentIfNeeded();
150151
auto& inlineItems = formattingState().inlineItems();
151152
lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
153+
computeStaticPositionForOutOfFlowContent(formattingState().outOfFlowBoxes());
152154
}
153155

154156
LayoutUnit InlineFormattingContext::usedContentHeight() const
@@ -253,6 +255,103 @@ void InlineFormattingContext::lineLayout(InlineItems& inlineItems, LineBuilder::
253255
}
254256
}
255257

258+
void InlineFormattingContext::computeStaticPositionForOutOfFlowContent(const FormattingState::OutOfFlowBoxList& outOfFlowBoxes)
259+
{
260+
// This function computes the static position for out-of-flow content inside the inline formatting context.
261+
// As per spec, the static position of an out-of-flow box is computed as if the position was set to static.
262+
// However it does not mean that the out-of-flow box should be involved in the inline layout process.
263+
// Instead we figure out this static position after the inline layout by looking at the previous/next sibling (or parent) box's geometry and
264+
// place the out-of-flow box at the logical right position.
265+
auto& formattingState = this->formattingState();
266+
auto& lines = formattingState.lines();
267+
auto& lineRuns = formattingState.lineRuns();
268+
269+
for (auto& outOfFlowBox : outOfFlowBoxes) {
270+
auto& outOfFlowGeometry = formattingState.boxGeometry(*outOfFlowBox);
271+
// Both previous float and out-of-flow boxes are skipped here. A series of adjoining out-of-flow boxes should all be placed
272+
// at the same static position (they don't affect next-sibling positions) and while floats do participate in the inline layout
273+
// their positions have already been taken into account during the inline layout.
274+
auto previousContentSkippingFloats = [&]() -> const Layout::Box* {
275+
auto* previousSibling = outOfFlowBox->previousSibling();
276+
for (; previousSibling && previousSibling->isFloatingPositioned(); previousSibling = previousSibling->previousSibling()) { }
277+
if (previousSibling)
278+
return previousSibling;
279+
// Parent is either the root here or another inline box (e.g. <span><img style="position: absolute"></span>)
280+
auto& parent = outOfFlowBox->parent();
281+
return &parent == &root() ? nullptr : &parent;
282+
}();
283+
284+
if (!previousContentSkippingFloats) {
285+
// This is the first (non-float)child. Let's place it to the left of the first run.
286+
// <div><img style="position: absolute">text content</div>
287+
ASSERT(lineRuns.size());
288+
outOfFlowGeometry.setLogicalTopLeft({ lineRuns[0].logicalLeft(), lines[0].lineBoxLogicalRect().top() });
289+
continue;
290+
}
291+
292+
if (previousContentSkippingFloats->isOutOfFlowPositioned()) {
293+
// Subsequent out-of-flow positioned boxes share the same static position.
294+
// <div>text content<img style="position: absolute"><img style="position: absolute"></div>
295+
outOfFlowGeometry.setLogicalTopLeft(BoxGeometry::borderBoxTopLeft(geometryForBox(*previousContentSkippingFloats)));
296+
continue;
297+
}
298+
299+
ASSERT(previousContentSkippingFloats->isInFlow());
300+
auto placeOutOfFlowBoxAfterPreviousInFlowBox = [&] {
301+
// The out-of-flow box should be placed after this inflow box.
302+
// Skip to the last run of this layout box. The last run's geometry is used to compute the out-of-flow box's static position.
303+
size_t lastRunIndexOnPreviousLayoutBox = 0;
304+
for (; lastRunIndexOnPreviousLayoutBox < lineRuns.size() && &lineRuns[lastRunIndexOnPreviousLayoutBox].layoutBox() != previousContentSkippingFloats; ++lastRunIndexOnPreviousLayoutBox) { }
305+
if (lastRunIndexOnPreviousLayoutBox == lineRuns.size()) {
306+
// FIXME: In very rare cases, the previous box's content might have been completely collapsed and left us with no run.
307+
ASSERT_NOT_IMPLEMENTED_YET();
308+
return;
309+
}
310+
for (; lastRunIndexOnPreviousLayoutBox < lineRuns.size() && &lineRuns[lastRunIndexOnPreviousLayoutBox].layoutBox() == previousContentSkippingFloats; ++lastRunIndexOnPreviousLayoutBox) { }
311+
--lastRunIndexOnPreviousLayoutBox;
312+
// Let's check if the previous run is the last run on the current line and use the next run's left instead.
313+
auto& previousRun = lineRuns[lastRunIndexOnPreviousLayoutBox];
314+
auto* nextRun = lastRunIndexOnPreviousLayoutBox + 1 < lineRuns.size() ? &lineRuns[lastRunIndexOnPreviousLayoutBox + 1] : nullptr;
315+
316+
if (nextRun && nextRun->lineIndex() == previousRun.lineIndex()) {
317+
// Previous and next runs are on the same line. The out-of-flow box is right at the previous run's logical right.
318+
// <div>text<img style="position: absolute">content</div>
319+
auto logicalLeft = previousRun.logicalRight();
320+
if (previousContentSkippingFloats->isInlineBox() && !previousContentSkippingFloats->isAnonymous()) {
321+
// <div>text<span><img style="position: absolute">content</span></div>
322+
// or
323+
// <div>text<span>content</span><img style="position: absolute"></div>
324+
auto& inlineBoxBoxGeometry = geometryForBox(*previousContentSkippingFloats);
325+
logicalLeft = previousContentSkippingFloats == &outOfFlowBox->parent()
326+
? BoxGeometry::borderBoxLeft(inlineBoxBoxGeometry) + inlineBoxBoxGeometry.contentBoxLeft()
327+
: BoxGeometry::borderBoxRect(inlineBoxBoxGeometry).right();
328+
}
329+
outOfFlowGeometry.setLogicalTopLeft({ logicalLeft, lines[previousRun.lineIndex()].lineBoxLogicalRect().top() });
330+
return;
331+
}
332+
333+
if (nextRun) {
334+
// The out of flow box is placed at the beginning of the next line (where the first run on the line is).
335+
// <div>text<br><img style="position: absolute"><img style="position: absolute">content</div>
336+
outOfFlowGeometry.setLogicalTopLeft({ nextRun->logicalLeft(), lines[nextRun->lineIndex()].lineBoxLogicalRect().top() });
337+
return;
338+
}
339+
340+
auto& lastLineLogicalRect = lines[previousRun.lineIndex()].lineBoxLogicalRect();
341+
// This out-of-flow box is the last box.
342+
// FIXME: Use isLineBreak instead to cover preserved new lines too.
343+
if (previousRun.layoutBox().isLineBreakBox()) {
344+
// <div>text<br><img style="position: absolute"><img style="position: absolute"></div>
345+
outOfFlowGeometry.setLogicalTopLeft({ lastLineLogicalRect.left(), lastLineLogicalRect.bottom() });
346+
return;
347+
}
348+
// FIXME: We may need to check if this box actually fits the last line and move it over to the "next" line.
349+
outOfFlowGeometry.setLogicalTopLeft({ previousRun.logicalRight(), lastLineLogicalRect.top() });
350+
};
351+
placeOutOfFlowBoxAfterPreviousInFlowBox();
352+
}
353+
}
354+
256355
IntrinsicWidthConstraints InlineFormattingContext::computedIntrinsicWidthConstraints()
257356
{
258357
auto& layoutState = this->layoutState();
@@ -291,7 +390,7 @@ IntrinsicWidthConstraints InlineFormattingContext::computedIntrinsicWidthConstra
291390
layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
292391
}
293392

294-
collectInlineContentIfNeeded();
393+
collectContentIfNeeded();
295394

296395
auto maximumLineWidth = [&](auto availableWidth) {
297396
// Switch to the min/max formatting root width values before formatting the lines.
@@ -415,7 +514,7 @@ void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox, const
415514
boxGeometry.setVerticalMargin({ contentHeightAndMargin.nonCollapsedMargin.before, contentHeightAndMargin.nonCollapsedMargin.after });
416515
}
417516

418-
void InlineFormattingContext::collectInlineContentIfNeeded()
517+
void InlineFormattingContext::collectContentIfNeeded()
419518
{
420519
auto& formattingState = this->formattingState();
421520
if (!formattingState.inlineItems().isEmpty())
@@ -424,24 +523,28 @@ void InlineFormattingContext::collectInlineContentIfNeeded()
424523
// <span>text<span></span><img></span> -> [InlineBoxStart][InlineLevelBox][InlineBoxStart][InlineBoxEnd][InlineLevelBox][InlineBoxEnd]
425524
ASSERT(root().hasInFlowOrFloatingChild());
426525
LayoutQueue layoutQueue;
427-
layoutQueue.append(root().firstInFlowOrFloatingChild());
526+
layoutQueue.append(root().firstChild());
428527
while (!layoutQueue.isEmpty()) {
429528
while (true) {
430529
auto& layoutBox = *layoutQueue.last();
431-
auto isBoxWithInlineContent = layoutBox.isInlineBox() && !layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox();
432-
if (!isBoxWithInlineContent)
530+
auto isInlineBoxWithInlineContent = layoutBox.isInlineBox() && !layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox() && !layoutBox.isOutOfFlowPositioned();
531+
if (!isInlineBoxWithInlineContent)
433532
break;
434533
// This is the start of an inline box (e.g. <span>).
435534
formattingState.addInlineItem({ layoutBox, InlineItem::Type::InlineBoxStart });
436535
auto& inlineBoxWithInlineContent = downcast<ContainerBox>(layoutBox);
437-
if (!inlineBoxWithInlineContent.hasInFlowOrFloatingChild())
536+
if (!inlineBoxWithInlineContent.hasChild())
438537
break;
439-
layoutQueue.append(inlineBoxWithInlineContent.firstInFlowOrFloatingChild());
538+
layoutQueue.append(inlineBoxWithInlineContent.firstChild());
440539
}
441540

442541
while (!layoutQueue.isEmpty()) {
443542
auto& layoutBox = *layoutQueue.takeLast();
444-
if (is<LineBreakBox>(layoutBox)) {
543+
if (layoutBox.isOutOfFlowPositioned()) {
544+
// Let's not construct InlineItems for out-of-flow content as they don't participate in the inline layout.
545+
// However to be able to static positioning them, we need to compute their approximate positions.
546+
formattingState.addOutOfFlowBox(layoutBox);
547+
} else if (is<LineBreakBox>(layoutBox)) {
445548
auto& lineBreakBox = downcast<LineBreakBox>(layoutBox);
446549
formattingState.addInlineItem({ layoutBox, lineBreakBox.isOptional() ? InlineItem::Type::WordBreakOpportunity : InlineItem::Type::HardLineBreak });
447550
} else if (layoutBox.isFloatingPositioned())
@@ -455,7 +558,7 @@ void InlineFormattingContext::collectInlineContentIfNeeded()
455558
else
456559
ASSERT_NOT_REACHED();
457560

458-
if (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling()) {
561+
if (auto* nextSibling = layoutBox.nextSibling()) {
459562
layoutQueue.append(nextSibling);
460563
break;
461564
}

Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
2929

3030
#include "FormattingContext.h"
31+
#include "FormattingState.h"
3132
#include "InlineFormattingGeometry.h"
3233
#include "InlineFormattingQuirks.h"
3334
#include "InlineLineBuilder.h"
@@ -61,6 +62,7 @@ class InlineFormattingContext final : public FormattingContext {
6162
IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
6263

6364
void lineLayout(InlineItems&, LineBuilder::InlineItemRange, const ConstraintsForInFlowContent&);
65+
void computeStaticPositionForOutOfFlowContent(const FormattingState::OutOfFlowBoxList&);
6466

6567
void computeIntrinsicWidthForFormattingRoot(const Box&);
6668
InlineLayoutUnit computedIntrinsicWidthForConstraint(InlineLayoutUnit availableWidth) const;
@@ -69,7 +71,7 @@ class InlineFormattingContext final : public FormattingContext {
6971
void computeHeightAndMargin(const Box&, const HorizontalConstraints&);
7072
void computeWidthAndMargin(const Box&, const HorizontalConstraints&);
7173

72-
void collectInlineContentIfNeeded();
74+
void collectContentIfNeeded();
7375
InlineRect computeGeometryForLineContent(const LineBuilder::LineContent&, const HorizontalConstraints&);
7476
void invalidateFormattingState(const InvalidationState&);
7577

0 commit comments

Comments
 (0)