- Revision
- 281424
- Author
- [email protected]
- Date
- 2021-08-22 17:03:10 -0700 (Sun, 22 Aug 2021)
Log Message
[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.
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (281423 => 281424)
--- trunk/LayoutTests/ChangeLog 2021-08-22 23:45:54 UTC (rev 281423)
+++ trunk/LayoutTests/ChangeLog 2021-08-23 00:03:10 UTC (rev 281424)
@@ -1,5 +1,15 @@
2021-08-22 Alan Bujtas <[email protected]>
+ [LFC][IFC] Add support for out-of-flow box static positioning
+ https://bugs.webkit.org/show_bug.cgi?id=229103
+
+ Reviewed by Antti Koivisto.
+
+ * 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.
+
+2021-08-22 Alan Bujtas <[email protected]>
+
[LFC][IFC] Add support for vertical-align: sub
https://bugs.webkit.org/show_bug.cgi?id=228217
Modified: trunk/LayoutTests/TestExpectations (281423 => 281424)
--- trunk/LayoutTests/TestExpectations 2021-08-22 23:45:54 UTC (rev 281423)
+++ trunk/LayoutTests/TestExpectations 2021-08-23 00:03:10 UTC (rev 281424)
@@ -5031,6 +5031,7 @@
webkit.org/b/226002 fast/layoutformattingcontext/table-simple-row-height.html [ Skip ]
webkit.org/b/226364 fast/layoutformattingcontext/table-with-percent-columns-and-spacing.html [ Skip ]
+fast/layoutformattingcontext/absolute-positioned-box-with-inline-sibling.html [ Skip ]
[ Debug ] fast/layoutformattingcontext/table-fixed-width-with-max-distribution.html [ Skip ]
[ Debug ] fast/layoutformattingcontext/table-space-distribution-simple-mismatching.html [ Skip ]
Added: trunk/LayoutTests/fast/inline/out-of-flow-with-static-position-in-ifc-expected.html (0 => 281424)
--- trunk/LayoutTests/fast/inline/out-of-flow-with-static-position-in-ifc-expected.html (rev 0)
+++ trunk/LayoutTests/fast/inline/out-of-flow-with-static-position-in-ifc-expected.html 2021-08-23 00:03:10 UTC (rev 281424)
@@ -0,0 +1,44 @@
+<style>
+body {
+ margin: 0px;
+ font-family: Ahem;
+}
+
+div {
+ line-height: 40px;
+}
+
+img {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+ background-color: green;
+}
+</style>
+<!-- out-of-flow boxes with static positioning in IFC -->
+<div>text</div>
+<div>text</div>
+<div>text</div>
+<div>text</div>
+<div><span></span>text</div>
+<div><span><span></span></span>text</div>
+<div><span>text</span>text</div>
+<div><span>text</span>text</div>
+<div><br></div>
+<div><br>text</div>
+<div><br><br></div>
+<div>text<br></div>
+<div>text<br></div>
+<img style="left: 0px; top: 0px;">
+<img style="left: 0px; top: 40px;">
+<img style="left: 64px; top: 80px;">
+<img style="left: 0px; top: 120px;"><img style="left: 64px; top: 120px;">
+<img style="left: 0px; top: 160px;"><img style="left: 64px; top: 160px;">
+<img style="left: 0px; top: 200px;"><img style="left: 64px; top: 200px;">
+<img style="left: 64px; top: 240px;"><img style="left: 128px; top: 240px;">
+<img style="left: 64px; top: 280px;"><img style="left: 128px; top: 280px;">
+<img style="left: 0px; top: 360px;">
+<img style="left: 64px; top: 400px;">
+<img style="left: 0px; top: 520px;">
+<img style="left: 0px; top: 560px;">
+<img style="left: 0px; top: 600px;">
Added: trunk/LayoutTests/fast/inline/out-of-flow-with-static-position-in-ifc.html (0 => 281424)
--- trunk/LayoutTests/fast/inline/out-of-flow-with-static-position-in-ifc.html (rev 0)
+++ trunk/LayoutTests/fast/inline/out-of-flow-with-static-position-in-ifc.html 2021-08-23 00:03:10 UTC (rev 281424)
@@ -0,0 +1,31 @@
+<style>
+body {
+ margin: 0px;
+ font-family: Ahem;
+}
+
+div {
+ line-height: 40px;
+}
+
+img {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+ background-color: green;
+}
+</style>
+<!-- out-of-flow boxes with static positioning in IFC -->
+<div><img>text</div>
+<div><img><img>text</div>
+<div>text<img></div>
+<div><img>text<img></div>
+<div><span><img></span>text<img></div>
+<div><span><span><img></span></span>text<img></div>
+<div><span>text<img></span>text<img></div>
+<div><span>text</span><img>text<img></div>
+<div><br><img></div>
+<div><br>text<img></div>
+<div><br><br><img></div>
+<div>text<br><img></div>
+<div>text<br><img><img></div>
Modified: trunk/Source/WebCore/ChangeLog (281423 => 281424)
--- trunk/Source/WebCore/ChangeLog 2021-08-22 23:45:54 UTC (rev 281423)
+++ trunk/Source/WebCore/ChangeLog 2021-08-23 00:03:10 UTC (rev 281424)
@@ -1,3 +1,27 @@
+2021-08-22 Alan Bujtas <[email protected]>
+
+ [LFC][IFC] Add support for out-of-flow box static positioning
+ https://bugs.webkit.org/show_bug.cgi?id=229103
+
+ Reviewed by Antti Koivisto.
+
+ 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:
+
2021-08-22 Myles C. Maxfield <[email protected]>
REGRESSION(r281389): canUseSimplifiedTextMeasuring() needs to match with WidthIterator::applyCSSVisibilityRules()
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp (281423 => 281424)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp 2021-08-22 23:45:54 UTC (rev 281423)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.cpp 2021-08-23 00:03:10 UTC (rev 281424)
@@ -136,10 +136,11 @@
layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
}
- collectInlineContentIfNeeded();
+ collectContentIfNeeded();
auto& inlineItems = formattingState().inlineItems();
lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
+ computeStaticPositionForOutOfFlowContent(formattingState().outOfFlowBoxes());
LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
}
@@ -146,9 +147,10 @@
void InlineFormattingContext::lineLayoutForIntergration(InvalidationState& invalidationState, const ConstraintsForInFlowContent& constraints)
{
invalidateFormattingState(invalidationState);
- collectInlineContentIfNeeded();
+ collectContentIfNeeded();
auto& inlineItems = formattingState().inlineItems();
lineLayout(inlineItems, { 0, inlineItems.size() }, constraints);
+ computeStaticPositionForOutOfFlowContent(formattingState().outOfFlowBoxes());
}
LayoutUnit InlineFormattingContext::usedContentHeight() const
@@ -253,6 +255,103 @@
}
}
+void InlineFormattingContext::computeStaticPositionForOutOfFlowContent(const FormattingState::OutOfFlowBoxList& outOfFlowBoxes)
+{
+ // This function computes 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/next sibling (or parent) box's geometry and
+ // place the out-of-flow box at the logical right position.
+ auto& formattingState = this->formattingState();
+ auto& lines = formattingState.lines();
+ auto& lineRuns = formattingState.lineRuns();
+
+ for (auto& outOfFlowBox : outOfFlowBoxes) {
+ auto& outOfFlowGeometry = formattingState.boxGeometry(*outOfFlowBox);
+ // Both previous float and out-of-flow boxes are skipped here. A series of adjoining out-of-flow boxes should all be placed
+ // at the same static position (they don't affect next-sibling positions) and while floats do participate in the inline layout
+ // their positions have already been taken into account during the inline layout.
+ auto previousContentSkippingFloats = [&]() -> const Layout::Box* {
+ auto* previousSibling = outOfFlowBox->previousSibling();
+ for (; previousSibling && previousSibling->isFloatingPositioned(); previousSibling = previousSibling->previousSibling()) { }
+ if (previousSibling)
+ return previousSibling;
+ // Parent is either the root here or another inline box (e.g. <span><img style="position: absolute"></span>)
+ auto& parent = outOfFlowBox->parent();
+ return &parent == &root() ? nullptr : &parent;
+ }();
+
+ if (!previousContentSkippingFloats) {
+ // This is the first (non-float)child. Let's place it to the left of the first run.
+ // <div><img style="position: absolute">text content</div>
+ ASSERT(lineRuns.size());
+ outOfFlowGeometry.setLogicalTopLeft({ lineRuns[0].logicalLeft(), lines[0].lineBoxLogicalRect().top() });
+ continue;
+ }
+
+ if (previousContentSkippingFloats->isOutOfFlowPositioned()) {
+ // Subsequent out-of-flow positioned boxes share the same static position.
+ // <div>text content<img style="position: absolute"><img style="position: absolute"></div>
+ outOfFlowGeometry.setLogicalTopLeft(BoxGeometry::borderBoxTopLeft(geometryForBox(*previousContentSkippingFloats)));
+ continue;
+ }
+
+ ASSERT(previousContentSkippingFloats->isInFlow());
+ auto placeOutOfFlowBoxAfterPreviousInFlowBox = [&] {
+ // The out-of-flow box should be placed after this inflow box.
+ // 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.
+ size_t lastRunIndexOnPreviousLayoutBox = 0;
+ for (; lastRunIndexOnPreviousLayoutBox < lineRuns.size() && &lineRuns[lastRunIndexOnPreviousLayoutBox].layoutBox() != previousContentSkippingFloats; ++lastRunIndexOnPreviousLayoutBox) { }
+ if (lastRunIndexOnPreviousLayoutBox == lineRuns.size()) {
+ // FIXME: In very rare cases, the previous box's content might have been completely collapsed and left us with no run.
+ ASSERT_NOT_IMPLEMENTED_YET();
+ return;
+ }
+ for (; lastRunIndexOnPreviousLayoutBox < lineRuns.size() && &lineRuns[lastRunIndexOnPreviousLayoutBox].layoutBox() == previousContentSkippingFloats; ++lastRunIndexOnPreviousLayoutBox) { }
+ --lastRunIndexOnPreviousLayoutBox;
+ // Let's check if the previous run is the last run on the current line and use the next run's left instead.
+ auto& previousRun = lineRuns[lastRunIndexOnPreviousLayoutBox];
+ auto* nextRun = lastRunIndexOnPreviousLayoutBox + 1 < lineRuns.size() ? &lineRuns[lastRunIndexOnPreviousLayoutBox + 1] : nullptr;
+
+ if (nextRun && nextRun->lineIndex() == previousRun.lineIndex()) {
+ // Previous and next runs are on the same line. The out-of-flow box is right at the previous run's logical right.
+ // <div>text<img style="position: absolute">content</div>
+ auto logicalLeft = previousRun.logicalRight();
+ if (previousContentSkippingFloats->isInlineBox() && !previousContentSkippingFloats->isAnonymous()) {
+ // <div>text<span><img style="position: absolute">content</span></div>
+ // or
+ // <div>text<span>content</span><img style="position: absolute"></div>
+ auto& inlineBoxBoxGeometry = geometryForBox(*previousContentSkippingFloats);
+ logicalLeft = previousContentSkippingFloats == &outOfFlowBox->parent()
+ ? BoxGeometry::borderBoxLeft(inlineBoxBoxGeometry) + inlineBoxBoxGeometry.contentBoxLeft()
+ : BoxGeometry::borderBoxRect(inlineBoxBoxGeometry).right();
+ }
+ outOfFlowGeometry.setLogicalTopLeft({ logicalLeft, lines[previousRun.lineIndex()].lineBoxLogicalRect().top() });
+ return;
+ }
+
+ if (nextRun) {
+ // The out of flow box is placed at the beginning of the next line (where the first run on the line is).
+ // <div>text<br><img style="position: absolute"><img style="position: absolute">content</div>
+ outOfFlowGeometry.setLogicalTopLeft({ nextRun->logicalLeft(), lines[nextRun->lineIndex()].lineBoxLogicalRect().top() });
+ return;
+ }
+
+ auto& lastLineLogicalRect = lines[previousRun.lineIndex()].lineBoxLogicalRect();
+ // This out-of-flow box is the last box.
+ // FIXME: Use isLineBreak instead to cover preserved new lines too.
+ if (previousRun.layoutBox().isLineBreakBox()) {
+ // <div>text<br><img style="position: absolute"><img style="position: absolute"></div>
+ outOfFlowGeometry.setLogicalTopLeft({ lastLineLogicalRect.left(), lastLineLogicalRect.bottom() });
+ return;
+ }
+ // FIXME: We may need to check if this box actually fits the last line and move it over to the "next" line.
+ outOfFlowGeometry.setLogicalTopLeft({ previousRun.logicalRight(), lastLineLogicalRect.top() });
+ };
+ placeOutOfFlowBoxAfterPreviousInFlowBox();
+ }
+}
+
IntrinsicWidthConstraints InlineFormattingContext::computedIntrinsicWidthConstraints()
{
auto& layoutState = this->layoutState();
@@ -291,7 +390,7 @@
layoutBox = nextInlineLevelBoxToLayout(*layoutBox, root());
}
- collectInlineContentIfNeeded();
+ collectContentIfNeeded();
auto maximumLineWidth = [&](auto availableWidth) {
// Switch to the min/max formatting root width values before formatting the lines.
@@ -415,7 +514,7 @@
boxGeometry.setVerticalMargin({ contentHeightAndMargin.nonCollapsedMargin.before, contentHeightAndMargin.nonCollapsedMargin.after });
}
-void InlineFormattingContext::collectInlineContentIfNeeded()
+void InlineFormattingContext::collectContentIfNeeded()
{
auto& formattingState = this->formattingState();
if (!formattingState.inlineItems().isEmpty())
@@ -424,24 +523,28 @@
// <span>text<span></span><img></span> -> [InlineBoxStart][InlineLevelBox][InlineBoxStart][InlineBoxEnd][InlineLevelBox][InlineBoxEnd]
ASSERT(root().hasInFlowOrFloatingChild());
LayoutQueue layoutQueue;
- layoutQueue.append(root().firstInFlowOrFloatingChild());
+ layoutQueue.append(root().firstChild());
while (!layoutQueue.isEmpty()) {
while (true) {
auto& layoutBox = *layoutQueue.last();
- auto isBoxWithInlineContent = layoutBox.isInlineBox() && !layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox();
- if (!isBoxWithInlineContent)
+ auto isInlineBoxWithInlineContent = layoutBox.isInlineBox() && !layoutBox.isInlineTextBox() && !layoutBox.isLineBreakBox() && !layoutBox.isOutOfFlowPositioned();
+ if (!isInlineBoxWithInlineContent)
break;
// This is the start of an inline box (e.g. <span>).
formattingState.addInlineItem({ layoutBox, InlineItem::Type::InlineBoxStart });
auto& inlineBoxWithInlineContent = downcast<ContainerBox>(layoutBox);
- if (!inlineBoxWithInlineContent.hasInFlowOrFloatingChild())
+ if (!inlineBoxWithInlineContent.hasChild())
break;
- layoutQueue.append(inlineBoxWithInlineContent.firstInFlowOrFloatingChild());
+ layoutQueue.append(inlineBoxWithInlineContent.firstChild());
}
while (!layoutQueue.isEmpty()) {
auto& layoutBox = *layoutQueue.takeLast();
- if (is<LineBreakBox>(layoutBox)) {
+ if (layoutBox.isOutOfFlowPositioned()) {
+ // Let's not construct InlineItems for out-of-flow content as they don't participate in the inline layout.
+ // However to be able to static positioning them, we need to compute their approximate positions.
+ formattingState.addOutOfFlowBox(layoutBox);
+ } else if (is<LineBreakBox>(layoutBox)) {
auto& lineBreakBox = downcast<LineBreakBox>(layoutBox);
formattingState.addInlineItem({ layoutBox, lineBreakBox.isOptional() ? InlineItem::Type::WordBreakOpportunity : InlineItem::Type::HardLineBreak });
} else if (layoutBox.isFloatingPositioned())
@@ -455,7 +558,7 @@
else
ASSERT_NOT_REACHED();
- if (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling()) {
+ if (auto* nextSibling = layoutBox.nextSibling()) {
layoutQueue.append(nextSibling);
break;
}
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h (281423 => 281424)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h 2021-08-22 23:45:54 UTC (rev 281423)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingContext.h 2021-08-23 00:03:10 UTC (rev 281424)
@@ -28,6 +28,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "FormattingContext.h"
+#include "FormattingState.h"
#include "InlineFormattingGeometry.h"
#include "InlineFormattingQuirks.h"
#include "InlineLineBuilder.h"
@@ -61,6 +62,7 @@
IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
void lineLayout(InlineItems&, LineBuilder::InlineItemRange, const ConstraintsForInFlowContent&);
+ void computeStaticPositionForOutOfFlowContent(const FormattingState::OutOfFlowBoxList&);
void computeIntrinsicWidthForFormattingRoot(const Box&);
InlineLayoutUnit computedIntrinsicWidthForConstraint(InlineLayoutUnit availableWidth) const;
@@ -69,7 +71,7 @@
void computeHeightAndMargin(const Box&, const HorizontalConstraints&);
void computeWidthAndMargin(const Box&, const HorizontalConstraints&);
- void collectInlineContentIfNeeded();
+ void collectContentIfNeeded();
InlineRect computeGeometryForLineContent(const LineBuilder::LineContent&, const HorizontalConstraints&);
void invalidateFormattingState(const InvalidationState&);