Changeset 283047 in webkit


Ignore:
Timestamp:
Sep 24, 2021, 10:44:14 AM (4 years ago)
Author:
Alan Bujtas
Message:

[LFC][IFC] Line breaking only uses a few style properties
https://bugs.webkit.org/show_bug.cgi?id=230757

Reviewed by Antti Koivisto.

The continuous runs, input to the line breaking, should only contain style properties that line breaking actually uses.
This is in preparation for supporting first-line.

  • layout/formattingContexts/inline/InlineContentBreaker.cpp:

(WebCore::Layout::InlineContentBreaker::isWrappingAllowed):
(WebCore::Layout::InlineContentBreaker::shouldKeepEndOfLineWhitespace const):
(WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
(WebCore::Layout::InlineContentBreaker::processOverflowingContentWithText const):
(WebCore::Layout::InlineContentBreaker::wordBreakBehavior const):
(WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::append):
(WebCore::Layout::InlineContentBreaker::ContinuousContent::reset):

  • layout/formattingContexts/inline/InlineContentBreaker.h:

(WebCore::Layout::InlineContentBreaker::ContinuousContent::Run::Run):

  • layout/formattingContexts/inline/InlineLineBuilder.cpp:

(WebCore::Layout::LineCandidate::InlineContent::appendInlineItem):
(WebCore::Layout::LineBuilder::handleInlineContent):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r283046 r283047  
     12021-09-24  Alan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Line breaking only uses a few style properties
     4        https://bugs.webkit.org/show_bug.cgi?id=230757
     5
     6        Reviewed by Antti Koivisto.
     7
     8        The continuous runs, input to the line breaking, should only contain style properties that line breaking actually uses.
     9        This is in preparation for supporting first-line.
     10
     11        * layout/formattingContexts/inline/InlineContentBreaker.cpp:
     12        (WebCore::Layout::InlineContentBreaker::isWrappingAllowed):
     13        (WebCore::Layout::InlineContentBreaker::shouldKeepEndOfLineWhitespace const):
     14        (WebCore::Layout::InlineContentBreaker::processOverflowingContent const):
     15        (WebCore::Layout::InlineContentBreaker::processOverflowingContentWithText const):
     16        (WebCore::Layout::InlineContentBreaker::wordBreakBehavior const):
     17        (WebCore::Layout::InlineContentBreaker::tryBreakingTextRun const):
     18        (WebCore::Layout::InlineContentBreaker::ContinuousContent::append):
     19        (WebCore::Layout::InlineContentBreaker::ContinuousContent::reset):
     20        * layout/formattingContexts/inline/InlineContentBreaker.h:
     21        (WebCore::Layout::InlineContentBreaker::ContinuousContent::Run::Run):
     22        * layout/formattingContexts/inline/InlineLineBuilder.cpp:
     23        (WebCore::Layout::LineCandidate::InlineContent::appendInlineItem):
     24        (WebCore::Layout::LineBuilder::handleInlineContent):
     25
    1262021-09-24  Youenn Fablet  <youenn@apple.com>
    227
  • trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.cpp

    r282779 r283047  
    113113}
    114114
    115 bool InlineContentBreaker::isWrappingAllowed(const InlineItem& inlineItem)
    116 {
    117     auto& styleToUse = inlineItem.isBox() ? inlineItem.layoutBox().parent().style() : inlineItem.layoutBox().style();
     115bool InlineContentBreaker::isWrappingAllowed(const ContinuousContent::Run& run)
     116{
    118117    // Do not try to wrap overflown 'pre' and 'no-wrap' content to next line.
    119     return styleToUse.whiteSpace() != WhiteSpace::Pre && styleToUse.whiteSpace() != WhiteSpace::NoWrap;
     118    return run.style.whiteSpace != WhiteSpace::Pre && run.style.whiteSpace != WhiteSpace::NoWrap;
    120119}
    121120
     
    126125    // It might very well get collapsed when we close the line (normal/nowrap/pre-line).
    127126    // See https://www.w3.org/TR/css-text-3/#white-space-property
    128     auto whitespace = continuousContent.runs()[*firstTextRunIndex(continuousContent)].inlineItem.style().whiteSpace();
     127    auto whitespace = continuousContent.runs()[*firstTextRunIndex(continuousContent)].style.whiteSpace;
    129128    return whitespace == WhiteSpace::Normal || whitespace == WhiteSpace::NoWrap || whitespace == WhiteSpace::PreWrap || whitespace == WhiteSpace::PreLine;
    130129}
     
    267266        // e.g. <div style="white-space: nowrap">some text<div style="display: inline-block; white-space: pre-wrap"></div></div>.
    268267        // While the inline-block has pre-wrap which allows wrapping, the content lives in a nowrap context.
    269         return isWrappingAllowed(continuousContent.runs()[overflowingRunIndex].inlineItem);
     268        return isWrappingAllowed(continuousContent.runs()[overflowingRunIndex]);
    270269    };
    271270    if (shouldWrapUnbreakableContentToNextLine())
     
    289288        }
    290289        // Check if this text run needs to stay on the current line. 
    291         return isWrappingAllowed(run.inlineItem);
     290        return isWrappingAllowed(run);
    292291    };
    293292
     
    372371                ASSERT(run.inlineItem.isText());
    373372                // We know that this run does not fit the available space. If we can break it at any position, let's just use the start of the run.
    374                 if (wordBreakBehavior(run.inlineItem.style(), lineStatus.hasWrapOpportunityAtPreviousPosition) == WordBreakRule::AtArbitraryPosition) {
     373                if (wordBreakBehavior(run.style, lineStatus.hasWrapOpportunityAtPreviousPosition) == WordBreakRule::AtArbitraryPosition) {
    375374                    // We must be on an inline box boundary. Let's go back to the run in front of the inline box start run.
    376375                    // e.g. <span>unbreakable_and_overflow<span style="word-break: break-all">breakable</span>
     
    406405}
    407406
    408 OptionSet<InlineContentBreaker::WordBreakRule> InlineContentBreaker::wordBreakBehavior(const RenderStyle& style, bool hasWrapOpportunityAtPreviousPosition) const
     407OptionSet<InlineContentBreaker::WordBreakRule> InlineContentBreaker::wordBreakBehavior(const ContinuousContent::Run::Style& style, bool hasWrapOpportunityAtPreviousPosition) const
    409408{
    410409    // Disregard any prohibition against line breaks mandated by the word-break property.
    411410    // The different wrapping opportunities must not be prioritized.
    412411    // Note hyphenation is not applied.
    413     if (style.lineBreak() == LineBreak::Anywhere)
     412    if (style.lineBreak == LineBreak::Anywhere)
    414413        return { WordBreakRule::AtArbitraryPosition };
    415414
    416415    auto includeHyphenationIfAllowed = [&](std::optional<InlineContentBreaker::WordBreakRule> wordBreakRule) -> OptionSet<InlineContentBreaker::WordBreakRule> {
    417         auto hyphenationIsAllowed = !n_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.computedLocale());
     416        auto hyphenationIsAllowed = !n_hyphenationIsDisabled && style.hyphens == Hyphens::Auto && canHyphenate(style.locale);
    418417        if (hyphenationIsAllowed) {
    419418            if (wordBreakRule)
     
    426425    };
    427426    // Breaking is allowed within “words”.
    428     if (style.wordBreak() == WordBreak::BreakAll)
     427    if (style.wordBreak == WordBreak::BreakAll)
    429428        return includeHyphenationIfAllowed(WordBreakRule::AtArbitraryPosition);
    430429    // For compatibility with legacy content, the word-break property also supports a deprecated break-word keyword.
    431430    // When specified, this has the same effect as word-break: normal and overflow-wrap: anywhere, regardless of the actual value of the overflow-wrap property.
    432     if (style.wordBreak() == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition)
     431    if (style.wordBreak == WordBreak::BreakWord && !hasWrapOpportunityAtPreviousPosition)
    433432        return includeHyphenationIfAllowed(WordBreakRule::AtArbitraryPosition);
    434433    // OverflowWrap::BreakWord/Anywhere An otherwise unbreakable sequence of characters may be broken at an arbitrary point if there are no otherwise-acceptable break points in the line.
    435434    // Note that this applies to content where CSS properties (e.g. WordBreak::KeepAll) make it unbreakable.
    436     if ((style.overflowWrap() == OverflowWrap::BreakWord || style.overflowWrap() == OverflowWrap::Anywhere) && !hasWrapOpportunityAtPreviousPosition)
     435    if ((style.overflowWrap == OverflowWrap::BreakWord || style.overflowWrap == OverflowWrap::Anywhere) && !hasWrapOpportunityAtPreviousPosition)
    437436        return includeHyphenationIfAllowed(WordBreakRule::AtArbitraryPosition);
    438437    // Breaking is forbidden within “words”.
    439     if (style.wordBreak() == WordBreak::KeepAll)
     438    if (style.wordBreak == WordBreak::KeepAll)
    440439        return { };
    441440    return includeHyphenationIfAllowed({ });
     
    446445    ASSERT(overflowingRun.inlineItem.isText());
    447446    auto& inlineTextItem = downcast<InlineTextItem>(overflowingRun.inlineItem);
    448     auto& style = inlineTextItem.style();
     447    auto& style = overflowingRun.style;
    449448    auto availableSpaceIsInfinite = !availableWidth.has_value();
    450449
     
    463462            }
    464463            auto runLength = inlineTextItem.length();
    465             unsigned limitBefore = style.hyphenationLimitBefore() == RenderStyle::initialHyphenationLimitBefore() ? 0 : style.hyphenationLimitBefore();
    466             unsigned limitAfter = style.hyphenationLimitAfter() == RenderStyle::initialHyphenationLimitAfter() ? 0 : style.hyphenationLimitAfter();
     464            auto limitBefore = style.hyphenationLimitBefore.value_or(0);
     465            auto limitAfter = style.hyphenationLimitAfter.value_or(0);
    467466            // Check if this run can accommodate the before/after limits at all before start measuring text.
    468467            if (limitBefore >= runLength || limitAfter >= runLength || limitBefore + limitAfter > runLength)
     
    470469
    471470            unsigned leftSideLength = runLength;
    472             auto& fontCascade = style.fontCascade();
    473             auto hyphenWidth = InlineLayoutUnit { fontCascade.width(TextRun { StringView { style.hyphenString() } }) };
     471            auto hyphenWidth = InlineLayoutUnit { style.fontCascade.width(TextRun { StringView { style.hyphenString } }) };
    474472            if (!availableSpaceIsInfinite) {
    475473                auto availableWidthExcludingHyphen = *availableWidth - hyphenWidth;
    476                 if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, fontCascade.pixelSize()))
     474                if (availableWidthExcludingHyphen <= 0 || !enoughWidthForHyphenation(availableWidthExcludingHyphen, style.fontCascade.pixelSize()))
    477475                    return { };
    478476                leftSideLength = TextUtil::midWordBreak(inlineTextItem, overflowingRun.logicalWidth, availableWidthExcludingHyphen, logicalLeft).length;
     
    482480            // Adjust before index to accommodate the limit-after value (it's the last potential hyphen location in this run).
    483481            auto hyphenBefore = std::min(leftSideLength, runLength - limitAfter) + 1;
    484             unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.computedLocale());
     482            unsigned hyphenLocation = lastHyphenLocation(StringView(inlineTextItem.inlineTextBox().content()).substring(inlineTextItem.start(), inlineTextItem.length()), hyphenBefore, style.locale);
    485483            if (!hyphenLocation || hyphenLocation < limitBefore)
    486484                return { };
     
    519517}
    520518
    521 void InlineContentBreaker::ContinuousContent::append(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth)
    522 {
    523     m_runs.append({ inlineItem, logicalWidth });
     519void InlineContentBreaker::ContinuousContent::append(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth)
     520{
     521    m_runs.append({ inlineItem, style, logicalWidth });
    524522    m_logicalWidth = clampTo<InlineLayoutUnit>(m_logicalWidth + logicalWidth);
    525523    if (!collapsibleWidth) {
     
    544542    m_runs.clear();
    545543}
    546 
    547544}
    548545}
  • trunk/Source/WebCore/layout/formattingContexts/inline/InlineContentBreaker.h

    r278253 r283047  
    2929
    3030#include "LayoutUnits.h"
     31#include "RenderStyle.h"
    3132
    3233namespace WebCore {
    33 
    34 class RenderStyle;
    35 
    3634namespace Layout {
    3735
     
    8583        bool isFullyCollapsible() const { return logicalWidth() == collapsibleLogicalWidth(); }
    8684
    87         void append(const InlineItem&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth);
     85        void append(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth);
    8886        void reset();
    8987
    9088        struct Run {
    91             Run(const InlineItem&, InlineLayoutUnit logicalWidth);
     89            Run(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
    9290            Run(const Run&);
    9391            Run& operator=(const Run&);
    9492
    9593            const InlineItem& inlineItem;
     94            struct Style {
     95                WhiteSpace whiteSpace { WhiteSpace::Normal };
     96                LineBreak lineBreak { LineBreak::Auto };
     97                WordBreak wordBreak { WordBreak::Normal };
     98                OverflowWrap overflowWrap { OverflowWrap::Normal };
     99                Hyphens hyphens { Hyphens::None };
     100                std::optional<unsigned> hyphenationLimitBefore;
     101                std::optional<unsigned> hyphenationLimitAfter;
     102                const FontCascade& fontCascade;
     103                const AtomString& hyphenString;
     104                const AtomString& locale;
     105            };
     106            Style style;
    96107            InlineLayoutUnit logicalWidth { 0 };
    97108        };
     
    117128    void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }
    118129
    119     static bool isWrappingAllowed(const InlineItem&);
     130    static bool isWrappingAllowed(const ContinuousContent::Run&);
    120131
    121132private:
     
    128139        AtHyphenationOpportunities = 1 << 1
    129140    };
    130     OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
     141    OptionSet<WordBreakRule> wordBreakBehavior(const ContinuousContent::Run::Style&, bool hasWrapOpportunityAtPreviousPosition) const;
    131142    bool shouldKeepEndOfLineWhitespace(const ContinuousContent&) const;
    132143
     
    134145};
    135146
    136 inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, InlineLayoutUnit logicalWidth)
     147inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth)
    137148    : inlineItem(inlineItem)
     149    , style({ style.whiteSpace()
     150        , style.lineBreak()
     151        , style.wordBreak()
     152        , style.overflowWrap()
     153        , style.hyphens()
     154        , style.hyphenationLimitBefore() != style.initialHyphenationLimitBefore() ? std::make_optional(style.hyphenationLimitBefore()) : std::nullopt
     155        , style.hyphenationLimitAfter() != style.initialHyphenationLimitAfter() ? std::make_optional(style.hyphenationLimitAfter()) : std::nullopt
     156        , style.fontCascade()
     157        , style.hyphenString()
     158        , style.fontDescription().computedLocale() })
    138159    , logicalWidth(logicalWidth)
    139160{
     
    142163inline InlineContentBreaker::ContinuousContent::Run::Run(const Run& other)
    143164    : inlineItem(other.inlineItem)
     165    , style(other.style)
    144166    , logicalWidth(other.logicalWidth)
    145167{
  • trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBuilder.cpp

    r281754 r283047  
    196196        return letterSpacing;
    197197    };
    198     m_continuousContent.append(inlineItem, logicalWidth, collapsibleWidth());
     198    // FIXME: While the line breaking related properties for atomic level boxes do not depend on the line index (first line style) it'd be great to figure out the correct style to pass in.
     199    m_continuousContent.append(inlineItem, !inlineItem.isBox() ? inlineItem.style() : inlineItem.layoutBox().parent().style(), logicalWidth, collapsibleWidth());
    199200    m_hasInlineLevelBox = m_hasInlineLevelBox || inlineItem.isBox() || inlineItem.isInlineBoxStart();
    200201}
     
    693694        if (lineCandidate.inlineContent.hasTrailingSoftWrapOpportunity()) {
    694695            // Check if we are allowed to wrap at this position.
    695             auto& trailingItem = candidateRuns.last().inlineItem;
     696            auto& trailingRun = candidateRuns.last();
    696697            // FIXME: There must be a way to decide if the trailing run actually ended up on the line.
    697698            // Let's just deal with collapsed leading whitespace for now.
    698             if (!m_line.runs().isEmpty() && InlineContentBreaker::isWrappingAllowed(trailingItem))
    699                 m_wrapOpportunityList.append(&trailingItem);
     699            if (!m_line.runs().isEmpty() && InlineContentBreaker::isWrappingAllowed(trailingRun))
     700                m_wrapOpportunityList.append(&trailingRun.inlineItem);
    700701        }
    701702        return { result.isEndOfLine, { candidateRuns.size(), false } };
Note: See TracChangeset for help on using the changeset viewer.