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

Changeset 181667 in webkit


Ignore:
Timestamp:
Mar 17, 2015, 3:20:30 PM (11 years ago)
Author:
Alan Bujtas
Message:

Simple line layout: Split fragments on renderer boundary on the fly.
https://bugs.webkit.org/show_bug.cgi?id=142579

Reviewed by Antti Koivisto.

Fragment splitting at renderers' boundary at the end of the line is no longer needed.
This patch ensures that TextFragmentIterator::nextTextFragment() does not concatenate
fragments across renderer boundary.

Source/WebCore:

Test: fast/text/simple-line-with-multiple-renderers.html

  • rendering/SimpleLineLayout.cpp:

(WebCore::SimpleLineLayout::revertRuns):
(WebCore::SimpleLineLayout::LineState::isEmpty):
(WebCore::SimpleLineLayout::LineState::appendFragmentAndCreateRunIfNeeded): Check if fragments need a new run and
whether neighboring fragments need collapsing.
(WebCore::SimpleLineLayout::LineState::revertUncommitted): Revert fragments over multiple renderers if they form
a fragment continuation.
(WebCore::SimpleLineLayout::LineState::removeTrailingWhitespace):
(WebCore::SimpleLineLayout::forceFragmentToLine): Ensure that if a fragment has continuation across multiple renderers,
they all get added to the current line.
(WebCore::SimpleLineLayout::createLineRuns):
(WebCore::SimpleLineLayout::LineState::appendFragment): Deleted.
(WebCore::SimpleLineLayout::FragmentForwardIterator::FragmentForwardIterator): Deleted.
(WebCore::SimpleLineLayout::FragmentForwardIterator::operator++): Deleted.
(WebCore::SimpleLineLayout::FragmentForwardIterator::operator!=): Deleted.
(WebCore::SimpleLineLayout::FragmentForwardIterator::operator*): Deleted.
(WebCore::SimpleLineLayout::begin): Deleted.
(WebCore::SimpleLineLayout::end): Deleted.
(WebCore::SimpleLineLayout::preWrap): Deleted.
(WebCore::SimpleLineLayout::removeTrailingWhitespace): Deleted.
(WebCore::SimpleLineLayout::updateLineConstrains): Deleted.
(WebCore::SimpleLineLayout::splitRunsAtRendererBoundary): Deleted.
(WebCore::SimpleLineLayout::createTextRuns): Deleted.
(WebCore::SimpleLineLayout::create): Deleted.
(WebCore::SimpleLineLayout::Layout::create): Deleted.

  • rendering/SimpleLineLayoutFlowContents.h:

(WebCore::SimpleLineLayout::FlowContents::Iterator::operator+):
(WebCore::SimpleLineLayout::FlowContents::Iterator::operator*):
(WebCore::SimpleLineLayout::FlowContents::Iterator::operator->):

  • rendering/SimpleLineLayoutFunctions.cpp:

(WebCore::SimpleLineLayout::paintFlow): Unrelated fix on non-collapsed tab position.

  • rendering/SimpleLineLayoutTextFragmentIterator.cpp:

(WebCore::SimpleLineLayout::TextFragmentIterator::TextFragmentIterator):
(WebCore::SimpleLineLayout::TextFragmentIterator::nextTextFragment):
(WebCore::SimpleLineLayout::TextFragmentIterator::revertToFragment):
(WebCore::SimpleLineLayout::TextFragmentIterator::textWidth):
(WebCore::SimpleLineLayout::TextFragmentIterator::skipToNextPosition):

  • rendering/SimpleLineLayoutTextFragmentIterator.h:

(WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::TextFragment):
(WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::overlapsToNextRenderer):
(WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::isCollapsible):
(WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::operator==):
(WebCore::SimpleLineLayout::TextFragmentIterator::isLineBreak):
(WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::split): Deleted.

LayoutTests:

  • fast/text/simple-line-with-multiple-renderers-expected.html: Added.
  • fast/text/simple-line-with-multiple-renderers.html: Added.
Location:
trunk
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r181659 r181667  
     12015-03-17  Zalan Bujtas  <zalan@apple.com>
     2
     3        Simple line layout: Split fragments on renderer boundary on the fly.
     4        https://bugs.webkit.org/show_bug.cgi?id=142579
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Fragment splitting at renderers' boundary at the end of the line is no longer needed.
     9        This patch ensures that TextFragmentIterator::nextTextFragment() does not concatenate
     10        fragments across renderer boundary.
     11
     12        * fast/text/simple-line-with-multiple-renderers-expected.html: Added.
     13        * fast/text/simple-line-with-multiple-renderers.html: Added.
     14
    1152015-03-17  Dean Jackson  <dino@apple.com>
    216
  • trunk/Source/WebCore/ChangeLog

    r181666 r181667  
     12015-03-17  Zalan Bujtas  <zalan@apple.com>
     2
     3        Simple line layout: Split fragments on renderer boundary on the fly.
     4        https://bugs.webkit.org/show_bug.cgi?id=142579
     5
     6        Reviewed by Antti Koivisto.
     7
     8        Fragment splitting at renderers' boundary at the end of the line is no longer needed.
     9        This patch ensures that TextFragmentIterator::nextTextFragment() does not concatenate
     10        fragments across renderer boundary.
     11
     12        Test: fast/text/simple-line-with-multiple-renderers.html
     13
     14        * rendering/SimpleLineLayout.cpp:
     15        (WebCore::SimpleLineLayout::revertRuns):
     16        (WebCore::SimpleLineLayout::LineState::isEmpty):
     17        (WebCore::SimpleLineLayout::LineState::appendFragmentAndCreateRunIfNeeded): Check if fragments need a new run and
     18        whether neighboring fragments need collapsing.
     19        (WebCore::SimpleLineLayout::LineState::revertUncommitted): Revert fragments over multiple renderers if they form
     20        a fragment continuation.
     21        (WebCore::SimpleLineLayout::LineState::removeTrailingWhitespace):
     22        (WebCore::SimpleLineLayout::forceFragmentToLine): Ensure that if a fragment has continuation across multiple renderers,
     23        they all get added to the current line.
     24        (WebCore::SimpleLineLayout::createLineRuns):
     25        (WebCore::SimpleLineLayout::LineState::appendFragment): Deleted.
     26        (WebCore::SimpleLineLayout::FragmentForwardIterator::FragmentForwardIterator): Deleted.
     27        (WebCore::SimpleLineLayout::FragmentForwardIterator::operator++): Deleted.
     28        (WebCore::SimpleLineLayout::FragmentForwardIterator::operator!=): Deleted.
     29        (WebCore::SimpleLineLayout::FragmentForwardIterator::operator*): Deleted.
     30        (WebCore::SimpleLineLayout::begin): Deleted.
     31        (WebCore::SimpleLineLayout::end): Deleted.
     32        (WebCore::SimpleLineLayout::preWrap): Deleted.
     33        (WebCore::SimpleLineLayout::removeTrailingWhitespace): Deleted.
     34        (WebCore::SimpleLineLayout::updateLineConstrains): Deleted.
     35        (WebCore::SimpleLineLayout::splitRunsAtRendererBoundary): Deleted.
     36        (WebCore::SimpleLineLayout::createTextRuns): Deleted.
     37        (WebCore::SimpleLineLayout::create): Deleted.
     38        (WebCore::SimpleLineLayout::Layout::create): Deleted.
     39        * rendering/SimpleLineLayoutFlowContents.h:
     40        (WebCore::SimpleLineLayout::FlowContents::Iterator::operator+):
     41        (WebCore::SimpleLineLayout::FlowContents::Iterator::operator*):
     42        (WebCore::SimpleLineLayout::FlowContents::Iterator::operator->):
     43        * rendering/SimpleLineLayoutFunctions.cpp:
     44        (WebCore::SimpleLineLayout::paintFlow): Unrelated fix on non-collapsed tab position.
     45        * rendering/SimpleLineLayoutTextFragmentIterator.cpp:
     46        (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragmentIterator):
     47        (WebCore::SimpleLineLayout::TextFragmentIterator::nextTextFragment):
     48        (WebCore::SimpleLineLayout::TextFragmentIterator::revertToFragment):
     49        (WebCore::SimpleLineLayout::TextFragmentIterator::textWidth):
     50        (WebCore::SimpleLineLayout::TextFragmentIterator::skipToNextPosition):
     51        * rendering/SimpleLineLayoutTextFragmentIterator.h:
     52        (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::TextFragment):
     53        (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::overlapsToNextRenderer):
     54        (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::isCollapsible):
     55        (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::operator==):
     56        (WebCore::SimpleLineLayout::TextFragmentIterator::isLineBreak):
     57        (WebCore::SimpleLineLayout::TextFragmentIterator::TextFragment::split): Deleted.
     58
    1592015-03-17  Jeremy Jones  <jeremyj@apple.com>
    260
  • trunk/Source/WebCore/rendering/SimpleLineLayout.cpp

    r180689 r181667  
    232232}
    233233
     234static void revertRuns(Layout::RunVector& runs, unsigned length, float width)
     235{
     236    ASSERT(runs.size());
     237    while (length) {
     238        Run& lastRun = runs.last();
     239        unsigned lastRunLength = lastRun.end - lastRun.start;
     240        if (lastRunLength > length) {
     241            lastRun.logicalRight -= width;
     242            lastRun.end -= length;
     243            break;
     244        }
     245        runs.removeLast();
     246        length -= lastRunLength;
     247        width -= (lastRun.logicalRight - lastRun.logicalLeft);
     248    }
     249}
     250
    234251class LineState {
    235252public:
    236253    void setAvailableWidth(float width) { m_availableWidth = width; }
     254    void setCollapedWhitespaceWidth(float width) { m_collapsedWhitespaceWidth = width; }
    237255    void setLogicalLeftOffset(float offset) { m_logicalLeftOffset = offset; }
    238256    void setOverflowedFragment(const TextFragmentIterator::TextFragment& fragment) { m_overflowedFragment = fragment; }
     
    242260    const TextFragmentIterator::TextFragment& overflowedFragment() const { return m_overflowedFragment; }
    243261    bool hasTrailingWhitespace() const { return m_trailingWhitespaceLength; }
     262    TextFragmentIterator::TextFragment lastFragment() const { return m_fragments.last(); }
    244263    bool isWhitespaceOnly() const { return m_trailingWhitespaceWidth && m_runsWidth == m_trailingWhitespaceWidth; }
    245264    bool fits(float extra) const { return m_availableWidth >= m_runsWidth + extra; }
    246265    bool firstCharacterFits() const { return m_firstCharacterFits; }
    247266    float width() const { return m_runsWidth; }
    248 
    249     void appendFragment(const TextFragmentIterator::TextFragment& fragment, Layout::RunVector& runs)
     267    bool isEmpty() const
     268    {
     269        if (!m_fragments.size())
     270            return true;
     271        if (!m_lastCompleteFragment.isEmpty())
     272            return false;
     273        return m_fragments.last().overlapsToNextRenderer();
     274    }
     275
     276    void appendFragmentAndCreateRunIfNeeded(const TextFragmentIterator::TextFragment& fragment, Layout::RunVector& runs)
    250277    {
    251278        // Adjust end position while collapsing.
    252279        unsigned endPosition = fragment.isCollapsed() ? fragment.start() + 1 : fragment.end();
    253 
    254         if (m_createNewRun)
     280        // New line needs new run.
     281        if (!m_runsWidth)
    255282            runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
    256283        else {
    257             ASSERT(runs.size());
    258             Run& lastRun = runs.last();
    259             lastRun.end = endPosition;
    260             lastRun.logicalRight = m_runsWidth + fragment.width();
    261         }
    262         m_createNewRun = fragment.isCollapsed();
     284            const auto& lastFragment = m_fragments.last();
     285            // Advance last completed fragment when the previous fragment is all set (including multiple parts across renderers)
     286            if ((lastFragment.type() != fragment.type()) || !lastFragment.overlapsToNextRenderer())
     287                m_lastCompleteFragment = lastFragment;
     288            // Collapse neighbouring whitespace, if they are across multiple renderers and are not collapsed yet.
     289            if (lastFragment.isCollapsible() && fragment.isCollapsible()) {
     290                ASSERT(lastFragment.isLastInRenderer());
     291                if (!lastFragment.isCollapsed()) {
     292                    // Line width needs to be reset so that now it takes collapsing into consideration.
     293                    m_runsWidth -= (lastFragment.width() - m_collapsedWhitespaceWidth);
     294                }
     295                // This fragment is collapsed completely. No run is needed.
     296                return;
     297            }
     298            if (lastFragment.isLastInRenderer() || lastFragment.isCollapsed())
     299                runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
     300            else {
     301                Run& lastRun = runs.last();
     302                lastRun.end = endPosition;
     303                lastRun.logicalRight += fragment.width();
     304            }
     305        }
     306        m_fragments.append(fragment);
    263307        m_runsWidth += fragment.width();
    264308
     
    275319    }
    276320
     321    TextFragmentIterator::TextFragment revertToLastCompleteFragment(Layout::RunVector& runs)
     322    {
     323        ASSERT(m_fragments.size());
     324        unsigned revertLength = 0;
     325        float revertWidth = 0;
     326        while (m_fragments.size()) {
     327            const auto& current = m_fragments.last();
     328            if (current == m_lastCompleteFragment)
     329                break;
     330            revertLength += current.end() - current.start();
     331            revertWidth += current.width();
     332            m_fragments.removeLast();
     333        }
     334        m_runsWidth -= revertWidth;
     335        if (revertLength)
     336            revertRuns(runs, revertLength, revertWidth);
     337        return m_lastCompleteFragment;
     338    }
     339
    277340    void removeTrailingWhitespace(Layout::RunVector& runs)
    278341    {
    279         ASSERT(runs.size());
    280         Run& lastRun = runs.last();
    281         lastRun.logicalRight -= m_trailingWhitespaceWidth;
    282         lastRun.end -= m_trailingWhitespaceLength;
    283         if (lastRun.start == lastRun.end)
    284             runs.removeLast();
    285 
     342        // FIXME: sync m_fragment, but it's ok for now as we don't use m_fragment anymore after removeTrailingWhitespace.
     343        if (!m_trailingWhitespaceLength)
     344            return;
     345        revertRuns(runs, m_trailingWhitespaceLength, m_trailingWhitespaceWidth);
    286346        m_runsWidth -= m_trailingWhitespaceWidth;
     347        m_trailingWhitespaceLength = 0;
    287348        m_trailingWhitespaceWidth = 0;
    288         m_trailingWhitespaceLength = 0;
    289349    }
    290350
     
    294354    TextFragmentIterator::TextFragment m_overflowedFragment;
    295355    float m_runsWidth { 0 };
    296     bool m_createNewRun { true };
     356    TextFragmentIterator::TextFragment m_lastCompleteFragment;
    297357    float m_trailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
    298358    unsigned m_trailingWhitespaceLength { 0 };
     359    float m_collapsedWhitespaceWidth { 0 };
    299360    // Having one character on the line does not necessarily mean it actually fits.
    300361    // First character of the first fragment might be forced on to the current line even if it does not fit.
    301362    bool m_firstCharacterFits { false };
     363    Vector<TextFragmentIterator::TextFragment> m_fragments;
    302364};
    303365
     
    390452
    391453    // Check if we need to skip the leading whitespace.
    392     if (style.collapseWhitespace && firstFragment.type() == TextFragmentIterator::TextFragment::Whitespace)
    393         firstFragment = textFragmentIterator.nextTextFragment();
     454    if (style.collapseWhitespace) {
     455        while (firstFragment.type() == TextFragmentIterator::TextFragment::Whitespace)
     456            firstFragment = textFragmentIterator.nextTextFragment();
     457    }
    394458    return firstFragment;
    395459}
    396460
     461static void forceFragmentToLine(LineState& line, TextFragmentIterator& textFragmentIterator, Layout::RunVector& runs, const TextFragmentIterator::TextFragment& fragment)
     462{
     463    line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
     464    // Check if there are more fragments to add to the current line.
     465    if (!fragment.overlapsToNextRenderer())
     466        return;
     467    auto nextFragment = textFragmentIterator.nextTextFragment();
     468    while (true) {
     469        if (nextFragment.type() != fragment.type())
     470            break;
     471        line.appendFragmentAndCreateRunIfNeeded(nextFragment, runs);
     472        // Does it overlap to the next segment?
     473        if (!nextFragment.overlapsToNextRenderer())
     474            return;
     475        nextFragment = textFragmentIterator.nextTextFragment();
     476    }
     477    line.setOverflowedFragment(nextFragment);
     478}
     479
    397480static bool createLineRuns(LineState& line, const LineState& previousLine, Layout::RunVector& runs, TextFragmentIterator& textFragmentIterator)
    398481{
    399482    const auto& style = textFragmentIterator.style();
     483    line.setCollapedWhitespaceWidth(style.spaceWidth);
    400484    bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow;
    401485    auto fragment = firstFragment(textFragmentIterator, previousLine);
     
    405489            // Add the new line fragment only if there's nothing on the line. (otherwise the extra new line character would show up at the end of the content.)
    406490            if (!line.width())
    407                 line.appendFragment(fragment, runs);
     491                line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
    408492            break;
    409493        }
     
    414498            // 3. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap)
    415499            // 4. Non-whitespace fragment when there's already another fragment on the line gets pushed to the next line.
    416             bool emptyLine = !line.width();
     500            bool emptyLine = line.isEmpty();
    417501            // Whitespace fragment.
    418502            if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
     
    420504                    // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
    421505                    line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
    422                     line.appendFragment(fragment, runs);
     506                    line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
    423507                }
    424508                // When whitespace collapse is on, whitespace that doesn't fit is simply skipped.
     
    429513                // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
    430514                line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
    431                 line.appendFragment(fragment, runs);
     515                line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
    432516                break;
    433517            }
    434518            // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though.
     519            ASSERT(fragment.type() == TextFragmentIterator::TextFragment::NonWhitespace);
    435520            if (emptyLine) {
    436                 line.appendFragment(fragment, runs);
     521                forceFragmentToLine(line, textFragmentIterator, runs, fragment);
    437522                break;
    438523            }
    439524            // Non-breakable non-whitespace fragment when there's already content on the line. Push it to the next line.
     525            if (line.lastFragment().overlapsToNextRenderer()) {
     526                // Check if this fragment is a continuation of a previous segment. In such cases, we need to remove them all.
     527                const auto& currentFragment = line.revertToLastCompleteFragment(runs);
     528                textFragmentIterator.revertToFragment(currentFragment);
     529                break;
     530            }
    440531            line.setOverflowedFragment(fragment);
    441532            break;
    442533        }
    443         line.appendFragment(fragment, runs);
     534        line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
    444535        // Find the next text fragment.
    445536        fragment = textFragmentIterator.nextTextFragment(line.width());
    446537    }
    447     return fragment.type() == TextFragmentIterator::TextFragment::ContentEnd && line.overflowedFragment().isEmpty();
     538    return (fragment.type() == TextFragmentIterator::TextFragment::ContentEnd && line.overflowedFragment().isEmpty()) || line.overflowedFragment().type() == TextFragmentIterator::TextFragment::ContentEnd;
    448539}
    449540
     
    467558}
    468559
    469 static void splitRunsAtRendererBoundary(Layout::RunVector& lineRuns, const TextFragmentIterator& textFragmentIterator)
    470 {
    471     // FIXME: We should probably split during run construction instead of as a separate pass.
    472     if (lineRuns.isEmpty())
    473         return;
    474     unsigned runIndex = 0;
    475     do {
    476         const Run& run = lineRuns.at(runIndex);
    477         ASSERT(run.start != run.end);
    478         auto& startSegment = textFragmentIterator.segmentForPosition(run.start);
    479         if (run.end <= startSegment.end)
    480             continue;
    481         // This run overlaps multiple renderers. Split it up.
    482         // Split run at the renderer's boundary and create a new run for the left side, while use the current run as the right side.
    483         float logicalRightOfLeftRun = run.logicalLeft + textFragmentIterator.textWidth(run.start, startSegment.end, run.logicalLeft);
    484         lineRuns.insert(runIndex, Run(run.start, startSegment.end, run.logicalLeft, logicalRightOfLeftRun, false));
    485         Run& rightSideRun = lineRuns.at(runIndex + 1);
    486         rightSideRun.start = startSegment.end;
    487         rightSideRun.logicalLeft = logicalRightOfLeftRun;
    488     } while (++runIndex < lineRuns.size());
    489 }
    490 
    491560static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
    492561{
     
    506575        closeLineEndingAndAdjustRuns(line, runs, previousRunCount, lineCount, textFragmentIterator);
    507576    } while (!isEndOfContent);
    508 
    509     if (flow.firstChild() != flow.lastChild())
    510         splitRunsAtRendererBoundary(runs, textFragmentIterator);
    511577}
    512578
  • trunk/Source/WebCore/rendering/SimpleLineLayoutFlowContents.h

    r181290 r181667  
    5656
    5757        Iterator& operator++();
     58        Iterator& operator--();
    5859        bool operator==(const Iterator& other) const;
    5960        bool operator!=(const Iterator& other) const;
    60         const Segment& operator*() const { return m_flowContents.m_segments[m_segmentIndex]; }
     61        const Segment& operator*() const;
     62        const Segment* operator->() const;
    6163
    6264    private:
     
    6870    Iterator end() const { return Iterator(*this, m_segments.size()); }
    6971
    70     unsigned length() const { return m_segments.last().end; };
    71 
    7272    unsigned segmentIndexForPosition(unsigned position) const;
    7373
    7474private:
    7575    unsigned segmentIndexForPositionSlow(unsigned position) const;
    76 
    7776    const Vector<Segment, 8> m_segments;
    78 
    7977    mutable unsigned m_lastSegmentIndex;
    8078};
     
    8381{
    8482    ++m_segmentIndex;
     83    return *this;
     84}
     85
     86inline FlowContents::Iterator& FlowContents::Iterator::operator--()
     87{
     88    --m_segmentIndex;
    8589    return *this;
    8690}
     
    9498{
    9599    return !(*this == other);
     100}
     101
     102inline const FlowContents::Segment& FlowContents::Iterator::operator*() const
     103{
     104    ASSERT(m_segmentIndex < m_flowContents.m_segments.size());
     105    return m_flowContents.m_segments[m_segmentIndex];
     106}
     107
     108inline const FlowContents::Segment* FlowContents::Iterator::operator->() const
     109{
     110    ASSERT(m_segmentIndex < m_flowContents.m_segments.size());
     111    return &(m_flowContents.m_segments[m_segmentIndex]);
    96112}
    97113
  • trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp

    r181505 r181667  
    8585        TextRun textRun(run.text());
    8686        textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
     87        textRun.setXPos(run.rect().x());
    8788        FloatPoint textOrigin = run.baseline() + paintOffset;
    8889        textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), flow.document().deviceScaleFactor()));
  • trunk/Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.cpp

    r181325 r181667  
    4949TextFragmentIterator::TextFragmentIterator(const RenderBlockFlow& flow)
    5050    : m_flowContents(flow)
    51     , m_lineBreakIterator((*m_flowContents.begin()).text, flow.style().locale())
     51    , m_currentSegment(m_flowContents.begin())
     52    , m_lineBreakIterator(m_currentSegment->text, flow.style().locale())
    5253    , m_style(flow.style())
    5354{
     
    6162    // 3. non-whitespace characters.
    6263    // 4. empty, indicating content end.
    63     if (isEnd(m_position))
     64    ASSERT(m_currentSegment != m_flowContents.end());
     65    if (m_position == m_currentSegment->end)
     66        ++m_currentSegment;
     67    // Is it content end?
     68    if (m_currentSegment == m_flowContents.end())
    6469        return TextFragment(m_position, m_position, 0, TextFragment::ContentEnd);
     70    unsigned segmentEndPosition = m_currentSegment->end;
     71    unsigned startPosition = m_position;
    6572    if (isLineBreak(m_position)) {
    66         TextFragment fragment(m_position, m_position + 1, 0, TextFragment::LineBreak);
    67         ++m_position;
    68         return fragment;
    69     }
    70     unsigned startPosition = m_position;
    71     unsigned endPosition = skipToNextPosition(PositionType::NonWhitespace, startPosition);
     73        unsigned endPosition = ++m_position;
     74        return TextFragment(startPosition, endPosition, 0, TextFragment::LineBreak);
     75    }
     76    float width = 0;
     77    bool overlappingFragment = false;
     78    unsigned endPosition = skipToNextPosition(PositionType::NonWhitespace, startPosition, width, xPosition, overlappingFragment);
    7279    ASSERT(startPosition <= endPosition);
    73     if (endPosition > startPosition) {
     80    if (startPosition < endPosition) {
    7481        bool multipleWhitespace = startPosition + 1 < endPosition;
    7582        bool isCollapsed = multipleWhitespace && m_style.collapseWhitespace;
    7683        bool isBreakable = !isCollapsed && multipleWhitespace;
    77         float width = isCollapsed ? m_style.spaceWidth : textWidth(startPosition, endPosition, xPosition);
    7884        m_position = endPosition;
    79         return TextFragment(startPosition, endPosition, width, TextFragment::Whitespace, isCollapsed, isBreakable);
    80     }
    81     endPosition = skipToNextPosition(PositionType::Breakable, startPosition + 1);
     85        return TextFragment(startPosition, endPosition, width, TextFragment::Whitespace, endPosition == segmentEndPosition, false, isCollapsed, m_style.collapseWhitespace, isBreakable);
     86    }
     87    endPosition = skipToNextPosition(PositionType::Breakable, startPosition, width, xPosition, overlappingFragment);
    8288    m_position = endPosition;
    83     return TextFragment(startPosition, endPosition, textWidth(startPosition, endPosition, xPosition), TextFragment::NonWhitespace, false, m_style.breakWordOnOverflow);
    84 }
    85 
    86 float TextFragmentIterator::textWidth(unsigned from, unsigned to, float xPosition) const
    87 {
    88     const auto& fromSegment = m_flowContents.segmentForPosition(from);
    89     ASSERT(is<RenderText>(fromSegment.renderer));
    90     if ((m_style.font.isFixedPitch() && fromSegment.end >= to) || (from == fromSegment.start && to == fromSegment.end))
    91         return downcast<RenderText>(fromSegment.renderer).width(from - fromSegment.start, to - from, m_style.font, xPosition, nullptr, nullptr);
    92 
    93     const auto* segment = &fromSegment;
    94     float textWidth = 0;
    95     unsigned fragmentEnd = 0;
    96     while (true) {
    97         fragmentEnd = std::min(to, segment->end);
    98         textWidth += segment->text.is8Bit() ? runWidth<LChar>(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth) :
    99             runWidth<UChar>(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth);
    100         if (fragmentEnd == to)
    101             break;
    102         from = fragmentEnd;
    103         segment = &m_flowContents.segmentForPosition(fragmentEnd);
    104     };
    105 
    106     return textWidth;
     89    return TextFragment(startPosition, endPosition, width, TextFragment::NonWhitespace, endPosition == segmentEndPosition, overlappingFragment, false, false, m_style.breakWordOnOverflow);
     90}
     91
     92void TextFragmentIterator::revertToFragment(const TextFragment& fragment)
     93{
     94    ASSERT(m_position >= fragment.end());
     95    ASSERT(fragment.start() >= 0);
     96    // Revert segment first.
     97    while (m_currentSegment->start > fragment.start())
     98        --m_currentSegment;
     99    // TODO: It reverts to the last fragment on the same position, but that's ok for now as we don't need to
     100    // differentiate multiple renderers on the same position.
     101    m_position = fragment.start();
    107102}
    108103
    109104template <typename CharacterType>
    110 static unsigned nextBreakablePosition(LazyLineBreakIterator& lineBreakIterator, const FlowContents::Segment& segment, unsigned startPosition)
    111 {
    112     return nextBreakablePositionNonLoosely<CharacterType, NBSPBehavior::IgnoreNBSP>(lineBreakIterator, segment.text.characters<CharacterType>(), segment.end - segment.start, startPosition);
     105unsigned TextFragmentIterator::nextBreakablePosition(const FlowContents::Segment& segment, unsigned startPosition)
     106{
     107    ASSERT(startPosition < segment.end);
     108    if (segment.text.impl() != m_lineBreakIterator.string().impl()) {
     109        const String& currentText = m_lineBreakIterator.string();
     110        unsigned textLength = currentText.length();
     111        UChar lastCharacter = textLength > 0 ? currentText[textLength - 1] : 0;
     112        UChar secondToLastCharacter = textLength > 1 ? currentText[textLength - 2] : 0;
     113        m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
     114        m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorModeUAX14);
     115    }
     116    const auto* characters = segment.text.characters<CharacterType>();
     117    unsigned segmentLength = segment.end - segment.start;
     118    unsigned segmentPosition = startPosition - segment.start;
     119    return segment.start + nextBreakablePositionNonLoosely<CharacterType, NBSPBehavior::IgnoreNBSP>(m_lineBreakIterator, characters, segmentLength, segmentPosition);
    113120}
    114121
    115122template <typename CharacterType>
    116 static unsigned nextNonWhitespacePosition(const FlowContents::Segment& segment, unsigned startPosition, const TextFragmentIterator::Style& style)
    117 {
     123unsigned TextFragmentIterator::nextNonWhitespacePosition(const FlowContents::Segment& segment, unsigned startPosition)
     124{
     125    ASSERT(startPosition < segment.end);
    118126    const auto* text = segment.text.characters<CharacterType>();
    119127    unsigned position = startPosition;
    120     unsigned length = segment.end - segment.start;
    121     for (; position < length; ++position) {
    122         auto character = text[position];
    123         bool isWhitespace = character == ' ' || character == '\t' || (!style.preserveNewline && character == '\n');
     128    for (; position < segment.end; ++position) {
     129        auto character = text[position - segment.start];
     130        bool isWhitespace = character == ' ' || character == '\t' || (!m_style.preserveNewline && character == '\n');
    124131        if (!isWhitespace)
    125132            return position;
     
    128135}
    129136
    130 unsigned TextFragmentIterator::skipToNextPosition(PositionType positionType, unsigned startPosition) const
    131 {
    132     if (isEnd(startPosition))
    133         return startPosition;
    134 
     137float TextFragmentIterator::textWidth(unsigned from, unsigned to, float xPosition) const
     138{
     139    auto& segment = *m_currentSegment;
     140    ASSERT(segment.start <= from && from <= segment.end && segment.start <= to && to <= segment.end);
     141    ASSERT(is<RenderText>(segment.renderer));
     142    if (m_style.font.isFixedPitch() || (from == segment.start && to == segment.end))
     143        return downcast<RenderText>(segment.renderer).width(from - segment.start, to - from, m_style.font, xPosition, nullptr, nullptr);
     144    return segment.text.is8Bit() ? runWidth<LChar>(segment, from, to, xPosition) : runWidth<UChar>(segment, from, to, xPosition);
     145}
     146
     147unsigned TextFragmentIterator::skipToNextPosition(PositionType positionType, unsigned startPosition, float& width, float xPosition, bool& overlappingFragment)
     148{
     149    overlappingFragment = false;
    135150    unsigned currentPosition = startPosition;
    136     FlowContents::Iterator it(m_flowContents, m_flowContents.segmentIndexForPosition(currentPosition));
    137     for (auto end = m_flowContents.end(); it != end; ++it) {
    138         auto& segment = *it;
    139         unsigned currentPositonRelativeToSegment = currentPosition - segment.start;
    140         unsigned nextPositionRelativeToSegment = 0;
    141         if (positionType == NonWhitespace) {
    142             nextPositionRelativeToSegment = segment.text.is8Bit() ? nextNonWhitespacePosition<LChar>(segment, currentPositonRelativeToSegment, m_style) :
    143                 nextNonWhitespacePosition<UChar>(segment, currentPositonRelativeToSegment, m_style);
    144         } else if (positionType == Breakable) {
    145             if (segment.text.impl() != m_lineBreakIterator.string().impl()) {
    146                 UChar lastCharacter = segment.start > 0 ? characterAt(segment.start - 1) : 0;
    147                 UChar secondToLastCharacter = segment.start > 1 ? characterAt(segment.start - 2) : 0;
    148                 m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
    149                 m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorModeUAX14);
    150             }
    151             nextPositionRelativeToSegment = segment.text.is8Bit() ? nextBreakablePosition<LChar>(m_lineBreakIterator, segment, currentPositonRelativeToSegment) :
    152                 nextBreakablePosition<UChar>(m_lineBreakIterator, segment, currentPositonRelativeToSegment);
    153         } else
    154             ASSERT_NOT_REACHED();
    155         currentPosition = segment.start + nextPositionRelativeToSegment;
    156         if (currentPosition < segment.end)
    157             break;
    158     }
    159     return currentPosition;
     151    unsigned nextPosition = currentPosition;
     152    // Collapsed whitespace has constant width. Do not measure it.
     153    if (positionType == NonWhitespace)
     154        nextPosition = m_currentSegment->text.is8Bit() ? nextNonWhitespacePosition<LChar>(*m_currentSegment, currentPosition) : nextNonWhitespacePosition<UChar>(*m_currentSegment, currentPosition);
     155    else if (positionType == Breakable) {
     156        // nextBreakablePosition returns the same position for certain characters such as hyphens. Call next again with modified position unless it's the end of the segment.
     157        nextPosition = m_currentSegment->text.is8Bit() ? nextBreakablePosition<LChar>(*m_currentSegment, currentPosition) : nextBreakablePosition<UChar>(*m_currentSegment, currentPosition);
     158        // We need to know whether the word actually finishes at the end of this renderer or not.
     159        if (nextPosition == m_currentSegment->end) {
     160            auto nextSegment = m_currentSegment;
     161            ++nextSegment;
     162            if (nextSegment != m_flowContents.end())
     163                overlappingFragment = nextPosition < (nextSegment->text.is8Bit() ? nextBreakablePosition<LChar>(*nextSegment, nextPosition) : nextBreakablePosition<UChar>(*nextSegment, nextPosition));
     164        } else if (nextPosition == currentPosition) {
     165            if (++nextPosition < m_currentSegment->end)
     166                nextPosition = m_currentSegment->text.is8Bit() ? nextBreakablePosition<LChar>(*m_currentSegment, currentPosition + 1) : nextBreakablePosition<UChar>(*m_currentSegment, currentPosition + 1);
     167        }
     168    }
     169    width = 0;
     170    if (nextPosition == currentPosition)
     171        return currentPosition;
     172    bool measureText = positionType != NonWhitespace || !m_style.collapseWhitespace;
     173    if (measureText) {
     174        float textWidth = this->textWidth(currentPosition, nextPosition, xPosition);
     175        xPosition += textWidth;
     176        width += textWidth;
     177    } else if (startPosition < nextPosition)
     178        width = m_style.spaceWidth;
     179    return nextPosition;
    160180}
    161181
    162182template <typename CharacterType>
    163 float TextFragmentIterator::runWidth(const String& text, unsigned from, unsigned to, float xPosition) const
    164 {
    165     ASSERT(from <= to);
    166     if (from == to)
     183float TextFragmentIterator::runWidth(const FlowContents::Segment& segment, unsigned startPosition, unsigned endPosition, float xPosition) const
     184{
     185    ASSERT(startPosition <= endPosition);
     186    if (startPosition == endPosition)
    167187        return 0;
    168     bool measureWithEndSpace = m_style.collapseWhitespace && to < text.length() && text[to] == ' ';
     188    unsigned segmentFrom = startPosition - segment.start;
     189    unsigned segmentTo = endPosition - segment.start;
     190    bool measureWithEndSpace = m_style.collapseWhitespace && segmentTo < segment.text.length() && segment.text[segmentTo] == ' ';
    169191    if (measureWithEndSpace)
    170         ++to;
    171     TextRun run(text.characters<CharacterType>() + from, to - from);
     192        ++segmentTo;
     193    TextRun run(segment.text.characters<CharacterType>() + segmentFrom, segmentTo - segmentFrom);
    172194    run.setXPos(xPosition);
    173195    run.setTabSize(!!m_style.tabWidth, m_style.tabWidth);
  • trunk/Source/WebCore/rendering/SimpleLineLayoutTextFragmentIterator.h

    r181325 r181667  
    4444        enum Type { ContentEnd, LineBreak, Whitespace, NonWhitespace };
    4545        TextFragment() = default;
    46         TextFragment(unsigned start, unsigned end, float width, Type type, bool isCollapsed = false, bool isBreakable = false)
     46        TextFragment(unsigned start, unsigned end, float width, Type type, bool isLastInRenderer = false, bool overlapsToNextRenderer = false, bool isCollapsed = false, bool isCollapsible = false, bool isBreakable = false)
    4747            : m_start(start)
    4848            , m_end(end)
     49            , m_width(width)
    4950            , m_type(type)
    50             , m_width(width)
     51            , m_isLastInRenderer(isLastInRenderer)
     52            , m_overlapsToNextRenderer(overlapsToNextRenderer)
    5153            , m_isCollapsed(isCollapsed)
     54            , m_isCollapsible(isCollapsible)
    5255            , m_isBreakable(isBreakable)
    5356        {
     
    5861        float width() const { return m_width; }
    5962        Type type() const { return m_type; }
     63        bool isLastInRenderer() const { return m_isLastInRenderer; }
     64        bool overlapsToNextRenderer() const { return m_overlapsToNextRenderer; }
    6065        bool isCollapsed() const { return m_isCollapsed; }
     66        bool isCollapsible() const { return m_isCollapsible; }
    6167        bool isBreakable() const { return m_isBreakable; }
    6268
    6369        bool isEmpty() const { return start() == end(); }
    6470        TextFragment split(unsigned splitPosition, const TextFragmentIterator&);
     71        bool operator==(const TextFragment& other) const
     72        {
     73            return m_start == other.m_start
     74                && m_end == other.m_end
     75                && m_width == other.m_width
     76                && m_type == other.m_type
     77                && m_isLastInRenderer == other.m_isLastInRenderer
     78                && m_overlapsToNextRenderer == other.m_overlapsToNextRenderer
     79                && m_isCollapsed == other.m_isCollapsed
     80                && m_isCollapsible == other.m_isCollapsible
     81                && m_isBreakable == other.m_isBreakable;
     82        }
    6583
    6684    private:
    6785        unsigned m_start { 0 };
    6886        unsigned m_end { 0 };
     87        float m_width { 0 };
    6988        Type m_type { NonWhitespace };
    70         float m_width { 0 };
     89        bool m_isLastInRenderer { false };
     90        bool m_overlapsToNextRenderer { false };
    7191        bool m_isCollapsed { false };
     92        bool m_isCollapsible { false };
    7293        bool m_isBreakable { false };
    7394    };
    7495    TextFragment nextTextFragment(float xPosition = 0);
    75     float textWidth(unsigned from, unsigned to, float xPosition) const;
     96    void revertToFragment(const TextFragment&);
     97    float textWidth(unsigned startPosition, unsigned endPosition, float xPosition) const;
    7698
    7799    struct Style {
     
    89111    };
    90112    const Style& style() const { return m_style; }
    91     // FIXME: remove splitRunsAtRendererBoundary()
    92     const FlowContents::Segment& segmentForPosition(unsigned position) const { return m_flowContents.segmentForPosition(position); };
    93113
    94114private:
    95115    enum PositionType { Breakable, NonWhitespace };
    96     unsigned skipToNextPosition(PositionType, unsigned startPosition) const;
    97     UChar characterAt(unsigned position) const;
     116    unsigned skipToNextPosition(PositionType, unsigned startPosition, float& width, float xPosition, bool& overlappingFragment);
    98117    bool isLineBreak(unsigned position) const;
    99     bool isEnd(unsigned position) const;
    100     template <typename CharacterType> float runWidth(const String&, unsigned from, unsigned to, float xPosition) const;
     118    template <typename CharacterType> unsigned nextBreakablePosition(const FlowContents::Segment&, unsigned startPosition);
     119    template <typename CharacterType> unsigned nextNonWhitespacePosition(const FlowContents::Segment&, unsigned startPosition);
     120    template <typename CharacterType> float runWidth(const FlowContents::Segment&, unsigned startPosition, unsigned endPosition, float xPosition) const;
    101121
    102122    FlowContents m_flowContents;
    103     mutable LazyLineBreakIterator m_lineBreakIterator;
     123    FlowContents::Iterator m_currentSegment;
     124    LazyLineBreakIterator m_lineBreakIterator;
    104125    const Style m_style;
    105126    unsigned m_position { 0 };
     
    128149}
    129150
    130 inline UChar TextFragmentIterator::characterAt(unsigned position) const
    131 {
    132     auto& segment = m_flowContents.segmentForPosition(position);
    133     return segment.text[position - segment.start];
    134 }
    135 
    136151inline bool TextFragmentIterator::isLineBreak(unsigned position) const
    137152{
    138     if (isEnd(position))
    139         return false;
    140     return m_style.preserveNewline && characterAt(position) == '\n';
    141 }
    142 
    143 inline bool TextFragmentIterator::isEnd(unsigned position) const
    144 {
    145     return position >= m_flowContents.length();
     153    const auto& segment = *m_currentSegment;
     154    ASSERT(segment.start <= position && position < segment.end);
     155    return m_style.preserveNewline && segment.text[position - segment.start] == '\n';
    146156}
    147157
Note: See TracChangeset for help on using the changeset viewer.