[LFC][IFC] Construct only one LineLayout object per inline formatting context
https://bugs.webkit.org/show_bug.cgi?id=204561
<rdar://problem/57463666>

Reviewed by Antti Koivisto.

Let's construct only one LineLayout object per IFC.

* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::lineLayout):
(WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthForConstraint const):
* layout/inlineformatting/InlineLineLayout.cpp:
(WebCore::Layout::LineLayout::LineLayout):
(WebCore::Layout::LineLayout::layout):
(WebCore::Layout::LineLayout::close):
(WebCore::Layout::LineLayout::LineInput::LineInput): Deleted.
* layout/inlineformatting/InlineLineLayout.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@252861 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 88bdff3..6cc9781 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,5 +1,25 @@
 2019-11-25  Zalan Bujtas  <zalan@apple.com>
 
+        [LFC][IFC] Construct only one LineLayout object per inline formatting context
+        https://bugs.webkit.org/show_bug.cgi?id=204561
+        <rdar://problem/57463666>
+
+        Reviewed by Antti Koivisto.
+
+        Let's construct only one LineLayout object per IFC.
+
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::lineLayout):
+        (WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthForConstraint const):
+        * layout/inlineformatting/InlineLineLayout.cpp:
+        (WebCore::Layout::LineLayout::LineLayout):
+        (WebCore::Layout::LineLayout::layout):
+        (WebCore::Layout::LineLayout::close):
+        (WebCore::Layout::LineLayout::LineInput::LineInput): Deleted.
+        * layout/inlineformatting/InlineLineLayout.h:
+
+2019-11-25  Zalan Bujtas  <zalan@apple.com>
+
         [LFC][IFC] Decouple LineLayout and Line
         https://bugs.webkit.org/show_bug.cgi?id=204560
         <rdar://problem/57463295>
diff --git a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
index 3e7a2a0..df7c537 100644
--- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
+++ b/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
@@ -96,13 +96,11 @@
     unsigned leadingInlineItemIndex = 0;
     Optional<LineLayout::PartialContent> leadingPartialContent;
     auto line = Line { *this, root().style().textAlign(), Line::SkipAlignment::No };
+    auto lineLayout = LineLayout { *this, inlineItems };
 
     while (leadingInlineItemIndex < inlineItems.size()) {
         line.initialize(constraintsForLine(usedHorizontalValues, lineLogicalTop));
-        auto lineInput = LineLayout::LineInput { inlineItems, leadingInlineItemIndex, leadingPartialContent };
-        auto lineLayout = LineLayout { *this, lineInput, line };
-
-        auto lineContent = lineLayout.layout();
+        auto lineContent = lineLayout.layout(line, leadingInlineItemIndex, leadingPartialContent);
         setDisplayBoxesForLine(lineContent, usedHorizontalValues);
 
         leadingPartialContent = { };
@@ -236,12 +234,11 @@
     LayoutUnit maximumLineWidth;
     unsigned leadingInlineItemIndex = 0;
     auto line = Line { *this, root().style().textAlign(), Line::SkipAlignment::Yes };
+    auto lineLayout = LineLayout { *this, inlineItems };
     while (leadingInlineItemIndex < inlineItems.size()) {
         // Only the horiztonal available width is constrained when computing intrinsic width.
         line.initialize(Line::Constraints { { }, usedHorizontalValues.constraints.width, false, { } });
-        auto lineInput = LineLayout::LineInput { inlineItems, leadingInlineItemIndex, { } };
-
-        auto lineContent = LineLayout(*this, lineInput, line).layout();
+        auto lineContent = lineLayout.layout(line, leadingInlineItemIndex, { });
 
         leadingInlineItemIndex = *lineContent.trailingInlineItemIndex + 1;
         LayoutUnit floatsWidth;
diff --git a/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp b/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp
index 74ef10a..a9ac6f1 100644
--- a/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp
+++ b/Source/WebCore/layout/inlineformatting/InlineLineLayout.cpp
@@ -67,13 +67,6 @@
     return boxGeometry.width();
 }
 
-LineLayout::LineInput::LineInput(const InlineItems& inlineItems, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent)
-    : inlineItems(inlineItems)
-    , leadingInlineItemIndex(leadingInlineItemIndex)
-    , leadingPartialContent(leadingPartialContent)
-{
-}
-
 void LineLayout::UncommittedContent::add(const InlineItem& inlineItem, LayoutUnit logicalWidth)
 {
     m_uncommittedRuns.append({ inlineItem, logicalWidth });
@@ -86,69 +79,76 @@
     m_width = 0;
 }
 
-LineLayout::LineLayout(const InlineFormattingContext& inlineFormattingContext, const LineInput& lineInput, Line& line)
+LineLayout::LineLayout(const InlineFormattingContext& inlineFormattingContext, const InlineItems& inlineItems)
     : m_inlineFormattingContext(inlineFormattingContext)
-    , m_lineInput(lineInput)
-    , m_line(line)
+    , m_inlineItems(inlineItems)
 {
 }
 
-LineLayout::LineContent LineLayout::layout()
+LineLayout::LineContent LineLayout::layout(Line& line, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent)
 {
+    auto initialize = [&] {
+        m_committedInlineItemCount = 0;
+        m_uncommittedContent.reset();
+        m_leadingPartialTextItem = { };
+        m_trailingPartialTextItem = { };
+        m_overflowTextLength = { };
+    };
+    initialize();
     // Iterate through the inline content and place the inline boxes on the current line.
     // Start with the partial leading text from the previous line.
-    auto firstNonPartialInlineItemIndex = m_lineInput.leadingInlineItemIndex;
-    if (m_lineInput.leadingPartialContent) {
+    auto firstNonPartialInlineItemIndex = leadingInlineItemIndex;
+    if (leadingPartialContent) {
         // Handle partial inline item (split text from the previous line).
-        auto& leadingTextItem = m_lineInput.inlineItems[m_lineInput.leadingInlineItemIndex];
+        auto& leadingTextItem = m_inlineItems[leadingInlineItemIndex];
         RELEASE_ASSERT(leadingTextItem->isText());
         // Construct a partial leading inline item.
         ASSERT(!m_leadingPartialTextItem);
-        m_leadingPartialTextItem = downcast<InlineTextItem>(*leadingTextItem).right(m_lineInput.leadingPartialContent->length);
-        if (placeInlineItem(*m_leadingPartialTextItem) == IsEndOfLine::Yes)
-            return close();
+        m_leadingPartialTextItem = downcast<InlineTextItem>(*leadingTextItem).right(leadingPartialContent->length);
+        if (placeInlineItem(line, *m_leadingPartialTextItem) == IsEndOfLine::Yes)
+            return close(line, leadingInlineItemIndex);
         ++firstNonPartialInlineItemIndex;
     }
 
-    for (auto inlineItemIndex = firstNonPartialInlineItemIndex; inlineItemIndex < m_lineInput.inlineItems.size(); ++inlineItemIndex) {
+    for (auto inlineItemIndex = firstNonPartialInlineItemIndex; inlineItemIndex < m_inlineItems.size(); ++inlineItemIndex) {
         // FIXME: We should not need to re-measure the dropped, uncommitted content when re-using them on the next line.
-        if (placeInlineItem(*m_lineInput.inlineItems[inlineItemIndex]) == IsEndOfLine::Yes)
-            return close();
+        if (placeInlineItem(line, *m_inlineItems[inlineItemIndex]) == IsEndOfLine::Yes)
+            return close(line, leadingInlineItemIndex);
     }
     // Check the uncommitted content whether they fit now that we know we are at a commit boundary.
     if (!m_uncommittedContent.isEmpty())
-        processUncommittedContent();
-    return close();
+        processUncommittedContent(line);
+    return close(line, leadingInlineItemIndex);
 }
 
-void LineLayout::commitPendingContent()
+void LineLayout::commitPendingContent(Line& line)
 {
     if (m_uncommittedContent.isEmpty())
         return;
     m_committedInlineItemCount += m_uncommittedContent.size();
     for (auto& uncommittedRun : m_uncommittedContent.runs())
-        m_line.append(uncommittedRun.inlineItem, uncommittedRun.logicalWidth);
+        line.append(uncommittedRun.inlineItem, uncommittedRun.logicalWidth);
     m_uncommittedContent.reset();
 }
 
-LineLayout::LineContent LineLayout::close()
+LineLayout::LineContent LineLayout::close(Line& line, unsigned leadingInlineItemIndex)
 {
-    ASSERT(m_committedInlineItemCount || m_line.hasIntrusiveFloat());
+    ASSERT(m_committedInlineItemCount || line.hasIntrusiveFloat());
     m_uncommittedContent.reset();
     if (!m_committedInlineItemCount)
-        return LineContent { { }, { }, WTFMove(m_floats), m_line.close(), m_line.lineBox() };
+        return LineContent { { }, { }, WTFMove(m_floats), line.close(), line.lineBox() };
 
     Optional<PartialContent> overflowContent;
     if (m_overflowTextLength)
         overflowContent = PartialContent { *m_overflowTextLength };
-    auto trailingInlineItemIndex = m_lineInput.leadingInlineItemIndex + m_committedInlineItemCount - 1;
+    auto trailingInlineItemIndex = leadingInlineItemIndex + m_committedInlineItemCount - 1;
 
     auto isLastLineWithInlineContent = [&] {
         if (overflowContent)
             return Line::IsLastLineWithInlineContent::No;
         // Skip floats backwards to see if this is going to be the last line with inline content.
-        for (auto i = m_lineInput.inlineItems.size(); i--;) {
-            if (!m_lineInput.inlineItems[i]->isFloat())
+        for (auto i = m_inlineItems.size(); i--;) {
+            if (!m_inlineItems[i]->isFloat())
                 return i == trailingInlineItemIndex ? Line::IsLastLineWithInlineContent::Yes : Line::IsLastLineWithInlineContent::No;
         }
         // There has to be at least one non-float item.
@@ -156,12 +156,12 @@
         return Line::IsLastLineWithInlineContent::No;
     };
 
-    return LineContent { trailingInlineItemIndex, overflowContent, WTFMove(m_floats), m_line.close(isLastLineWithInlineContent()), m_line.lineBox() };
+    return LineContent { trailingInlineItemIndex, overflowContent, WTFMove(m_floats), line.close(isLastLineWithInlineContent()), line.lineBox() };
 }
 
-LineLayout::IsEndOfLine LineLayout::placeInlineItem(const InlineItem& inlineItem)
+LineLayout::IsEndOfLine LineLayout::placeInlineItem(Line& line, const InlineItem& inlineItem)
 {
-    auto currentLogicalRight = m_line.lineBox().logicalRight();
+    auto currentLogicalRight = line.lineBox().logicalRight();
     auto itemLogicalWidth = inlineItemWidth(formattingContext(), inlineItem, currentLogicalRight);
 
     // Floats are special, they are intrusive but they don't really participate in the line layout context.
@@ -171,50 +171,50 @@
         // Not sure what to do when the float takes up the available space and we've got continuous content. Browser engines don't agree.
         // Let's just commit the pending content and try placing the float for now.
         if (!m_uncommittedContent.isEmpty()) {
-            if (processUncommittedContent() == IsEndOfLine::Yes)
+            if (processUncommittedContent(line) == IsEndOfLine::Yes)
                 return IsEndOfLine::Yes;
         }
-        auto lineIsConsideredEmpty = m_line.isVisuallyEmpty() && !m_line.hasIntrusiveFloat();
-        if (LineBreaker().shouldWrapFloatBox(itemLogicalWidth, m_line.availableWidth() + m_line.trailingTrimmableWidth(), lineIsConsideredEmpty))
+        auto lineIsConsideredEmpty = line.isVisuallyEmpty() && !line.hasIntrusiveFloat();
+        if (LineBreaker().shouldWrapFloatBox(itemLogicalWidth, line.availableWidth() + line.trailingTrimmableWidth(), lineIsConsideredEmpty))
             return IsEndOfLine::Yes;
 
         // This float can sit on the current line.
         auto& floatBox = inlineItem.layoutBox();
         // Shrink available space for current line and move existing inline runs.
-        floatBox.isLeftFloatingPositioned() ? m_line.moveLogicalLeft(itemLogicalWidth) : m_line.moveLogicalRight(itemLogicalWidth);
+        floatBox.isLeftFloatingPositioned() ? line.moveLogicalLeft(itemLogicalWidth) : line.moveLogicalRight(itemLogicalWidth);
         m_floats.append(makeWeakPtr(inlineItem));
         ++m_committedInlineItemCount;
-        m_line.setHasIntrusiveFloat();
+        line.setHasIntrusiveFloat();
         return IsEndOfLine::No;
     }
     // Forced line breaks are also special.
     if (inlineItem.isForcedLineBreak()) {
-        auto isEndOfLine = !m_uncommittedContent.isEmpty() ? processUncommittedContent() : IsEndOfLine::No;
+        auto isEndOfLine = !m_uncommittedContent.isEmpty() ? processUncommittedContent(line) : IsEndOfLine::No;
         // When the uncommitted content fits(or the line is empty), add the line break to this line as well.
         if (isEndOfLine == IsEndOfLine::No) {
             m_uncommittedContent.add(inlineItem, itemLogicalWidth);
-            commitPendingContent();
+            commitPendingContent(line);
         }
         return IsEndOfLine::Yes;
     }
     //
     auto isEndOfLine = IsEndOfLine::No;
     if (!m_uncommittedContent.isEmpty() && shouldProcessUncommittedContent(inlineItem))
-        isEndOfLine = processUncommittedContent();
+        isEndOfLine = processUncommittedContent(line);
     // The current item might fit as well.
     if (isEndOfLine == IsEndOfLine::No)
         m_uncommittedContent.add(inlineItem, itemLogicalWidth);
     return isEndOfLine;
 }
 
-LineLayout::IsEndOfLine LineLayout::processUncommittedContent()
+LineLayout::IsEndOfLine LineLayout::processUncommittedContent(Line& line)
 {
     // Check if the pending content fits.
-    auto lineIsConsideredEmpty = m_line.isVisuallyEmpty() && !m_line.hasIntrusiveFloat();
-    auto breakingContext = LineBreaker().breakingContextForInlineContent(m_uncommittedContent.runs(), m_uncommittedContent.width(), m_line.availableWidth(), lineIsConsideredEmpty);
+    auto lineIsConsideredEmpty = line.isVisuallyEmpty() && !line.hasIntrusiveFloat();
+    auto breakingContext = LineBreaker().breakingContextForInlineContent(m_uncommittedContent.runs(), m_uncommittedContent.width(), line.availableWidth(), lineIsConsideredEmpty);
     // The uncommitted content can fully, partially fit the current line (commit/partial commit) or not at all (reset).
     if (breakingContext.contentBreak == LineBreaker::BreakingContext::ContentBreak::Keep)
-        commitPendingContent();
+        commitPendingContent(line);
     else if (breakingContext.contentBreak == LineBreaker::BreakingContext::ContentBreak::Split) {
         ASSERT(breakingContext.trailingPartialContent);
         ASSERT(m_uncommittedContent.runs()[breakingContext.trailingPartialContent->runIndex].inlineItem.isText());
@@ -230,7 +230,7 @@
         // Keep the non-overflow part of the uncommitted runs and add the trailing partial content.
         m_uncommittedContent.trim(overflowInlineTextItemIndex);
         m_uncommittedContent.add(*m_trailingPartialTextItem, breakingContext.trailingPartialContent->logicalWidth);
-        commitPendingContent();
+        commitPendingContent(line);
     } else if (breakingContext.contentBreak == LineBreaker::BreakingContext::ContentBreak::Wrap)
         m_uncommittedContent.reset();
     else
diff --git a/Source/WebCore/layout/inlineformatting/InlineLineLayout.h b/Source/WebCore/layout/inlineformatting/InlineLineLayout.h
index e485da8..d71b29f 100644
--- a/Source/WebCore/layout/inlineformatting/InlineLineLayout.h
+++ b/Source/WebCore/layout/inlineformatting/InlineLineLayout.h
@@ -35,25 +35,12 @@
 
 class LineLayout {
 public:
-    struct LineInput;
-    LineLayout(const InlineFormattingContext&, const LineInput&, Line&);
-
-    struct LineContent;
-    LineContent layout();
+    LineLayout(const InlineFormattingContext&, const InlineItems&);
 
     struct PartialContent {
         // This will potentially gain some more members. 
         unsigned length;
     };
-    struct LineInput {
-        LineInput(const InlineItems&, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent);
-
-        const InlineItems& inlineItems;
-        unsigned leadingInlineItemIndex { 0 };
-        Optional<PartialContent> leadingPartialContent;
-        Optional<LayoutUnit> floatMinimumLogicalBottom;
-    };
-
     struct LineContent {
         Optional<unsigned> trailingInlineItemIndex;
         Optional<PartialContent> trailingPartialContent;
@@ -61,6 +48,7 @@
         const Line::RunList runList;
         const LineBox lineBox;
     };
+    LineContent layout(Line&, unsigned leadingInlineItemIndex, Optional<PartialContent> leadingPartialContent);
 
     struct Run {
         const InlineItem& inlineItem;
@@ -72,11 +60,11 @@
 private:
     const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
     enum class IsEndOfLine { No, Yes };
-    IsEndOfLine placeInlineItem(const InlineItem&);
-    void commitPendingContent();
-    LineContent close();
+    IsEndOfLine placeInlineItem(Line&, const InlineItem&);
+    void commitPendingContent(Line&);
+    LineContent close(Line&, unsigned leadingInlineItemIndex);
     bool shouldProcessUncommittedContent(const InlineItem&) const;
-    IsEndOfLine processUncommittedContent();
+    IsEndOfLine processUncommittedContent(Line&);
     
     struct UncommittedContent {
         void add(const InlineItem&, LayoutUnit logicalWidth);
@@ -95,8 +83,7 @@
     };
 
     const InlineFormattingContext& m_inlineFormattingContext;
-    const LineInput& m_lineInput;
-    Line& m_line;
+    const InlineItems& m_inlineItems;
     UncommittedContent m_uncommittedContent;
     unsigned m_committedInlineItemCount { 0 };
     Vector<WeakPtr<InlineItem>> m_floats;