⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 267637 in webkit


Ignore:
Timestamp:
Sep 26, 2020, 2:31:49 PM (6 years ago)
Author:
Alan Bujtas
Message:

[LFC][IFC] Add support for <wbr>
https://bugs.webkit.org/show_bug.cgi?id=217014

Reviewed by Antti Koivisto.

Source/WebCore:

Introduce the WordBreakOpportunity type of InlineItem and take it into account while searching for word break opportunities.
It generates an empty line run but it does not yet have geometry information.

Test: fast/layoutformattingcontext/wbr-simple.html

  • layout/inlineformatting/InlineFormattingContext.cpp:

(WebCore::Layout::InlineFormattingContext::collectInlineContentIfNeeded):

  • layout/inlineformatting/InlineItem.h:

(WebCore::Layout::InlineItem::isWordBreakOpportunity const):

  • layout/inlineformatting/InlineLine.cpp:

(WebCore::Layout::Line::appendWith):
(WebCore::Layout::Line::appendTextContent):
(WebCore::Layout::Line::appendWordBreakOpportunity):

  • layout/inlineformatting/InlineLine.h:

(WebCore::Layout::Line::Run::isWordBreakOpportunity const):

  • layout/inlineformatting/InlineLineBuilder.cpp:

(WebCore::Layout::nextWrapOpportunity):
(WebCore::Layout::LineCandidate::InlineContent::appendInlineItem):
(WebCore::Layout::LineBuilder::inlineItemWidth const):
(WebCore::Layout::LineBuilder::nextContentForLine):

LayoutTests:

  • fast/layoutformattingcontext/wbr-simple-expected.html: Added.
  • fast/layoutformattingcontext/wbr-simple.html: Added.
Location:
trunk
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r267622 r267637  
     12020-09-26  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Add support for <wbr>
     4        https://bugs.webkit.org/show_bug.cgi?id=217014
     5
     6        Reviewed by Antti Koivisto.
     7
     8        * fast/layoutformattingcontext/wbr-simple-expected.html: Added.
     9        * fast/layoutformattingcontext/wbr-simple.html: Added.
     10
    1112020-09-26  Zalan Bujtas  <zalan@apple.com>
    212
  • trunk/Source/WebCore/ChangeLog

    r267625 r267637  
     12020-09-26  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Add support for <wbr>
     4        https://bugs.webkit.org/show_bug.cgi?id=217014
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Introduce the WordBreakOpportunity type of InlineItem and take it into account while searching for word break opportunities.
     9        It generates an empty line run but it does not yet have geometry information.
     10
     11        Test: fast/layoutformattingcontext/wbr-simple.html
     12
     13        * layout/inlineformatting/InlineFormattingContext.cpp:
     14        (WebCore::Layout::InlineFormattingContext::collectInlineContentIfNeeded):
     15        * layout/inlineformatting/InlineItem.h:
     16        (WebCore::Layout::InlineItem::isWordBreakOpportunity const):
     17        * layout/inlineformatting/InlineLine.cpp:
     18        (WebCore::Layout::Line::appendWith):
     19        (WebCore::Layout::Line::appendTextContent):
     20        (WebCore::Layout::Line::appendWordBreakOpportunity):
     21        * layout/inlineformatting/InlineLine.h:
     22        (WebCore::Layout::Line::Run::isWordBreakOpportunity const):
     23        * layout/inlineformatting/InlineLineBuilder.cpp:
     24        (WebCore::Layout::nextWrapOpportunity):
     25        (WebCore::Layout::LineCandidate::InlineContent::appendInlineItem):
     26        (WebCore::Layout::LineBuilder::inlineItemWidth const):
     27        (WebCore::Layout::LineBuilder::nextContentForLine):
     28
    1292020-09-26  Alex Christensen  <achristensen@webkit.org>
    230
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp

    r267461 r267637  
    4141#include "LayoutInitialContainingBlock.h"
    4242#include "LayoutInlineTextBox.h"
     43#include "LayoutLineBreakBox.h"
    4344#include "LayoutReplacedBox.h"
    4445#include "LayoutState.h"
     
    371372        while (!layoutQueue.isEmpty()) {
    372373            auto& layoutBox = *layoutQueue.takeLast();
    373             if (layoutBox.isLineBreakBox()) {
    374                 // FIXME: Treat <wbr> as a word break opportunity instead.
    375                 formattingState.addInlineItem({ layoutBox, InlineItem::Type::HardLineBreak });
     374            if (is<LineBreakBox>(layoutBox)) {
     375                auto& lineBreakBox = downcast<LineBreakBox>(layoutBox);
     376                formattingState.addInlineItem({ layoutBox, lineBreakBox.isOptional() ? InlineItem::Type::WordBreakOpportunity : InlineItem::Type::HardLineBreak });
    376377            } else if (layoutBox.isFloatingPositioned())
    377378                formattingState.addInlineItem({ layoutBox, InlineItem::Type::Float });
  • trunk/Source/WebCore/layout/inlineformatting/InlineItem.h

    r255268 r267637  
    3636class InlineItem {
    3737public:
    38     enum class Type : uint8_t { Text, HardLineBreak, SoftLineBreak, Box, Float, ContainerStart, ContainerEnd };
     38    enum class Type : uint8_t { Text, HardLineBreak, SoftLineBreak, WordBreakOpportunity, Box, Float, ContainerStart, ContainerEnd };
    3939    InlineItem(const Box& layoutBox, Type);
    4040
     
    4747    bool isFloat() const { return type() == Type::Float; }
    4848    bool isLineBreak() const { return isSoftLineBreak() || isHardLineBreak(); }
     49    bool isWordBreakOpportunity() const { return type() == Type::WordBreakOpportunity; }
    4950    bool isSoftLineBreak() const { return type() == Type::SoftLineBreak; }
    5051    bool isHardLineBreak() const { return type() == Type::HardLineBreak; }
  • trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp

    r267622 r267637  
    225225    else if (inlineItem.isLineBreak())
    226226        appendLineBreak(inlineItem);
     227    else if (inlineItem.isWordBreakOpportunity())
     228        appendWordBreakOpportunity(inlineItem);
    227229    else if (inlineItem.isContainerStart())
    228230        appendInlineContainerStart(inlineItem, inlineRunDetails.logicalWidth);
     
    285287            if (run.isText())
    286288                return run.hasCollapsibleTrailingWhitespace();
    287             ASSERT(run.isContainerStart() || run.isContainerEnd());
     289            ASSERT(run.isContainerStart() || run.isContainerEnd() || run.isWordBreakOpportunity());
    288290        }
    289291        // Leading whitespace.
     
    349351}
    350352
     353void Line::appendWordBreakOpportunity(const InlineItem& inlineItem)
     354{
     355    m_runs.append({ inlineItem, contentLogicalWidth(), 0_lu });
     356}
     357
    351358bool Line::isRunVisuallyNonEmpty(const Run& run) const
    352359{
     
    375382    }
    376383
     384    if (run.isWordBreakOpportunity())
     385        return false;
     386
    377387    ASSERT_NOT_REACHED();
    378388    return false;
  • trunk/Source/WebCore/layout/inlineformatting/InlineLine.h

    r267515 r267637  
    6767        bool isBox() const { return m_type == InlineItem::Type::Box; }
    6868        bool isLineBreak() const { return m_type == InlineItem::Type::HardLineBreak || m_type == InlineItem::Type::SoftLineBreak; }
     69        bool isWordBreakOpportunity() const { return m_type == InlineItem::Type::WordBreakOpportunity; }
    6970        bool isContainerStart() const { return m_type == InlineItem::Type::ContainerStart; }
    7071        bool isContainerEnd() const { return m_type == InlineItem::Type::ContainerEnd; }
     
    144145    void appendInlineContainerEnd(const InlineItem&, InlineLayoutUnit logicalWidth);
    145146    void appendLineBreak(const InlineItem&);
     147    void appendWordBreakOpportunity(const InlineItem&);
    146148
    147149    void removeTrailingTrimmableContent();
  • trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp

    r267617 r267637  
    111111static inline size_t nextWrapOpportunity(const InlineItems& inlineContent, size_t startIndex, const LineBuilder::InlineItemRange layoutRange)
    112112{
    113     // 1. Find the start candidate by skipping leading non-content items e.g <span><span>start : skip "<span><span>"
    114     // 2. Find the end candidate by skipping non-content items inbetween e.g. <span><span>start</span>end: skip "</span>"
     113    // 1. Find the start candidate by skipping leading non-content items e.g "<span><span>start". Opportunity is after "<span><span>".
     114    // 2. Find the end candidate by skipping non-content items inbetween e.g. "<span><span>start</span>end". Opportunity is after "</span>".
    115115    // 3. Check if there's a soft wrap opportunity between the 2 candidate inline items and repeat.
    116     // 4. Any force line break inbetween is considered as a wrap opportunity.
    117 
    118     // [ex-][container start][container end][float][ample] (ex-<span></span><div style="float:left"></div>ample) : wrap index is at [ex-].
    119     // [ex][container start][amp-][container start][le] (ex<span>amp-<span>ample) : wrap index is at [amp-].
    120     // [ex-][container start][line break][ample] (ex-<span><br>ample) : wrap index is after [br].
    121     auto isAtLineBreak = false;
    122 
    123     auto inlineItemIndexWithContent = [&] (auto index) {
    124         // Note that floats are not part of the inline content. We should treat them as if they were not here as far as wrap opportunities are concerned.
    125         // [text][float box][text] is essentially just [text][text]
    126         for (; index < layoutRange.end; ++index) {
    127             auto& inlineItem = inlineContent[index];
    128             if (inlineItem.isText() || inlineItem.isBox())
    129                 return index;
    130             if (inlineItem.isLineBreak()) {
    131                 isAtLineBreak = true;
    132                 return index;
    133             }
    134         }
    135         return layoutRange.end;
    136     };
    137 
    138     // Start at the first inline item with content.
    139     // [container start][ex-] : start at [ex-]
    140     auto startContentIndex = inlineItemIndexWithContent(startIndex);
    141     if (isAtLineBreak) {
    142         // Content starts with a line break. The wrap position is after the line break.
    143         return startContentIndex + 1;
    144     }
    145 
    146     while (startContentIndex < layoutRange.end) {
    147         // 1. Find the next inline item with content.
    148         // 2. Check if there's a soft wrap opportunity between the start and the next inline item.
    149         auto nextContentIndex = inlineItemIndexWithContent(startContentIndex + 1);
    150         if (nextContentIndex == layoutRange.end)
    151             return nextContentIndex;
    152         if (isAtLineBreak) {
    153             // We always stop at line breaks. The wrap position is after the line break.
    154             return nextContentIndex + 1;
    155         }
    156         if (isAtSoftWrapOpportunity(inlineContent[startContentIndex], inlineContent[nextContentIndex])) {
    157             // There's a soft wrap opportunity between the start and the nextContent.
     116    // 4. Any force line break/explicit wrap content inbetween is considered as wrap opportunity.
     117
     118    // [ex-][container start][container end][float][ample] (ex-<span></span><div style="float:left"></div>ample). Wrap index is at [ex-].
     119    // [ex][container start][amp-][container start][le] (ex<span>amp-<span>ample). Wrap index is at [amp-].
     120    // [ex-][container start][line break][ample] (ex-<span><br>ample). Wrap index is after [br].
     121    auto previousInlineItemIndex = Optional<size_t> { };
     122    for (auto index = startIndex; index < layoutRange.end; ++index) {
     123        auto& inlineItem = inlineContent[index];
     124        if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity()) {
     125            // We always stop at explicit wrapping opportunities e.g. <br>. The wrap position is after the opportunity position.
     126            return ++index;
     127        }
     128        if (inlineItem.isFloat()) {
     129            // Floats are not part of the inline content. We ignore them as far as wrap opportunities are concerned.
     130            // [text][float box][text] is essentially just [text][text]
     131            continue;
     132        }
     133        if (inlineItem.isContainerStart() || inlineItem.isContainerEnd()) {
     134            // There's no wrapping opportunity between <span>text, <span></span> or </span>text.
     135            continue;
     136        }
     137        ASSERT(inlineItem.isText() || inlineItem.isBox());
     138        if (!previousInlineItemIndex) {
     139            previousInlineItemIndex = index;
     140            continue;
     141        }
     142        if (isAtSoftWrapOpportunity(inlineContent[*previousInlineItemIndex], inlineContent[index])) {
     143            // There's a soft wrap opportunity between 'previousInlineItemIndex' and 'index'.
    158144            // Now forward-find from the start position to see where we can actually wrap.
    159145            // [ex-][ample] vs. [ex-][container start][container end][ample]
    160146            // where [ex-] is startContent and [ample] is the nextContent.
    161             for (auto candidateIndex = startContentIndex + 1; candidateIndex < nextContentIndex; ++candidateIndex) {
     147            for (auto candidateIndex = *previousInlineItemIndex + 1; candidateIndex < index; ++candidateIndex) {
    162148                if (inlineContent[candidateIndex].isContainerStart()) {
    163149                    // inline content and [container start] and [container end] form unbreakable content.
     
    169155                }
    170156            }
    171             return nextContentIndex;
    172         }
    173         startContentIndex = nextContentIndex;
     157            return index;
     158        }
     159        previousInlineItemIndex = index;
    174160    }
    175161    return layoutRange.end;
     
    248234            return inlineTextItem.isWhitespace() && !TextUtil::shouldPreserveTrailingWhitespace(inlineTextItem.style());
    249235        }
    250         if (inlineItem.isContainerStart() || inlineItem.isContainerEnd())
     236        if (inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isWordBreakOpportunity())
    251237            return false;
    252238        ASSERT_NOT_REACHED();
     
    311297    }
    312298
    313     if (inlineItem.isLineBreak())
    314         return 0;
     299    if (inlineItem.isLineBreak() || inlineItem.isWordBreakOpportunity())
     300        return { };
    315301
    316302    auto& layoutBox = inlineItem.layoutBox();
     
    538524            continue;
    539525        }
    540         if (inlineItem.isText() || inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isBox()) {
     526        if (inlineItem.isText() || inlineItem.isContainerStart() || inlineItem.isContainerEnd() || inlineItem.isBox() || inlineItem.isWordBreakOpportunity()) {
    541527            auto inlineItenmWidth = inlineItemWidth(inlineItem, currentLogicalRight);
    542528            lineCandidate.inlineContent.appendInlineItem(inlineItem, inlineItenmWidth);
Note: See TracChangeset for help on using the changeset viewer.