Title: [266680] trunk/Source/WebCore
Revision
266680
Author
[email protected]
Date
2020-09-06 08:59:35 -0700 (Sun, 06 Sep 2020)

Log Message

[LFC][IFC] Move Line handing to LineBuilder
https://bugs.webkit.org/show_bug.cgi?id=216200

Reviewed by Antti Koivisto.

1. LineBuilder owns Line now.
2. LineBuilder can build line for both layout and intrinsic width computation (now these are two distinct code paths).
3. LineBuilder also constructs the LineBox when needed (This was previously in InlineFormattingContext::lineLayout).

* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::lineLayout):
(WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthForConstraint const):
(WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
(WebCore::Layout::InlineFormattingContext::constraintsForLine): Deleted.
* layout/inlineformatting/InlineFormattingContext.h:
* layout/inlineformatting/InlineFormattingContextGeometry.cpp:
(WebCore::Layout::InlineFormattingContext::Geometry::computedTextIndent const): Deleted.
* layout/inlineformatting/InlineLineBox.cpp:
(WebCore::Layout::LineBox::formattingContext const):
(WebCore::Layout::LineBox::root const):
(WebCore::Layout::LineBox::layoutState const):
* layout/inlineformatting/InlineLineBox.h:
(WebCore::Layout::LineBox::contentLogicalWidth const):
(WebCore::Layout::LineBox::formattingContext const): Deleted.
(WebCore::Layout::LineBox::root const): Deleted.
(WebCore::Layout::LineBox::layoutState const): Deleted.
* layout/inlineformatting/InlineLineBuilder.cpp:
(WebCore::Layout::LineBuilder::LineBuilder):
(WebCore::Layout::LineBuilder::layoutInlineContent):
(WebCore::Layout::LineBuilder::initialize):
(WebCore::Layout::LineBuilder::close):
(WebCore::Layout::LineBuilder::constraintsForLine):
(WebCore::Layout::LineBuilder::commitFloats):
(WebCore::Layout::LineBuilder::handleFloatsAndInlineContent):
(WebCore::Layout::LineBuilder::commitPartialContent):
(WebCore::Layout::LineBuilder::rebuildLine):
(WebCore::Layout::LineBuilder::layoutState const):
* layout/inlineformatting/InlineLineBuilder.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (266679 => 266680)


--- trunk/Source/WebCore/ChangeLog	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/ChangeLog	2020-09-06 15:59:35 UTC (rev 266680)
@@ -1,3 +1,44 @@
+2020-09-06  Zalan Bujtas  <[email protected]>
+
+        [LFC][IFC] Move Line handing to LineBuilder
+        https://bugs.webkit.org/show_bug.cgi?id=216200
+
+        Reviewed by Antti Koivisto.
+
+        1. LineBuilder owns Line now.
+        2. LineBuilder can build line for both layout and intrinsic width computation (now these are two distinct code paths).
+        3. LineBuilder also constructs the LineBox when needed (This was previously in InlineFormattingContext::lineLayout).
+
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::lineLayout):
+        (WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthForConstraint const):
+        (WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
+        (WebCore::Layout::InlineFormattingContext::constraintsForLine): Deleted.
+        * layout/inlineformatting/InlineFormattingContext.h:
+        * layout/inlineformatting/InlineFormattingContextGeometry.cpp:
+        (WebCore::Layout::InlineFormattingContext::Geometry::computedTextIndent const): Deleted.
+        * layout/inlineformatting/InlineLineBox.cpp:
+        (WebCore::Layout::LineBox::formattingContext const):
+        (WebCore::Layout::LineBox::root const):
+        (WebCore::Layout::LineBox::layoutState const):
+        * layout/inlineformatting/InlineLineBox.h:
+        (WebCore::Layout::LineBox::contentLogicalWidth const):
+        (WebCore::Layout::LineBox::formattingContext const): Deleted.
+        (WebCore::Layout::LineBox::root const): Deleted.
+        (WebCore::Layout::LineBox::layoutState const): Deleted.
+        * layout/inlineformatting/InlineLineBuilder.cpp:
+        (WebCore::Layout::LineBuilder::LineBuilder):
+        (WebCore::Layout::LineBuilder::layoutInlineContent):
+        (WebCore::Layout::LineBuilder::initialize):
+        (WebCore::Layout::LineBuilder::close):
+        (WebCore::Layout::LineBuilder::constraintsForLine):
+        (WebCore::Layout::LineBuilder::commitFloats):
+        (WebCore::Layout::LineBuilder::handleFloatsAndInlineContent):
+        (WebCore::Layout::LineBuilder::commitPartialContent):
+        (WebCore::Layout::LineBuilder::rebuildLine):
+        (WebCore::Layout::LineBuilder::layoutState const):
+        * layout/inlineformatting/InlineLineBuilder.h:
+
 2020-09-05  Sam Weinig  <[email protected]>
 
         [WebIDL] Add support for interface mixins

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (266679 => 266680)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2020-09-06 15:59:35 UTC (rev 266680)
@@ -140,42 +140,22 @@
         Optional<unsigned> overflowContentLength;
     };
     Optional<PreviousLine> previousLine;
-    auto line = Line { *this };
-    auto lineBuilder = LineBuilder { *this, root(), inlineItems };
+    auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
+    auto isFirstLine = !formattingState().displayInlineContent() || formattingState().displayInlineContent()->lineBoxes.isEmpty();
 
+    auto lineBuilder = LineBuilder { *this, floatingContext, root(), inlineItems };
     while (!needsLayoutRange.isEmpty()) {
-        auto lineConstraints = constraintsForLine(constraints.horizontal, lineLogicalTop);
-        line.open(lineConstraints.availableLogicalWidth);
-        line.setHasIntrusiveFloat(lineConstraints.lineIsConstrainedByFloat);
         // Turn previous line's overflow content length into the next line's leading content partial length.
         // "sp[<-line break->]lit_content" -> overflow length: 11 -> leading partial content length: 11.
         auto partialLeadingContentLength = previousLine ? previousLine->overflowContentLength : WTF::nullopt;
-        auto lineContent = lineBuilder.layoutInlineContent(line, needsLayoutRange, partialLeadingContentLength);
+        auto initialLineConstraints = ConstraintsForInFlowContent { constraints.horizontal, { lineLogicalTop, makeOptional(toLayoutUnit(quirks().initialLineHeight())) } };
+        auto lineContent = lineBuilder.layoutInlineContent(needsLayoutRange, partialLeadingContentLength, initialLineConstraints, isFirstLine);
+        setDisplayBoxesForLine(lineContent, constraints.horizontal);
         auto lineContentRange = lineContent.inlineItemRange;
-        auto isLastLineWithInlineContent = [&] {
-            if (lineContent.partialContent)
-                return false;
-            if (lineContentRange.end == needsLayoutRange.end)
-                return true;
-            // Omit floats to see if this is the last line with inline content.
-            for (auto i = needsLayoutRange.end; i--;) {
-                if (!inlineItems[i].isFloat())
-                    return i == lineContentRange.end - 1;
-            }
-            // There has to be at least one non-float item.
-            ASSERT_NOT_REACHED();
-            return false;
-        }();
-        line.close(isLastLineWithInlineContent);
-
-        auto lineIsVisuallyEmpty = line.isVisuallyEmpty() ? LineBox::IsLineVisuallyEmpty::Yes : LineBox::IsLineVisuallyEmpty::No;
-        auto lastLine = isLastLineWithInlineContent ? LineBox::IsLastLineWithInlineContent::Yes : LineBox::IsLastLineWithInlineContent::No;
-        auto lineBox = LineBox { *this, lineConstraints.logicalTopLeft, line.lineLogicalWidth(), line.contentLogicalWidth(), lineContent.runs, lineIsVisuallyEmpty, lastLine};
-        setDisplayBoxesForLine(lineContent, lineBox, constraints.horizontal);
-
         if (!lineContentRange.isEmpty()) {
             ASSERT(needsLayoutRange.start < lineContentRange.end);
-            lineLogicalTop = lineBox.logicalBottom();
+            isFirstLine = false;
+            lineLogicalTop = lineContent.lineBox.logicalBottom();
             // When the trailing content is partial, we need to reuse the last InlineTextItem.
             auto lastInlineItemNeedsPartialLayout = lineContent.partialContent.hasValue();
             if (lastInlineItemNeedsPartialLayout) {
@@ -196,10 +176,9 @@
         }
         // Floats prevented us placing any content on the line.
         ASSERT(lineContent.runs.isEmpty());
-        ASSERT(line.hasIntrusiveFloat());
+        ASSERT(lineContent.hasIntrusiveFloat);
         // Move the next line below the intrusive float.
-        auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
-        auto floatConstraints = floatingContext.constraints(lineLogicalTop, toLayoutUnit(lineBox.logicalBottom()));
+        auto floatConstraints = floatingContext.constraints(lineLogicalTop, toLayoutUnit(lineContent.lineBox.logicalBottom()));
         ASSERT(floatConstraints.left || floatConstraints.right);
         static auto inifitePoint = PointInContextRoot::max();
         // In case of left and right constraints, we need to pick the one that's closer to the current line.
@@ -270,18 +249,14 @@
 {
     auto& inlineItems = formattingState().inlineItems();
     auto maximumLineWidth = InlineLayoutUnit { };
-    auto line = Line { *this };
-    auto lineBuilder = LineBuilder { *this, root(), inlineItems };
+    auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
+    auto lineBuilder = LineBuilder { *this, floatingContext, root(), inlineItems };
     auto layoutRange = LineBuilder::InlineItemRange { 0 , inlineItems.size() };
     while (!layoutRange.isEmpty()) {
-        // Only the horiztonal available width is constrained when computing intrinsic width.
-        line.open(availableWidth);
-        auto lineContent = lineBuilder.layoutInlineContent(line, layoutRange, { });
-        // FIXME: Find out if min/max needs to set the isLastLine bit.
-        line.close(false);
-        layoutRange.start = lineContent.inlineItemRange.end;
+        auto intrinsicContent = lineBuilder.computedIntrinsicWidth(layoutRange, availableWidth);
+        layoutRange.start = intrinsicContent.inlineItemRange.end;
         // FIXME: Use line logical left and right to take floats into account.
-        maximumLineWidth = std::max(maximumLineWidth, line.contentLogicalWidth());
+        maximumLineWidth = std::max(maximumLineWidth, intrinsicContent.logicalWidth);
     }
     return maximumLineWidth;
 }
@@ -415,78 +390,10 @@
     }
 }
 
-InlineFormattingContext::LineConstraints InlineFormattingContext::constraintsForLine(const HorizontalConstraints& horizontalConstraints, InlineLayoutUnit lineLogicalTop)
+void InlineFormattingContext::setDisplayBoxesForLine(const LineBuilder::LineContent& lineContent, const HorizontalConstraints& horizontalConstraints)
 {
-    auto lineLogicalLeft = horizontalConstraints.logicalLeft;
-    auto lineLogicalRight = lineLogicalLeft + horizontalConstraints.logicalWidth;
-    auto lineIsConstrainedByFloat = false;
-
-    auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
-    // Check for intruding floats and adjust logical left/available width for this line accordingly.
-    if (!floatingContext.isEmpty()) {
-        // FIXME: Add support for variable line height, where the intrusive floats should be probed as the line height grows.
-        auto floatConstraints = floatingContext.constraints(toLayoutUnit(lineLogicalTop), toLayoutUnit(lineLogicalTop + quirks().initialLineHeight()));
-        // Check if these constraints actually put limitation on the line.
-        if (floatConstraints.left && floatConstraints.left->x <= lineLogicalLeft)
-            floatConstraints.left = { };
-
-        if (floatConstraints.right && floatConstraints.right->x >= lineLogicalRight)
-            floatConstraints.right = { };
-
-        lineIsConstrainedByFloat = floatConstraints.left || floatConstraints.right;
-
-        if (floatConstraints.left && floatConstraints.right) {
-            ASSERT(floatConstraints.left->x <= floatConstraints.right->x);
-            lineLogicalRight = floatConstraints.right->x;
-            lineLogicalLeft = floatConstraints.left->x;
-        } else if (floatConstraints.left) {
-            ASSERT(floatConstraints.left->x >= lineLogicalLeft);
-            lineLogicalLeft = floatConstraints.left->x;
-        } else if (floatConstraints.right) {
-            ASSERT(floatConstraints.right->x >= lineLogicalLeft);
-            lineLogicalRight = floatConstraints.right->x;
-        }
-    }
-
-    auto computedTextIndent = [&] {
-        // text-indent property specifies the indentation applied to lines of inline content in a block.
-        // The indent is treated as a margin applied to the start edge of the line box.
-        // Unless otherwise specified, only lines that are the first formatted line of an element are affected.
-        // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
-        // FIXME: Add support for each-line.
-        // [Integration] root()->parent() would normally produce a valid layout box.
-        auto& root = this->root();
-        auto isFormattingContextRootCandidateToTextIndent = !root.isAnonymous();
-        if (root.isAnonymous()) {
-            // Unless otherwise specified by the each-line and/or hanging keywords, only lines that are the first formatted line
-            // of an element are affected.
-            // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
-            isFormattingContextRootCandidateToTextIndent = RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()
-                ? layoutState().isIntegratedRootBoxFirstChild()
-                : root.parent().firstInFlowChild() == &root;
-        }
-        if (!isFormattingContextRootCandidateToTextIndent)
-            return InlineLayoutUnit { };
-        auto invertLineRange = false;
-#if ENABLE(CSS3_TEXT)
-        invertLineRange = root.style().textIndentType() == TextIndentType::Hanging;
-#endif
-        auto isFirstLine = formattingState().ensureDisplayInlineContent().lineBoxes.isEmpty();
-        // text-indent: hanging inverts which lines are affected.
-        // inverted line range -> all the lines except the first one.
-        // !inverted line range -> first line gets the indent.
-        auto shouldIndent = invertLineRange != isFirstLine;
-        if (!shouldIndent)
-            return InlineLayoutUnit { };
-        return geometry().computedTextIndent(root, horizontalConstraints).valueOr(InlineLayoutUnit { });
-    };
-    lineLogicalLeft += computedTextIndent();
-    return LineConstraints { { lineLogicalLeft, lineLogicalTop }, lineLogicalRight - lineLogicalLeft, lineIsConstrainedByFloat };
-}
-
-void InlineFormattingContext::setDisplayBoxesForLine(const LineBuilder::LineContent& lineContent, const LineBox& lineBox, const HorizontalConstraints& horizontalConstraints)
-{
     auto& formattingState = this->formattingState();
+    auto& lineBox = lineContent.lineBox;
     if (!lineContent.floats.isEmpty()) {
         auto floatingContext = FloatingContext { root(), *this, formattingState.floatingState() };
         // Move floats to their final position.

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h (266679 => 266680)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h	2020-09-06 15:59:35 UTC (rev 266680)
@@ -67,7 +67,6 @@
     public:
         ContentHeightAndMargin inlineBlockHeightAndMargin(const Box&, const HorizontalConstraints&, const OverrideVerticalValues&) const;
         ContentWidthAndMargin inlineBlockWidthAndMargin(const Box&, const HorizontalConstraints&, const OverrideHorizontalValues&);
-        Optional<InlineLayoutUnit> computedTextIndent(const ContainerBox& formattingContextRoot, const HorizontalConstraints&) const;
 
     private:
         friend class InlineFormattingContext;
@@ -88,13 +87,7 @@
     void computeWidthAndMargin(const Box&, const HorizontalConstraints&);
 
     void collectInlineContentIfNeeded();
-    struct LineConstraints {
-        InlineLayoutPoint logicalTopLeft;
-        InlineLayoutUnit availableLogicalWidth { 0 };
-        bool lineIsConstrainedByFloat { false };
-    };
-    LineConstraints constraintsForLine(const HorizontalConstraints&, InlineLayoutUnit lineLogicalTop);
-    void setDisplayBoxesForLine(const LineBuilder::LineContent&, const LineBox&, const HorizontalConstraints&);
+    void setDisplayBoxesForLine(const LineBuilder::LineContent&, const HorizontalConstraints&);
     void invalidateFormattingState(const InvalidationState&);
 
     const InlineFormattingState& formattingState() const { return downcast<InlineFormattingState>(FormattingContext::formattingState()); }

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp (266679 => 266680)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp	2020-09-06 15:59:35 UTC (rev 266680)
@@ -75,15 +75,7 @@
     return complicatedCases(layoutBox, horizontalConstraints, overrideVerticalValues);
 }
 
-Optional<InlineLayoutUnit> InlineFormattingContext::Geometry::computedTextIndent(const ContainerBox& formattingContextRoot, const HorizontalConstraints& horizontalConstraints) const
-{
-    auto textIndent = formattingContextRoot.style().textIndent();
-    if (textIndent == RenderStyle::initialTextIndent())
-        return { };
-    return InlineLayoutUnit { minimumValueForLength(textIndent, horizontalConstraints.logicalWidth) };
 }
-
 }
-}
 
 #endif

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.cpp (266679 => 266680)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.cpp	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.cpp	2020-09-06 15:59:35 UTC (rev 266680)
@@ -409,7 +409,22 @@
     }
 }
 
+const InlineFormattingContext& LineBox::formattingContext() const
+{
+    return m_inlineFormattingContext;
 }
+
+const Box& LineBox::root() const
+{
+    return formattingContext().root();
 }
 
+LayoutState& LineBox::layoutState() const
+{
+    return formattingContext().layoutState();
+}
+
+}
+}
+
 #endif

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h (266679 => 266680)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h	2020-09-06 15:59:35 UTC (rev 266680)
@@ -123,9 +123,9 @@
 
     InlineLayoutUnit contentLogicalWidth() const { return m_contentLogicalWidth; }
 
-    const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
-    const Box& root() const { return formattingContext().root(); }
-    LayoutState& layoutState() const { return formattingContext().layoutState(); }
+    const InlineFormattingContext& formattingContext() const;
+    const Box& root() const;
+    LayoutState& layoutState() const;
 
 private:
     Display::InlineRect m_rect;

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (266679 => 266680)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-09-06 15:59:35 UTC (rev 266680)
@@ -29,6 +29,7 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "DisplayBox.h"
+#include "FloatingContext.h"
 #include "InlineFormattingContext.h"
 #include "LayoutBox.h"
 #include "TextUtil.h"
@@ -280,30 +281,65 @@
     return boxGeometry.width();
 }
 
-LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, const ContainerBox& formattingContextRoot, const InlineItems& inlineItems)
+LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, const FloatingContext& floatingContext, const ContainerBox& formattingContextRoot, const InlineItems& inlineItems)
     : m_inlineFormattingContext(inlineFormattingContext)
+    , m_floatingContext(floatingContext)
     , m_formattingContextRoot(formattingContextRoot)
+    , m_line(inlineFormattingContext)
     , m_inlineItems(inlineItems)
 {
 }
 
-LineBuilder::LineContent LineBuilder::layoutInlineContent(Line& line, const InlineItemRange& layoutRange, Optional<unsigned> partialLeadingContentLength)
+LineBuilder::LineContent LineBuilder::layoutInlineContent(const InlineItemRange& needsLayoutRange, Optional<unsigned> partialLeadingContentLength, const FormattingContext::ConstraintsForInFlowContent& initialConstraints, bool isFirstLine)
 {
-    ASSERT(m_floats.isEmpty());
+    auto usedConstraints = constraintsForLine(initialConstraints, isFirstLine);
+    initialize(usedConstraints);
+
+    auto committedContent = placeInlineContent(needsLayoutRange, partialLeadingContentLength);
+    auto committedRange = close(needsLayoutRange, committedContent);
+
+    auto lineLogicalTopLeft = InlineLayoutPoint { usedConstraints.logicalLeft, initialConstraints.vertical.logicalTop };
+    auto isLastLine = isLastLineWithInlineContent(committedRange, needsLayoutRange.end, committedContent.partialTrailingContent.hasValue());
+    auto lineIsVisuallyEmpty = m_line.isVisuallyEmpty() ? LineBox::IsLineVisuallyEmpty::Yes : LineBox::IsLineVisuallyEmpty::No;
+    return LineContent { committedContent.partialTrailingContent, committedRange, m_floats, m_line.hasIntrusiveFloat()
+        , LineBox { formattingContext(), lineLogicalTopLeft, m_line.lineLogicalWidth(), m_line.contentLogicalWidth(), m_line.runs(), lineIsVisuallyEmpty
+        , isLastLine ? LineBox::IsLastLineWithInlineContent::Yes : LineBox::IsLastLineWithInlineContent::No }
+        , m_line.runs() };
+}
+
+LineBuilder::IntrinsicContent LineBuilder::computedIntrinsicWidth(const InlineItemRange& needsLayoutRange, InlineLayoutUnit availableWidth)
+{
+    initialize({ { }, availableWidth, false });
+    auto committedContent = placeInlineContent(needsLayoutRange, { });
+    auto committedRange = close(needsLayoutRange, committedContent);
+    return { committedRange, m_line.contentLogicalWidth() };
+}
+
+void LineBuilder::initialize(const UsedConstraints& lineConstraints)
+{
+    m_floats.clear();
     m_partialLeadingTextItem = { };
     m_lastWrapOpportunityItem = { };
+
+    m_line.open(lineConstraints.availableLogicalWidth);
+    m_line.setHasIntrusiveFloat(lineConstraints.isConstrainedByFloat);
+}
+
+LineBuilder::CommittedContent LineBuilder::placeInlineContent(const InlineItemRange& needsLayoutRange, Optional<unsigned> partialLeadingContentLength)
+{
+    auto lineCandidate = LineCandidate { };
     auto lineBreaker = LineBreaker { };
-    auto currentItemIndex = layoutRange.start;
-    unsigned committedInlineItemCount = 0;
-    auto lineCandidate = LineCandidate { };
-    while (currentItemIndex < layoutRange.end) {
+
+    auto currentItemIndex = needsLayoutRange.start;
+    size_t committedInlineItemCount = 0;
+    while (currentItemIndex < needsLayoutRange.end) {
         // 1. Collect the set of runs that we can commit to the line as one entity e.g. <span>text_and_span_start_span_end</span>.
         // 2. Apply floats and shrink the available horizontal space e.g. <span>intru_<div style="float: left"></div>sive_float</span>.
         // 3. Check if the content fits the line and commit the content accordingly (full, partial or not commit at all).
         // 4. Return if we are at the end of the line either by not being able to fit more content or because of an explicit line break.
-        nextContentForLine(lineCandidate, currentItemIndex, layoutRange, partialLeadingContentLength, line.availableWidth() + line.trimmableTrailingWidth(), line.contentLogicalWidth());
+        nextContentForLine(lineCandidate, currentItemIndex, needsLayoutRange, partialLeadingContentLength, m_line.availableWidth() + m_line.trimmableTrailingWidth(), m_line.contentLogicalWidth());
         // Now check if we can put this content on the current line.
-        auto result = handleFloatsAndInlineContent(lineBreaker, line, layoutRange, lineCandidate);
+        auto result = handleFloatsAndInlineContent(lineBreaker, needsLayoutRange, lineCandidate);
         committedInlineItemCount = result.committedCount.isRevert ? result.committedCount.value : committedInlineItemCount + result.committedCount.value;
         auto& inlineContent = lineCandidate.inlineContent;
         auto inlineContentIsFullyCommitted = inlineContent.runs().size() == result.committedCount.value && !result.partialContent;
@@ -311,33 +347,106 @@
 
         if (inlineContentIsFullyCommitted && inlineContent.trailingLineBreak()) {
             // Fully commited (or empty) content followed by a line break means "end of line".
-            line.append(*inlineContent.trailingLineBreak(), { });
+            m_line.append(*inlineContent.trailingLineBreak(), { });
             ++committedInlineItemCount;
             isEndOfLine = true;
         }
         if (isEndOfLine) {
             // We can't place any more items on the current line.
-            return close(line, layoutRange, committedInlineItemCount, result.partialContent);
+            return { committedInlineItemCount, result.partialContent };
         }
-        currentItemIndex = layoutRange.start + committedInlineItemCount + m_floats.size();
+        currentItemIndex = needsLayoutRange.start + committedInlineItemCount + m_floats.size();
         partialLeadingContentLength = { };
     }
     // Looks like we've run out of runs.
-    return close(line, layoutRange, committedInlineItemCount, { });
+    return { committedInlineItemCount, { } };
 }
 
-LineBuilder::LineContent LineBuilder::close(const Line& line, const InlineItemRange& layoutRange, unsigned committedInlineItemCount, Optional<LineContent::PartialContent> partialContent)
+LineBuilder::InlineItemRange LineBuilder::close(const InlineItemRange& needsLayoutRange, const CommittedContent& committedContent)
 {
-    ASSERT_UNUSED(line, committedInlineItemCount || !m_floats.isEmpty() || line.hasIntrusiveFloat());
-    auto numberOfCommittedItems = committedInlineItemCount + m_floats.size();
-    auto trailingInlineItemIndex = layoutRange.start + numberOfCommittedItems - 1;
-    auto lineRange = InlineItemRange { layoutRange.start, trailingInlineItemIndex + 1 };
-    ASSERT(lineRange.end <= layoutRange.end);
+    ASSERT(committedContent.inlineItemCount || !m_floats.isEmpty() || m_line.hasIntrusiveFloat());
+    auto numberOfCommittedItems = committedContent.inlineItemCount + m_floats.size();
+    auto trailingInlineItemIndex = needsLayoutRange.start + numberOfCommittedItems - 1;
+    auto lineRange = InlineItemRange { needsLayoutRange.start, trailingInlineItemIndex + 1 };
+    ASSERT(lineRange.end <= needsLayoutRange.end);
     // Adjust hyphenated line count.
-    m_successiveHyphenatedLineCount = partialContent && partialContent->trailingContentHasHyphen ? m_successiveHyphenatedLineCount + 1 : 0;
-    return LineContent { partialContent, lineRange, WTFMove(m_floats), line.runs() };
+    m_successiveHyphenatedLineCount = committedContent.partialTrailingContent && committedContent.partialTrailingContent->trailingContentHasHyphen ? m_successiveHyphenatedLineCount + 1 : 0;
+    m_line.close(isLastLineWithInlineContent(lineRange, needsLayoutRange.end, committedContent.partialTrailingContent.hasValue()));
+    return lineRange;
 }
 
+LineBuilder::UsedConstraints LineBuilder::constraintsForLine(const FormattingContext::ConstraintsForInFlowContent& initialConstraints, bool isFirstLine)
+{
+    auto lineLogicalLeft = initialConstraints.horizontal.logicalLeft;
+    auto lineLogicalTop = initialConstraints.vertical.logicalTop;
+    auto lineLogicalRight = lineLogicalLeft + initialConstraints.horizontal.logicalWidth;
+    auto lineIsConstrainedByFloat = false;
+
+    // Check for intruding floats and adjust logical left/available width for this line accordingly.
+    if (!m_floatingContext.isEmpty()) {
+        // FIXME: Add support for variable line height, where the intrusive floats should be probed as the line height grows.
+        auto floatConstraints = m_floatingContext.constraints(toLayoutUnit(lineLogicalTop), toLayoutUnit(lineLogicalTop + *initialConstraints.vertical.logicalHeight));
+        // Check if these constraints actually put limitation on the line.
+        if (floatConstraints.left && floatConstraints.left->x <= lineLogicalLeft)
+            floatConstraints.left = { };
+
+        if (floatConstraints.right && floatConstraints.right->x >= lineLogicalRight)
+            floatConstraints.right = { };
+
+        lineIsConstrainedByFloat = floatConstraints.left || floatConstraints.right;
+
+        if (floatConstraints.left && floatConstraints.right) {
+            ASSERT(floatConstraints.left->x <= floatConstraints.right->x);
+            lineLogicalRight = floatConstraints.right->x;
+            lineLogicalLeft = floatConstraints.left->x;
+        } else if (floatConstraints.left) {
+            ASSERT(floatConstraints.left->x >= lineLogicalLeft);
+            lineLogicalLeft = floatConstraints.left->x;
+        } else if (floatConstraints.right) {
+            ASSERT(floatConstraints.right->x >= lineLogicalLeft);
+            lineLogicalRight = floatConstraints.right->x;
+        }
+    }
+
+    auto computedTextIndent = [&]() -> InlineLayoutUnit {
+        // text-indent property specifies the indentation applied to lines of inline content in a block.
+        // The indent is treated as a margin applied to the start edge of the line box.
+        // Unless otherwise specified, only lines that are the first formatted line of an element are affected.
+        // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
+        // FIXME: Add support for each-line.
+        // [Integration] root()->parent() would normally produce a valid layout box.
+        auto& root = this->root();
+        auto isFormattingContextRootCandidateToTextIndent = !root.isAnonymous();
+        if (root.isAnonymous()) {
+            // Unless otherwise specified by the each-line and/or hanging keywords, only lines that are the first formatted line
+            // of an element are affected.
+            // For example, the first line of an anonymous block box is only affected if it is the first child of its parent element.
+            isFormattingContextRootCandidateToTextIndent = RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled()
+                ? layoutState().isIntegratedRootBoxFirstChild()
+                : root.parent().firstInFlowChild() == &root;
+        }
+        if (!isFormattingContextRootCandidateToTextIndent)
+            return { };
+        auto invertLineRange = false;
+#if ENABLE(CSS3_TEXT)
+        invertLineRange = root.style().textIndentType() == TextIndentType::Hanging;
+#endif
+        // text-indent: hanging inverts which lines are affected.
+        // inverted line range -> all the lines except the first one.
+        // !inverted line range -> first line gets the indent.
+        auto shouldIndent = invertLineRange != isFirstLine;
+        if (!shouldIndent)
+            return { };
+
+        auto textIndent = root.style().textIndent();
+        if (textIndent == RenderStyle::initialTextIndent())
+            return { };
+        return { minimumValueForLength(textIndent, initialConstraints.horizontal.logicalWidth) };
+    };
+    lineLogicalLeft += computedTextIndent();
+    return UsedConstraints { lineLogicalLeft, lineLogicalRight - lineLogicalLeft, lineIsConstrainedByFloat };
+}
+
 void LineBuilder::nextContentForLine(LineCandidate& lineCandidate, unsigned currentInlineItemIndex, const InlineItemRange& layoutRange, Optional<unsigned> partialLeadingContentLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight)
 {
     ASSERT(currentInlineItemIndex < layoutRange.end);
@@ -384,7 +493,7 @@
     }
 }
 
-void LineBuilder::commitFloats(Line& line, const LineCandidate& lineCandidate, CommitIntrusiveFloatsOnly commitIntrusiveOnly)
+void LineBuilder::commitFloats(const LineCandidate& lineCandidate, CommitIntrusiveFloatsOnly commitIntrusiveOnly)
 {
     auto& floatContent = lineCandidate.floatContent;
     auto leftIntrusiveFloatsWidth = InlineLayoutUnit { };
@@ -405,21 +514,21 @@
                 rightIntrusiveFloatsWidth += floatCandidate.logicalWidth;
         }
     }
-    line.setHasIntrusiveFloat(hasIntrusiveFloat);
+    m_line.setHasIntrusiveFloat(hasIntrusiveFloat);
     if (leftIntrusiveFloatsWidth || rightIntrusiveFloatsWidth) {
         if (leftIntrusiveFloatsWidth)
-            line.moveLogicalLeft(leftIntrusiveFloatsWidth);
+            m_line.moveLogicalLeft(leftIntrusiveFloatsWidth);
         if (rightIntrusiveFloatsWidth)
-            line.moveLogicalRight(rightIntrusiveFloatsWidth);
+            m_line.moveLogicalRight(rightIntrusiveFloatsWidth);
     }
 }
 
-LineBuilder::Result LineBuilder::handleFloatsAndInlineContent(LineBreaker& lineBreaker, Line& line, const InlineItemRange& layoutRange, const LineCandidate& lineCandidate)
+LineBuilder::Result LineBuilder::handleFloatsAndInlineContent(LineBreaker& lineBreaker, const InlineItemRange& layoutRange, const LineCandidate& lineCandidate)
 {
     auto& inlineContent = lineCandidate.inlineContent;
     auto& candidateRuns = inlineContent.runs();
     if (candidateRuns.isEmpty()) {
-        commitFloats(line, lineCandidate);
+        commitFloats(lineCandidate);
         return { LineBreaker::IsEndOfLine::No };
     }
 
@@ -433,9 +542,9 @@
 
     auto& floatContent = lineCandidate.floatContent;
     // Check if this new content fits.
-    auto availableWidth = line.availableWidth() - floatContent.intrusiveWidth();
-    auto isLineConsideredEmpty = line.isVisuallyEmpty() && !line.hasIntrusiveFloat();
-    auto lineStatus = LineBreaker::LineStatus { availableWidth, line.trimmableTrailingWidth(), line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty };
+    auto availableWidth = m_line.availableWidth() - floatContent.intrusiveWidth();
+    auto isLineConsideredEmpty = m_line.isVisuallyEmpty() && !m_line.hasIntrusiveFloat();
+    auto lineStatus = LineBreaker::LineStatus { availableWidth, m_line.trimmableTrailingWidth(), m_line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty };
     auto result = lineBreaker.shouldWrapInlineContent(candidateRuns, inlineContent.logicalWidth(), lineStatus);
     if (result.lastWrapOpportunityItem)
         m_lastWrapOpportunityItem = result.lastWrapOpportunityItem;
@@ -442,8 +551,8 @@
     if (result.action == LineBreaker::Result::Action::Keep) {
         // This continuous content can be fully placed on the current line including non-intrusive floats.
         for (auto& run : candidateRuns)
-            line.append(run.inlineItem, run.logicalWidth);
-        commitFloats(line, lineCandidate);
+            m_line.append(run.inlineItem, run.logicalWidth);
+        commitFloats(lineCandidate);
         return { result.isEndOfLine, { candidateRuns.size(), false } };
     }
     if (result.action == LineBreaker::Result::Action::Push) {
@@ -455,14 +564,14 @@
         ASSERT(result.isEndOfLine == LineBreaker::IsEndOfLine::Yes);
         // Not only this content can't be placed on the current line, but we even need to revert the line back to an earlier position.
         ASSERT(m_lastWrapOpportunityItem);
-        return { LineBreaker::IsEndOfLine::Yes, { rebuildLine(line, layoutRange), true } };
+        return { LineBreaker::IsEndOfLine::Yes, { rebuildLine(layoutRange), true } };
     }
     if (result.action == LineBreaker::Result::Action::Split) {
         ASSERT(result.isEndOfLine == LineBreaker::IsEndOfLine::Yes);
         // Commit the combination of full and partial content on the current line.
-        commitFloats(line, lineCandidate, CommitIntrusiveFloatsOnly::Yes);
+        commitFloats(lineCandidate, CommitIntrusiveFloatsOnly::Yes);
         ASSERT(result.partialTrailingContent);
-        commitPartialContent(line, candidateRuns, *result.partialTrailingContent);
+        commitPartialContent(candidateRuns, *result.partialTrailingContent);
         // When splitting multiple runs <span style="word-break: break-all">text</span><span>content</span>, we might end up splitting them at run boundary.
         // It simply means we don't really have a partial run. Partial content yes, but not partial run.
         auto trailingRunIndex = result.partialTrailingContent->trailingRunIndex;
@@ -480,7 +589,7 @@
     return { LineBreaker::IsEndOfLine::No };
 }
 
-void LineBuilder::commitPartialContent(Line& line, const LineBreaker::RunList& runs, const LineBreaker::Result::PartialTrailingContent& partialTrailingContent)
+void LineBuilder::commitPartialContent(const LineBreaker::RunList& runs, const LineBreaker::Result::PartialTrailingContent& partialTrailingContent)
 {
     for (size_t index = 0; index < runs.size(); ++index) {
         auto& run = runs[index];
@@ -490,26 +599,26 @@
             if (auto partialRun = partialTrailingContent.partialRun) {
                 auto& trailingInlineTextItem = downcast<InlineTextItem>(runs[partialTrailingContent.trailingRunIndex].inlineItem);
                 auto partialTrailingTextItem = trailingInlineTextItem.left(partialRun->length);
-                line.appendPartialTrailingTextItem(partialTrailingTextItem, partialRun->logicalWidth, partialRun->needsHyphen);
+                m_line.appendPartialTrailingTextItem(partialTrailingTextItem, partialRun->logicalWidth, partialRun->needsHyphen);
                 return;
             }
             // The partial run is the last content to commit.
-            line.append(run.inlineItem, run.logicalWidth);
+            m_line.append(run.inlineItem, run.logicalWidth);
             return;
         }
-        line.append(run.inlineItem, run.logicalWidth);
+        m_line.append(run.inlineItem, run.logicalWidth);
     }
 }
 
-size_t LineBuilder::rebuildLine(Line& line, const InlineItemRange& layoutRange)
+size_t LineBuilder::rebuildLine(const InlineItemRange& layoutRange)
 {
     // Clear the line and start appending the inline items closing with the last wrap opportunity run.
-    line.clearContent();
+    m_line.clearContent();
     auto currentItemIndex = layoutRange.start;
     auto logicalRight = InlineLayoutUnit { };
     if (m_partialLeadingTextItem) {
         auto logicalWidth = inlineItemWidth(*m_partialLeadingTextItem, logicalRight);
-        line.append(*m_partialLeadingTextItem, logicalWidth);
+        m_line.append(*m_partialLeadingTextItem, logicalWidth);
         logicalRight += logicalWidth;
         if (&m_partialLeadingTextItem.value() == m_lastWrapOpportunityItem)
             return 1;
@@ -518,7 +627,7 @@
     for (; currentItemIndex < layoutRange.end; ++currentItemIndex) {
         auto& inlineItem = m_inlineItems[currentItemIndex];
         auto logicalWidth = inlineItemWidth(inlineItem, logicalRight);
-        line.append(inlineItem, logicalWidth);
+        m_line.append(inlineItem, logicalWidth);
         logicalRight += logicalWidth;
         if (&inlineItem == m_lastWrapOpportunityItem)
             return currentItemIndex - layoutRange.start + 1;
@@ -526,7 +635,28 @@
     return layoutRange.size();
 }
 
+bool LineBuilder::isLastLineWithInlineContent(const InlineItemRange& lineRange, size_t lastInlineItemIndex, bool hasPartialTrailingContent) const
+{
+    if (hasPartialTrailingContent)
+        return false;
+    if (lineRange.end == lastInlineItemIndex)
+        return true;
+    // Omit floats to see if this is the last line with inline content.
+    for (auto i = lastInlineItemIndex; i--;) {
+        if (!m_inlineItems[i].isFloat())
+            return i == lineRange.end - 1;
+    }
+    // There has to be at least one non-float item.
+    ASSERT_NOT_REACHED();
+    return false;
 }
+
+const LayoutState& LineBuilder::layoutState() const
+{
+    return formattingContext().layoutState();
 }
 
+}
+}
+
 #endif

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h (266679 => 266680)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h	2020-09-06 04:56:00 UTC (rev 266679)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h	2020-09-06 15:59:35 UTC (rev 266680)
@@ -28,16 +28,18 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "InlineLine.h"
+#include "InlineLineBox.h"
 #include "InlineLineBreaker.h"
 
 namespace WebCore {
 namespace Layout {
 
+class FloatingContext;
 struct LineCandidate;
 
 class LineBuilder {
 public:
-    LineBuilder(const InlineFormattingContext&, const ContainerBox& formattingContextRoot, const InlineItems&);
+    LineBuilder(const InlineFormattingContext&, const FloatingContext&, const ContainerBox& formattingContextRoot, const InlineItems&);
 
     struct InlineItemRange {
         bool isEmpty() const { return start == end; }
@@ -57,13 +59,21 @@
             const InlineItem* item { nullptr };
         };
         using FloatList = Vector<Float>;
-        FloatList floats;
+        const FloatList& floats;
+        bool hasIntrusiveFloat { false };
+        const LineBox lineBox;
         const Line::RunList& runs;
     };
-    LineContent layoutInlineContent(Line&, const InlineItemRange&, Optional<unsigned> partialLeadingContentLength);
+    LineContent layoutInlineContent(const InlineItemRange&, Optional<unsigned> partialLeadingContentLength, const FormattingContext::ConstraintsForInFlowContent& initialLineConstraints, bool isFirstLine);
 
+    struct IntrinsicContent {
+        InlineItemRange inlineItemRange;
+        InlineLayoutUnit logicalWidth { 0 };
+    };
+    IntrinsicContent computedIntrinsicWidth(const InlineItemRange&, InlineLayoutUnit availableWidth);
+
 private:
-    void nextContentForLine(LineCandidate&, unsigned inlineItemIndex, const InlineItemRange& layoutRange, Optional<unsigned> overflowLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight);
+    void nextContentForLine(LineCandidate&, unsigned inlineItemIndex, const InlineItemRange& needsLayoutRange, Optional<unsigned> overflowLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight);
     struct Result {
         LineBreaker::IsEndOfLine isEndOfLine { LineBreaker::IsEndOfLine::No };
         struct CommittedContentCount {
@@ -74,19 +84,35 @@
         Optional <LineContent::PartialContent> partialContent { };
     };
     enum class CommitIntrusiveFloatsOnly { No, Yes };
-    void commitFloats(Line&, const LineCandidate&, CommitIntrusiveFloatsOnly = CommitIntrusiveFloatsOnly::No);
-    Result handleFloatsAndInlineContent(LineBreaker&, Line&, const InlineItemRange& layoutRange, const LineCandidate&);
-    size_t rebuildLine(Line&, const InlineItemRange& layoutRange);
-    void commitPartialContent(Line&, const LineBreaker::RunList&, const LineBreaker::Result::PartialTrailingContent&);
-    LineContent close(const Line&, const InlineItemRange& layoutRange, unsigned committedInlineItemCount, Optional<LineContent::PartialContent>);
+    struct UsedConstraints {
+        InlineLayoutUnit logicalLeft { 0 };
+        InlineLayoutUnit availableLogicalWidth { 0 };
+        bool isConstrainedByFloat { false };
+    };
+    UsedConstraints constraintsForLine(const FormattingContext::ConstraintsForInFlowContent& initialLineConstraints, bool isFirstLine);
+    void commitFloats(const LineCandidate&, CommitIntrusiveFloatsOnly = CommitIntrusiveFloatsOnly::No);
+    Result handleFloatsAndInlineContent(LineBreaker&, const InlineItemRange& needsLayoutRange, const LineCandidate&);
+    size_t rebuildLine(const InlineItemRange& needsLayoutRange);
+    void commitPartialContent(const LineBreaker::RunList&, const LineBreaker::Result::PartialTrailingContent&);
+    void initialize(const UsedConstraints&);
+    struct CommittedContent {
+        size_t inlineItemCount { 0 };
+        Optional <LineContent::PartialContent> partialTrailingContent;
+    };
+    CommittedContent placeInlineContent(const InlineItemRange&, Optional<unsigned> partialLeadingContentLength);
+    InlineItemRange close(const InlineItemRange& needsLayoutRange, const CommittedContent&);
 
     InlineLayoutUnit inlineItemWidth(const InlineItem&, InlineLayoutUnit contentLogicalLeft) const;
+    bool isLastLineWithInlineContent(const InlineItemRange& lineRange, size_t lastInlineItemIndex, bool hasPartialTrailingContent) const;
 
     const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
     const ContainerBox& root() const { return m_formattingContextRoot; }
+    const LayoutState& layoutState() const;
 
     const InlineFormattingContext& m_inlineFormattingContext;
+    const FloatingContext& m_floatingContext;
     const ContainerBox& m_formattingContextRoot;
+    Line m_line;
     const InlineItems& m_inlineItems;
     LineContent::FloatList m_floats;
     Optional<InlineTextItem> m_partialLeadingTextItem;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to