Changeset 182325 in webkit


Ignore:
Timestamp:
Apr 3, 2015, 11:27:55 AM (10 years ago)
Author:
Alan Bujtas
Message:

Simple line layout: Use pre-computed simple line runs to produce innerText content.
https://bugs.webkit.org/show_bug.cgi?id=143351

Reviewed by Antti Koivisto.

This is in preparation to cover <br> with simple line layout.
Runs provide line layout information that can be used to cover hard line break cases.
(similar to TextIterator::handleTextBox())

Covered by existing tests.

  • editing/TextIterator.cpp:

(WebCore::TextIterator::handleTextNode):
(WebCore::TextIterator::emitCharacter):
(WebCore::TextIterator::emitText):

  • editing/TextIterator.h:
  • rendering/SimpleLineLayoutResolver.h:

(WebCore::SimpleLineLayout::RunResolver::Run::isEndOfLine):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r182321 r182325  
     12015-04-03  Zalan Bujtas  <zalan@apple.com>
     2
     3        Simple line layout: Use pre-computed simple line runs to produce innerText content.
     4        https://bugs.webkit.org/show_bug.cgi?id=143351
     5
     6        Reviewed by Antti Koivisto.
     7
     8        This is in preparation to cover <br> with simple line layout.
     9        Runs provide line layout information that can be used to cover hard line break cases.
     10        (similar to TextIterator::handleTextBox())
     11
     12        Covered by existing tests.
     13
     14        * editing/TextIterator.cpp:
     15        (WebCore::TextIterator::handleTextNode):
     16        (WebCore::TextIterator::emitCharacter):
     17        (WebCore::TextIterator::emitText):
     18        * editing/TextIterator.h:
     19        * rendering/SimpleLineLayoutResolver.h:
     20        (WebCore::SimpleLineLayout::RunResolver::Run::isEndOfLine):
     21
    1222015-04-03  Antti Koivisto  <antti@apple.com>
    223
  • trunk/Source/WebCore/editing/TextIterator.cpp

    r179143 r182325  
    4545#include "RenderTextFragment.h"
    4646#include "ShadowRoot.h"
     47#include "SimpleLineLayout.h"
     48#include "SimpleLineLayoutResolver.h"
    4749#include "TextBoundaries.h"
    4850#include "TextBreakIterator.h"
     
    508510
    509511    auto& renderer = *textNode.renderer();
    510        
     512    const auto* previousTextNode = m_lastTextNode;
    511513    m_lastTextNode = &textNode;
    512514    String rendererText = renderer.text();
     
    542544    }
    543545
    544     if (renderer.simpleLineLayout()) {
     546    if (const auto* layout = renderer.simpleLineLayout()) {
    545547        if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
    546548            return true;
    547         // This code aims to produce same results as handleTextBox() below so test results don't change. It does not make much logical sense.
    548         unsigned end = (m_node == m_endContainer) ? static_cast<unsigned>(m_endOffset) : rendererText.length();
    549         unsigned runEnd = m_offset;
    550         unsigned runStart = m_offset;
    551         while (runEnd < end && (renderer.style().isCollapsibleWhiteSpace(rendererText[runEnd]) || rendererText[runEnd] == '\t'))
    552             ++runEnd;
    553         bool addSpaceForPrevious = m_lastTextNodeEndedWithCollapsedSpace && m_lastCharacter && !renderer.style().isCollapsibleWhiteSpace(m_lastCharacter);
    554         if (runEnd > runStart || addSpaceForPrevious) {
    555             if (runEnd == rendererText.length()) {
    556                 m_lastTextNodeEndedWithCollapsedSpace = true;
    557                 return true;
    558             }
    559             bool addSpaceForCurrent = runStart || (m_lastCharacter && !renderer.style().isCollapsibleWhiteSpace(m_lastCharacter));
    560             if (addSpaceForCurrent || addSpaceForPrevious) {
    561                 emitCharacter(' ', textNode, nullptr, runStart, runEnd);
    562                 m_offset = runEnd;
     549        // Use the simple layout runs to iterate over the text content.
     550        ASSERT(renderer.parent() && is<RenderBlockFlow>(renderer.parent()));
     551        const auto& blockFlow = downcast<RenderBlockFlow>(*renderer.parent());
     552        SimpleLineLayout::RunResolver runResolver(blockFlow, *layout);
     553        auto range = runResolver.rangeForRenderer(renderer);
     554        unsigned endPosition = (m_node == m_endContainer) ? static_cast<unsigned>(m_endOffset) : rendererText.length();
     555        // Simple line layout run positions are all absolute to the parent flow.
     556        // Offsetting is required when multiple renderers are present.
     557        if (previousTextNode && previousTextNode != &textNode) {
     558            const RenderObject& previousRenderer = *previousTextNode->renderer();
     559            if (previousRenderer.parent() != &blockFlow)
     560                m_previousTextLengthInFlow = 0;
     561            else
     562                m_previousTextLengthInFlow += previousTextNode->renderer()->text()->length();
     563        }
     564        // Skip to m_offset position.
     565        auto it = range.begin();
     566        auto end = range.end();
     567        while (it != end && (*it).end() <= (static_cast<unsigned>(m_offset) + m_previousTextLengthInFlow))
     568            ++it;
     569        if (it == end) {
     570            // Collapsed trailing whitespace.
     571            m_offset = endPosition;
     572            m_lastTextNodeEndedWithCollapsedSpace = true;
     573            return true;
     574        }
     575        if (m_nextRunNeedsWhitespace) {
     576            emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
     577            return false;
     578        }
     579        const auto run = *it;
     580        ASSERT(run.end() - run.start() <= rendererText.length());
     581        // contentStart skips leading whitespace.
     582        unsigned contentStart = std::max<unsigned>(m_offset, run.start() - m_previousTextLengthInFlow);
     583        unsigned contentEnd = std::min(endPosition, run.end() - m_previousTextLengthInFlow);
     584        // Check if whitespace adjustment is needed when crossing renderer boundary.
     585        if (previousTextNode && previousTextNode != &textNode) {
     586            bool lastCharacterIsNotWhitespace = m_lastCharacter && !renderer.style().isCollapsibleWhiteSpace(m_lastCharacter);
     587            bool addTrailingWhitespaceForPrevious = m_lastTextNodeEndedWithCollapsedSpace && lastCharacterIsNotWhitespace;
     588            bool leadingWhitespaceIsNeededForCurrent = contentStart > static_cast<unsigned>(m_offset) && lastCharacterIsNotWhitespace;
     589            if (addTrailingWhitespaceForPrevious || leadingWhitespaceIsNeededForCurrent) {
     590                emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
    563591                return false;
    564592            }
    565             runStart = runEnd;
    566         }
    567         while (runEnd < end && !renderer.style().isCollapsibleWhiteSpace(rendererText[runEnd]))
    568             ++runEnd;
    569         if (runStart < end)
    570             emitText(textNode, renderer, runStart, runEnd);
    571         m_offset = runEnd;
    572         return runEnd == end;
     593        }
     594        // \n \t single whitespace characters need replacing so that the new line/tab character won't show up.
     595        unsigned stopPosition = contentStart;
     596        while (stopPosition < contentEnd) {
     597            if (rendererText[stopPosition] == '\n' || rendererText[stopPosition] == '\t') {
     598                emitText(textNode, renderer, contentStart, stopPosition);
     599                m_offset = stopPosition + 1;
     600                m_nextRunNeedsWhitespace = true;
     601                return false;
     602            }
     603            ++stopPosition;
     604        }
     605        emitText(textNode, renderer, contentStart, contentEnd);
     606        // When line ending with collapsed whitespace is present, we need to carry over one whitespace: foo\nbar -> foo bar (otherwise we would end up with foobar).
     607        m_nextRunNeedsWhitespace = run.isEndOfLine() && contentEnd < endPosition && renderer.style().isCollapsibleWhiteSpace(rendererText[contentEnd]);
     608        m_offset = contentEnd;
     609        return static_cast<unsigned>(m_offset) == endPosition;
    573610    }
    574611
     
    10661103    m_lastCharacter = character;
    10671104    m_lastTextNodeEndedWithCollapsedSpace = false;
     1105    m_nextRunNeedsWhitespace = false;
    10681106}
    10691107
     
    10901128
    10911129    m_lastTextNodeEndedWithCollapsedSpace = false;
     1130    m_nextRunNeedsWhitespace = false;
    10921131    m_hasEmitted = true;
    10931132}
  • trunk/Source/WebCore/editing/TextIterator.h

    r177588 r182325  
    164164    UChar m_lastCharacter;
    165165
     166    // Used to do simple line layout run logic.
     167    bool m_nextRunNeedsWhitespace { false };
     168    unsigned m_previousTextLengthInFlow { 0 };
    166169    // Used when text boxes are out of order (Hebrew/Arabic with embedded LTR text)
    167170    Vector<InlineTextBox*> m_sortedTextBoxes;
  • trunk/Source/WebCore/rendering/SimpleLineLayoutResolver.h

    r181290 r182325  
    6464        FloatPoint baseline() const;
    6565        StringView text() const;
     66        bool isEndOfLine() const;
    6667
    6768        unsigned lineIndex() const;
     
    159160}
    160161
     162inline bool RunResolver::Run::isEndOfLine() const
     163{
     164    return m_iterator.simpleRun().isEndOfLine;
     165}
     166
    161167inline unsigned RunResolver::Run::lineIndex() const
    162168{
Note: See TracChangeset for help on using the changeset viewer.