Changeset 254661 in webkit


Ignore:
Timestamp:
Jan 15, 2020 6:23:39 PM (4 years ago)
Author:
Alan Bujtas
Message:

[LFC][IFC] LineBreaker::shouldWrapInlineContent should take the candidate content width
https://bugs.webkit.org/show_bug.cgi?id=206305
<rdar://problem/58613977>

Reviewed by Antti Koivisto.

We already have the width information of the candidate runs. Let's not loop through the runs just to re-collect the logical width.
~3% progression on PerformanceTests/Layout/line-layout-simple.html.

  • layout/inlineformatting/InlineLineBreaker.cpp:

(WebCore::Layout::LineBreaker::shouldWrapInlineContent):
(WebCore::Layout::LineBreaker::tryWrappingInlineContent const):
(WebCore::Layout::ContinuousContent::ContinuousContent):

  • layout/inlineformatting/InlineLineBreaker.h:
  • layout/inlineformatting/LineLayoutContext.cpp:

(WebCore::Layout::LineCandidateContent::inlineContentLogicalWidth const):
(WebCore::Layout::LineCandidateContent::append):
(WebCore::Layout::LineCandidateContent::reset):
(WebCore::Layout::LineLayoutContext::tryAddingInlineItems):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r254660 r254661  
     12020-01-15  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] LineBreaker::shouldWrapInlineContent should take the candidate content width
     4        https://bugs.webkit.org/show_bug.cgi?id=206305
     5        <rdar://problem/58613977>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        We already have the width information of the candidate runs. Let's not loop through the runs just to re-collect the logical width.
     10        ~3% progression on PerformanceTests/Layout/line-layout-simple.html.
     11
     12        * layout/inlineformatting/InlineLineBreaker.cpp:
     13        (WebCore::Layout::LineBreaker::shouldWrapInlineContent):
     14        (WebCore::Layout::LineBreaker::tryWrappingInlineContent const):
     15        (WebCore::Layout::ContinuousContent::ContinuousContent):
     16        * layout/inlineformatting/InlineLineBreaker.h:
     17        * layout/inlineformatting/LineLayoutContext.cpp:
     18        (WebCore::Layout::LineCandidateContent::inlineContentLogicalWidth const):
     19        (WebCore::Layout::LineCandidateContent::append):
     20        (WebCore::Layout::LineCandidateContent::reset):
     21        (WebCore::Layout::LineLayoutContext::tryAddingInlineItems):
     22
    1232020-01-15  Commit Queue  <commit-queue@webkit.org>
    224
  • trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.cpp

    r254629 r254661  
    5151}
    5252
     53static inline Optional<size_t> lastWrapOpportunityIndex(const LineBreaker::RunList& runList)
     54{
     55    // <span style="white-space: pre">no_wrap</span><span>yes wrap</span><span style="white-space: pre">no_wrap</span>.
     56    // [container start][no_wrap][container end][container start][yes] <- continuous content
     57    // [ ] <- continuous content
     58    // [wrap][container end][container start][no_wrap][container end] <- continuous content
     59    // Return #0 as the index where the second continuous content can wrap at.
     60    ASSERT(!runList.isEmpty());
     61    auto lastItemIndex = runList.size() - 1;
     62    return isWrappingAllowed(runList[lastItemIndex].inlineItem.style()) ? makeOptional(lastItemIndex) : WTF::nullopt;
     63}
     64
    5365struct ContinuousContent {
    54     ContinuousContent(const LineBreaker::RunList&);
     66    ContinuousContent(const LineBreaker::RunList&, InlineLayoutUnit contentLogicalWidth);
    5567
    5668    const LineBreaker::RunList& runs() const { return m_runs; }
     
    6577    bool hasTrailingCollapsibleContent() const { return !!m_trailingCollapsibleContent.width; }
    6678    bool isTrailingContentFullyCollapsible() const { return m_trailingCollapsibleContent.isFullyCollapsible; }
    67     Optional<size_t> lastWrapOpportunityIndex() const;
    6879
    6980    Optional<size_t> firstTextRunIndex() const;
     
    105116}
    106117
    107 LineBreaker::Result LineBreaker::shouldWrapInlineContent(const RunList& candidateRuns, const LineStatus& lineStatus)
    108 {
    109     auto candidateContent = ContinuousContent { candidateRuns };
     118LineBreaker::Result LineBreaker::shouldWrapInlineContent(const RunList& candidateRuns, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus& lineStatus)
     119{
     120    auto inlineContentWrapping = [&] {
     121        if (candidateContentLogicalWidth <= lineStatus.availableWidth)
     122            return Result { Result::Action::Keep };
     123#if USE_FLOAT_AS_INLINE_LAYOUT_UNIT
     124        // Preferred width computation sums up floats while line breaker substracts them. This can lead to epsilon-scale differences.
     125        if (WTF::areEssentiallyEqual(candidateContentLogicalWidth, lineStatus.availableWidth))
     126            return Result { Result::Action::Keep };
     127#endif
     128        return tryWrappingInlineContent(candidateRuns, candidateContentLogicalWidth, lineStatus);
     129    };
     130
     131    auto result = inlineContentWrapping();
     132    if (result.action == Result::Action::Keep) {
     133        // If this is not the end of the line, hold on to the last eligible line wrap opportunity so that we could revert back
     134        // to this position if no other line breaking opportunity exists in this content.
     135        if (auto lastLineWrapOpportunityIndex = lastWrapOpportunityIndex(candidateRuns)) {
     136            auto isEligibleLineWrapOpportunity = [&] (auto& candidateItem) {
     137                // Just check for leading collapsible whitespace for now.
     138                if (!lineStatus.lineIsEmpty || !candidateItem.isText() || !downcast<InlineTextItem>(candidateItem).isWhitespace())
     139                    return true;
     140                return shouldKeepBeginningOfLineWhitespace(candidateItem.style());
     141            };
     142            auto& inlineItem = candidateRuns[*lastLineWrapOpportunityIndex].inlineItem;
     143            if (isEligibleLineWrapOpportunity(inlineItem))
     144                m_lastWrapOpportunity = &inlineItem;
     145        }
     146    }
     147    return result;
     148}
     149
     150LineBreaker::Result LineBreaker::tryWrappingInlineContent(const RunList& candidateRuns, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus& lineStatus) const
     151{
     152    auto candidateContent = ContinuousContent { candidateRuns, candidateContentLogicalWidth };
    110153    ASSERT(!candidateContent.isEmpty());
    111     auto result = tryWrappingInlineContent(candidateContent, lineStatus);
    112     // If this is not the end of the line, hold on to the last eligible line wrap opportunity so that we could revert back
    113     // to this position if no other line breaking opportunity exists in this content.
    114     if (result.isEndOfLine == IsEndOfLine::Yes)
    115         return result;
    116     if (auto lastLineWrapOpportunityIndex = candidateContent.lastWrapOpportunityIndex()) {
    117         auto isEligibleLineWrapOpportunity = [&] (auto& candidateItem) {
    118             // Just check for leading collapsible whitespace for now.
    119             if (!lineStatus.lineIsEmpty || !candidateItem.isText() || !downcast<InlineTextItem>(candidateItem).isWhitespace())
    120                 return true;
    121             return shouldKeepBeginningOfLineWhitespace(candidateItem.style());
    122         };
    123         auto& inlineItem = candidateContent.runs()[*lastLineWrapOpportunityIndex].inlineItem;
    124         if (isEligibleLineWrapOpportunity(inlineItem))
    125             m_lastWrapOpportunity = &inlineItem;
    126     }
    127     return result;
    128 }
    129 
    130 LineBreaker::Result LineBreaker::tryWrappingInlineContent(const ContinuousContent& candidateContent, const LineStatus& lineStatus) const
    131 {
    132     if (candidateContent.width() <= lineStatus.availableWidth)
    133         return { Result::Action::Keep };
    134 
    135 #if USE_FLOAT_AS_INLINE_LAYOUT_UNIT
    136     // Preferred width computation sums up floats while line breaker substracts them. This can lead to epsilon-scale differences.
    137     if (WTF::areEssentiallyEqual(candidateContent.width(), lineStatus.availableWidth))
    138         return { Result::Action::Keep };
    139 #endif
    140 
     154
     155    ASSERT(candidateContent.width() > lineStatus.availableWidth);
    141156    if (candidateContent.hasTrailingCollapsibleContent()) {
    142157        ASSERT(candidateContent.hasTextContentOnly());
     
    341356}
    342357
    343 ContinuousContent::ContinuousContent(const LineBreaker::RunList& runs)
     358ContinuousContent::ContinuousContent(const LineBreaker::RunList& runs, InlineLayoutUnit contentLogicalWidth)
    344359    : m_runs(runs)
     360    , m_width(contentLogicalWidth)
    345361{
    346362    // Figure out the trailing collapsible state.
     
    373389        }
    374390    }
    375     // The trailing whitespace loop above is mostly about inspecting the last entry, so while it
    376     // looks like we are looping through the m_runs twice, it's really just one full loop in addition to checking the last run.
    377     for (auto& run : m_runs) {
    378         // Line break is not considered an inline content.
    379         ASSERT(!run.inlineItem.isLineBreak());
    380         m_width += run.logicalWidth;
    381     }
    382391}
    383392
     
    440449}
    441450
    442 Optional<size_t> ContinuousContent::lastWrapOpportunityIndex() const
    443 {
    444     // <span style="white-space: pre">no_wrap</span><span>yes wrap</span><span style="white-space: pre">no_wrap</span>.
    445     // [container start][no_wrap][container end][container start][yes] <- continuous content
    446     // [ ] <- continuous content
    447     // [wrap][container end][container start][no_wrap][container end] <- continuous content
    448     // Return #0 as the index where the second continuous content can wrap at.
    449     auto lastItemIndex = m_runs.size() - 1;
    450     return isWrappingAllowed(m_runs[lastItemIndex].inlineItem.style()) ? makeOptional(lastItemIndex) : WTF::nullopt;
    451 }
    452 
    453451void ContinuousContent::TrailingCollapsibleContent::reset()
    454452{
  • trunk/Source/WebCore/layout/inlineformatting/InlineLineBreaker.h

    r254032 r254661  
    8080        bool lineIsEmpty { true };
    8181    };
    82     Result shouldWrapInlineContent(const RunList& candidateRuns, const LineStatus&);
     82    Result shouldWrapInlineContent(const RunList& candidateRuns, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus&);
    8383    bool shouldWrapFloatBox(InlineLayoutUnit floatLogicalWidth, InlineLayoutUnit availableWidth, bool lineIsEmpty);
    8484
     
    9696    // see https://drafts.csswg.org/css-text-3/#line-break-details
    9797    Optional<WrappedTextContent> wrapTextContent(const RunList&, const LineStatus&) const;
    98     Result tryWrappingInlineContent(const ContinuousContent&, const LineStatus&) const;
     98    Result tryWrappingInlineContent(const RunList&, InlineLayoutUnit candidateContentLogicalWidth, const LineStatus&) const;
    9999    Optional<PartialRun> tryBreakingTextRun(const Run& overflowRun, InlineLayoutUnit availableWidth) const;
    100100
  • trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp

    r254630 r254661  
    182182    bool hasIntrusiveFloats() const { return !m_floats.isEmpty(); }
    183183    const LineBreaker::RunList& inlineRuns() const { return m_inlineRuns; }
     184    InlineLayoutUnit inlineContentLogicalWidth() const { return m_inlineContentLogicalWidth; }
    184185    const LineLayoutContext::FloatList& floats() const { return m_floats; }
    185186
     
    191192    void setTrailingLineBreak(const InlineItem& lineBreakItem) { m_trailingLineBreak = &lineBreakItem; }
    192193
     194    InlineLayoutUnit m_inlineContentLogicalWidth { 0 };
    193195    LineBreaker::RunList m_inlineRuns;
    194196    LineLayoutContext::FloatList m_floats;
     
    203205    if (inlineItem.isFloat())
    204206        return m_floats.append(makeWeakPtr(inlineItem));
     207    m_inlineContentLogicalWidth += *logicalWidth;
    205208    m_inlineRuns.append({ inlineItem, *logicalWidth });
    206209}
     
    208211void LineCandidateContent::reset()
    209212{
     213    m_inlineContentLogicalWidth = 0;
    210214    m_inlineRuns.clear();
    211215    m_floats.clear();
     
    411415
    412416    auto& candidateRuns = candidateContent.inlineRuns();
    413     auto result = lineBreaker.shouldWrapInlineContent(candidateRuns, lineStatus);
     417    auto result = lineBreaker.shouldWrapInlineContent(candidateRuns, candidateContent.inlineContentLogicalWidth(), lineStatus);
    414418    if (result.action == LineBreaker::Result::Action::Keep) {
    415419        // This continuous content can be fully placed on the current line.
Note: See TracChangeset for help on using the changeset viewer.