Title: [272968] branches/safari-611-branch/Source/WebCore
Revision
272968
Author
repst...@apple.com
Date
2021-02-16 17:20:59 -0800 (Tue, 16 Feb 2021)

Log Message

Cherry-pick r272856. rdar://problem/74410114

    [LFC][IFC] Do not re-measure wrapped content
    https://bugs.webkit.org/show_bug.cgi?id=221874

    Reviewed by Antti Koivisto.

    This patch addresses the performance issue with extremely long content when
    the content gets re-measured many time while performing line breaking.

    When a certain text content does not fit the line we can
    1. keep it on the current line and let it overflow or
    2. wrap the entire content to the next line or
    3. split it somewhere in the middle.

    In case of 2 and 3 this overflowing content turns into the leading content on the subsequent line.
    Now due to ligature (#3) and position dependent glyph sizing(#2) we need to remeasure this leading content again.
    However with unreasonably long content and relatively small horizontal constraint, this could lead to
    repeated, continuous text measuring.
    In this patch we turn the overflow width into the leading width so that we measure the long text content only once.
    (FIXME: This should be turned into a generic "use the overflow content as-is when turning it into leading content")

    * layout/inlineformatting/InlineFormattingContext.cpp:
    (WebCore::Layout::InlineFormattingContext::lineLayout):
    * layout/inlineformatting/InlineLineBuilder.cpp:
    (WebCore::Layout::LineBuilder::layoutInlineContent):
    (WebCore::Layout::LineBuilder::computedIntrinsicWidth):
    (WebCore::Layout::LineBuilder::placeInlineContent):
    (WebCore::Layout::LineBuilder::candidateContentForLine):
    (WebCore::Layout::LineBuilder::handleInlineContent):
    * layout/inlineformatting/InlineLineBuilder.h:

    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@272856 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Modified Paths

Diff

Modified: branches/safari-611-branch/Source/WebCore/ChangeLog (272967 => 272968)


--- branches/safari-611-branch/Source/WebCore/ChangeLog	2021-02-17 01:20:46 UTC (rev 272967)
+++ branches/safari-611-branch/Source/WebCore/ChangeLog	2021-02-17 01:20:59 UTC (rev 272968)
@@ -1,5 +1,74 @@
 2021-02-16  Ruben Turcios  <rubent...@apple.com>
 
+        Cherry-pick r272856. rdar://problem/74410114
+
+    [LFC][IFC] Do not re-measure wrapped content
+    https://bugs.webkit.org/show_bug.cgi?id=221874
+    
+    Reviewed by Antti Koivisto.
+    
+    This patch addresses the performance issue with extremely long content when
+    the content gets re-measured many time while performing line breaking.
+    
+    When a certain text content does not fit the line we can
+    1. keep it on the current line and let it overflow or
+    2. wrap the entire content to the next line or
+    3. split it somewhere in the middle.
+    
+    In case of 2 and 3 this overflowing content turns into the leading content on the subsequent line.
+    Now due to ligature (#3) and position dependent glyph sizing(#2) we need to remeasure this leading content again.
+    However with unreasonably long content and relatively small horizontal constraint, this could lead to
+    repeated, continuous text measuring.
+    In this patch we turn the overflow width into the leading width so that we measure the long text content only once.
+    (FIXME: This should be turned into a generic "use the overflow content as-is when turning it into leading content")
+    
+    * layout/inlineformatting/InlineFormattingContext.cpp:
+    (WebCore::Layout::InlineFormattingContext::lineLayout):
+    * layout/inlineformatting/InlineLineBuilder.cpp:
+    (WebCore::Layout::LineBuilder::layoutInlineContent):
+    (WebCore::Layout::LineBuilder::computedIntrinsicWidth):
+    (WebCore::Layout::LineBuilder::placeInlineContent):
+    (WebCore::Layout::LineBuilder::candidateContentForLine):
+    (WebCore::Layout::LineBuilder::handleInlineContent):
+    * layout/inlineformatting/InlineLineBuilder.h:
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@272856 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-02-15  Zalan Bujtas  <za...@apple.com>
+
+            [LFC][IFC] Do not re-measure wrapped content
+            https://bugs.webkit.org/show_bug.cgi?id=221874
+
+            Reviewed by Antti Koivisto.
+
+            This patch addresses the performance issue with extremely long content when
+            the content gets re-measured many time while performing line breaking.
+
+            When a certain text content does not fit the line we can
+            1. keep it on the current line and let it overflow or
+            2. wrap the entire content to the next line or
+            3. split it somewhere in the middle.
+
+            In case of 2 and 3 this overflowing content turns into the leading content on the subsequent line.
+            Now due to ligature (#3) and position dependent glyph sizing(#2) we need to remeasure this leading content again.
+            However with unreasonably long content and relatively small horizontal constraint, this could lead to
+            repeated, continuous text measuring.
+            In this patch we turn the overflow width into the leading width so that we measure the long text content only once.
+            (FIXME: This should be turned into a generic "use the overflow content as-is when turning it into leading content")
+
+            * layout/inlineformatting/InlineFormattingContext.cpp:
+            (WebCore::Layout::InlineFormattingContext::lineLayout):
+            * layout/inlineformatting/InlineLineBuilder.cpp:
+            (WebCore::Layout::LineBuilder::layoutInlineContent):
+            (WebCore::Layout::LineBuilder::computedIntrinsicWidth):
+            (WebCore::Layout::LineBuilder::placeInlineContent):
+            (WebCore::Layout::LineBuilder::candidateContentForLine):
+            (WebCore::Layout::LineBuilder::handleInlineContent):
+            * layout/inlineformatting/InlineLineBuilder.h:
+
+2021-02-16  Ruben Turcios  <rubent...@apple.com>
+
         Cherry-pick r272841. rdar://problem/74409856
 
     [iOS] MobileSafari crashes at WebCore: WebCore::VideoFullscreenInterfaceAVKit::doEnterFullscreen

Modified: branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp (272967 => 272968)


--- branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2021-02-17 01:20:46 UTC (rev 272967)
+++ branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp	2021-02-17 01:20:59 UTC (rev 272968)
@@ -180,6 +180,7 @@
     struct PreviousLine {
         LineBuilder::InlineItemRange range;
         size_t overflowContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth;
     };
     Optional<PreviousLine> previousLine;
     auto& floatingState = formattingState.floatingState();
@@ -191,8 +192,9 @@
         // 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 : 0;
+        auto leadingLogicalWidth = previousLine ? previousLine->overflowLogicalWidth : WTF::nullopt;
         auto initialLineConstraints = InlineRect { lineLogicalTop, constraints.horizontal.logicalLeft, constraints.horizontal.logicalWidth, quirks().initialLineHeight() };
-        auto lineContent = lineBuilder.layoutInlineContent(needsLayoutRange, partialLeadingContentLength, initialLineConstraints, isFirstLine);
+        auto lineContent = lineBuilder.layoutInlineContent(needsLayoutRange, partialLeadingContentLength, leadingLogicalWidth, initialLineConstraints, isFirstLine);
         auto lineLogicalRect = computeGeometryForLineContent(lineContent, constraints.horizontal);
 
         auto lineContentRange = lineContent.inlineItemRange;
@@ -218,7 +220,7 @@
                 }
             }
             needsLayoutRange.start = lastInlineItemNeedsPartialLayout ? lineContentRange.end - 1 : lineContentRange.end;
-            previousLine = PreviousLine { lineContentRange, lineContent.partialTrailingContentLength };
+            previousLine = PreviousLine { lineContentRange, lineContent.partialTrailingContentLength, lineContent.overflowLogicalWidth };
             continue;
         }
         // Floats prevented us placing any content on the line.

Modified: branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (272967 => 272968)


--- branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2021-02-17 01:20:46 UTC (rev 272967)
+++ branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2021-02-17 01:20:59 UTC (rev 272968)
@@ -252,15 +252,15 @@
 {
 }
 
-LineBuilder::LineContent LineBuilder::layoutInlineContent(const InlineItemRange& needsLayoutRange, size_t partialLeadingContentLength, const InlineRect& initialLineLogicalRect, bool isFirstLine)
+LineBuilder::LineContent LineBuilder::layoutInlineContent(const InlineItemRange& needsLayoutRange, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> overflowLogicalWidth, const InlineRect& initialLineLogicalRect, bool isFirstLine)
 {
     initialize(initialConstraintsForLine(initialLineLogicalRect, isFirstLine));
 
-    auto committedContent = placeInlineContent(needsLayoutRange, partialLeadingContentLength);
+    auto committedContent = placeInlineContent(needsLayoutRange, partialLeadingContentLength, overflowLogicalWidth);
     auto committedRange = close(needsLayoutRange, committedContent);
 
     auto isLastLine = isLastLineWithInlineContent(committedRange, needsLayoutRange.end, committedContent.partialTrailingContentLength);
-    return LineContent { committedRange, committedContent.partialTrailingContentLength, m_floats, m_contentIsConstrainedByFloat
+    return LineContent { committedRange, committedContent.partialTrailingContentLength, committedContent.overflowLogicalWidth, m_floats, m_contentIsConstrainedByFloat
         , m_lineLogicalRect.topLeft()
         , m_lineLogicalRect.width()
         , m_line.contentLogicalWidth()
@@ -272,7 +272,7 @@
 LineBuilder::IntrinsicContent LineBuilder::computedIntrinsicWidth(const InlineItemRange& needsLayoutRange, InlineLayoutUnit availableWidth)
 {
     initialize({ { { }, { availableWidth, maxInlineLayoutUnit() } }, false });
-    auto committedContent = placeInlineContent(needsLayoutRange, { });
+    auto committedContent = placeInlineContent(needsLayoutRange, { }, { });
     auto committedRange = close(needsLayoutRange, committedContent);
     return { committedRange, m_line.contentLogicalWidth(), m_floats };
 }
@@ -288,7 +288,7 @@
     m_contentIsConstrainedByFloat = lineConstraints.isConstrainedByFloat;
 }
 
-LineBuilder::CommittedContent LineBuilder::placeInlineContent(const InlineItemRange& needsLayoutRange, size_t partialLeadingContentLength)
+LineBuilder::CommittedContent LineBuilder::placeInlineContent(const InlineItemRange& needsLayoutRange, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> leadingLogicalWidth)
 {
     auto lineCandidate = LineCandidate { layoutState().shouldIgnoreTrailingLetterSpacing() };
     auto inlineContentBreaker = InlineContentBreaker { };
@@ -300,7 +300,7 @@
         // 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.
-        candidateContentForLine(lineCandidate, currentItemIndex, needsLayoutRange, partialLeadingContentLength, m_line.contentLogicalRight());
+        candidateContentForLine(lineCandidate, currentItemIndex, needsLayoutRange, partialLeadingContentLength, std::exchange(leadingLogicalWidth, WTF::nullopt), m_line.contentLogicalRight());
         // Now check if we can put this content on the current line.
         auto result = Result { };
         if (lineCandidate.floatItem) {
@@ -329,7 +329,7 @@
         }
         if (isEndOfLine) {
             // We can't place any more items on the current line.
-            return { committedInlineItemCount, result.partialTrailingContentLength };
+            return { committedInlineItemCount, result.partialTrailingContentLength, result.overflowLogicalWidth };
         }
         currentItemIndex = needsLayoutRange.start + committedInlineItemCount + m_floats.size();
         partialLeadingContentLength = { };
@@ -453,7 +453,7 @@
     return UsedConstraints { { initialLineLogicalRect.top(), lineLogicalLeft, lineLogicalRight - lineLogicalLeft, initialLineLogicalRect.height() }, lineIsConstrainedByFloat };
 }
 
-void LineBuilder::candidateContentForLine(LineCandidate& lineCandidate, size_t currentInlineItemIndex, const InlineItemRange& layoutRange, size_t partialLeadingContentLength, InlineLayoutUnit currentLogicalRight)
+void LineBuilder::candidateContentForLine(LineCandidate& lineCandidate, size_t currentInlineItemIndex, const InlineItemRange& layoutRange, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> leadingLogicalWidth, InlineLayoutUnit currentLogicalRight)
 {
     ASSERT(currentInlineItemIndex < layoutRange.end);
     lineCandidate.reset();
@@ -468,7 +468,7 @@
         // Handle leading partial content first (overflowing text from the previous line).
         // Construct a partial leading inline item.
         m_partialLeadingTextItem = downcast<InlineTextItem>(m_inlineItems[currentInlineItemIndex]).right(partialLeadingContentLength);
-        auto itemWidth = inlineItemWidth(*m_partialLeadingTextItem, currentLogicalRight);
+        auto itemWidth = leadingLogicalWidth ? *std::exchange(leadingLogicalWidth, WTF::nullopt) : inlineItemWidth(*m_partialLeadingTextItem, currentLogicalRight);
         lineCandidate.inlineContent.appendInlineItem(*m_partialLeadingTextItem, itemWidth);
         currentLogicalRight += itemWidth;
         ++currentInlineItemIndex;
@@ -483,7 +483,8 @@
             continue;
         }
         if (inlineItem.isText() || inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd() || inlineItem.isBox()) {
-            auto logicalWidth = inlineItemWidth(inlineItem, currentLogicalRight);
+            ASSERT(!leadingLogicalWidth || inlineItem.isText());
+            auto logicalWidth = leadingLogicalWidth ? *std::exchange(leadingLogicalWidth, WTF::nullopt) : inlineItemWidth(inlineItem, currentLogicalRight);
             lineCandidate.inlineContent.appendInlineItem(inlineItem, logicalWidth);
             currentLogicalRight += logicalWidth;
             continue;
@@ -637,10 +638,26 @@
             m_line.append(run.inlineItem, run.logicalWidth);
         return { result.isEndOfLine, { candidateRuns.size(), false } };
     }
+
+    auto eligibleOverflowWidthAsLeading = [&] () -> Optional<InlineLayoutUnit> {
+        // FIXME: Add support for other types of continuous content.
+        ASSERT(result.action == InlineContentBreaker::Result::Action::Wrap || result.action == InlineContentBreaker::Result::Action::Break);
+        if (candidateRuns.size() != 1 || !candidateRuns.first().inlineItem.isText())
+            return { };
+        auto& inlineTextItem = downcast<InlineTextItem>(candidateRuns.first().inlineItem);
+        if (inlineTextItem.isWhitespace())
+            return { };
+        if (result.action == InlineContentBreaker::Result::Action::Wrap)
+            return candidateRuns.first().logicalWidth;
+        if (result.action == InlineContentBreaker::Result::Action::Break && result.partialTrailingContent->partialRun)
+            return candidateRuns.first().logicalWidth - result.partialTrailingContent->partialRun->logicalWidth;
+        return { };
+    };
+
     if (result.action == InlineContentBreaker::Result::Action::Wrap) {
         ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
         // This continuous content can't be placed on the current line. Nothing to commit at this time.
-        return { InlineContentBreaker::IsEndOfLine::Yes };
+        return { InlineContentBreaker::IsEndOfLine::Yes, { }, { }, eligibleOverflowWidthAsLeading() };
     }
     if (result.action == InlineContentBreaker::Result::Action::WrapWithHyphen) {
         ASSERT(result.isEndOfLine == InlineContentBreaker::IsEndOfLine::Yes);
@@ -677,7 +694,7 @@
         auto& trailingInlineTextItem = downcast<InlineTextItem>(candidateRuns[trailingRunIndex].inlineItem);
         ASSERT(partialRun.length < trailingInlineTextItem.length());
         auto overflowLength = trailingInlineTextItem.length() - partialRun.length;
-        return { InlineContentBreaker::IsEndOfLine::Yes, { committedInlineItemCount, false }, overflowLength };
+        return { InlineContentBreaker::IsEndOfLine::Yes, { committedInlineItemCount, false }, overflowLength, eligibleOverflowWidthAsLeading() };
     }
     ASSERT_NOT_REACHED();
     return { InlineContentBreaker::IsEndOfLine::No };

Modified: branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h (272967 => 272968)


--- branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h	2021-02-17 01:20:46 UTC (rev 272967)
+++ branches/safari-611-branch/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h	2021-02-17 01:20:59 UTC (rev 272968)
@@ -52,6 +52,7 @@
     struct LineContent {
         InlineItemRange inlineItemRange;
         size_t partialTrailingContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth;
         const FloatList& floats;
         bool hasIntrusiveFloat { false };
         InlineLayoutPoint logicalTopLeft;
@@ -61,7 +62,7 @@
         bool isLastLineWithInlineContent { true };
         const Line::RunList& runs;
     };
-    LineContent layoutInlineContent(const InlineItemRange&, size_t partialLeadingContentLength, const InlineRect& initialLineLogicalRect, bool isFirstLine);
+    LineContent layoutInlineContent(const InlineItemRange&, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> leadingLogicalWidth, const InlineRect& initialLineLogicalRect, bool isFirstLine);
 
     struct IntrinsicContent {
         InlineItemRange inlineItemRange;
@@ -71,7 +72,7 @@
     IntrinsicContent computedIntrinsicWidth(const InlineItemRange&, InlineLayoutUnit availableWidth);
 
 private:
-    void candidateContentForLine(LineCandidate&, size_t inlineItemIndex, const InlineItemRange& needsLayoutRange, size_t overflowLength, InlineLayoutUnit currentLogicalRight);
+    void candidateContentForLine(LineCandidate&, size_t inlineItemIndex, const InlineItemRange& needsLayoutRange, size_t overflowLength, Optional<InlineLayoutUnit> leadingLogicalWidth, InlineLayoutUnit currentLogicalRight);
     size_t nextWrapOpportunity(size_t startIndex, const LineBuilder::InlineItemRange& layoutRange) const;
 
     struct Result {
@@ -82,6 +83,7 @@
         };
         CommittedContentCount committedCount { };
         size_t partialTrailingContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth { };
     };
     struct UsedConstraints {
         InlineRect logicalRect;
@@ -99,8 +101,9 @@
     struct CommittedContent {
         size_t inlineItemCount { 0 };
         size_t partialTrailingContentLength { 0 };
+        Optional<InlineLayoutUnit> overflowLogicalWidth { };
     };
-    CommittedContent placeInlineContent(const InlineItemRange&, size_t partialLeadingContentLength);
+    CommittedContent placeInlineContent(const InlineItemRange&, size_t partialLeadingContentLength, Optional<InlineLayoutUnit> overflowLogicalWidth);
     InlineItemRange close(const InlineItemRange& needsLayoutRange, const CommittedContent&);
 
     InlineLayoutUnit inlineItemWidth(const InlineItem&, InlineLayoutUnit contentLogicalLeft) const;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to