Changeset 159030 in webkit


Ignore:
Timestamp:
Nov 10, 2013 2:17:32 PM (11 years ago)
Author:
Antti Koivisto
Message:

Implement white-space property on simple line layout path
https://bugs.webkit.org/show_bug.cgi?id=124122

Source/WebCore:

Reviewed by Andreas Kling.

Support all values of the white-space property and the tab-size property.

Tests: fast/forms/basic-textareas-simple-lines.html

fast/text/embed-at-end-of-pre-wrap-line-simple-lines.html
fast/text/whitespace/pre-wrap-line-test-simple-lines.html
fast/text/whitespace/pre-wrap-long-word-simple-lines.html
fast/text/whitespace/pre-wrap-spaces-after-newline-simple-lines.html

  • rendering/SimpleLineLayout.cpp:

(WebCore::SimpleLineLayout::canUseFor):
(WebCore::SimpleLineLayout::isWhitespace):
(WebCore::SimpleLineLayout::skipWhitespaces):
(WebCore::SimpleLineLayout::textWidth):
(WebCore::SimpleLineLayout::measureWord):
(WebCore::SimpleLineLayout::createTextRuns):

  • rendering/SimpleLineLayoutFunctions.cpp:

(WebCore::SimpleLineLayout::paintDebugBorders):
(WebCore::SimpleLineLayout::paintFlow):

LayoutTests:

Reviewed by Andreas Kling.

The simple line layout produces slightly different runs in some pre-wrap cases compared
to the line box path (with less unnecessary boxes). To keep the test coverage this patch forces the
existing render tree dump based tests to use line boxes. It also adds new ref tests for
the same cases where the test uses the simple line path and the ref is forced on the line box path.
This ensures that the paths produce pixel-identical results.

  • fast/forms/basic-textareas-simple-lines-expected.html: Added.
  • fast/forms/basic-textareas-simple-lines.html: Added.
  • fast/forms/basic-textareas.html:
  • fast/text/embed-at-end-of-pre-wrap-line-simple-lines-expected.html: Added.
  • fast/text/embed-at-end-of-pre-wrap-line-simple-lines.html: Added.
  • fast/text/embed-at-end-of-pre-wrap-line.html:
  • fast/text/whitespace/pre-wrap-line-test-simple-lines-expected.html: Added.
  • fast/text/whitespace/pre-wrap-line-test-simple-lines.html: Added.
  • fast/text/whitespace/pre-wrap-line-test.html:
  • fast/text/whitespace/pre-wrap-long-word-simple-lines-expected.html: Added.
  • fast/text/whitespace/pre-wrap-long-word-simple-lines.html: Added. New simple test for overflowing lines which was only covered by the very large basic-textareas.html.
  • fast/text/whitespace/pre-wrap-spaces-after-newline-simple-lines-expected.html: Added.
  • fast/text/whitespace/pre-wrap-spaces-after-newline-simple-lines.html: Added.
  • fast/text/whitespace/pre-wrap-spaces-after-newline.html:
Location:
trunk
Files:
10 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r159019 r159030  
     12013-11-10  Antti Koivisto  <antti@apple.com>
     2
     3        Implement white-space property on simple line layout path
     4        https://bugs.webkit.org/show_bug.cgi?id=124122
     5
     6        Reviewed by Andreas Kling.
     7       
     8        The simple line layout produces slightly different runs in some pre-wrap cases compared
     9        to the line box path (with less unnecessary boxes). To keep the test coverage this patch forces the
     10        existing render tree dump based tests to use line boxes. It also adds new ref tests for
     11        the same cases where the test uses the simple line path and the ref is forced on the line box path.
     12        This ensures that the paths produce pixel-identical results.
     13
     14        * fast/forms/basic-textareas-simple-lines-expected.html: Added.
     15        * fast/forms/basic-textareas-simple-lines.html: Added.
     16        * fast/forms/basic-textareas.html:
     17        * fast/text/embed-at-end-of-pre-wrap-line-simple-lines-expected.html: Added.
     18        * fast/text/embed-at-end-of-pre-wrap-line-simple-lines.html: Added.
     19        * fast/text/embed-at-end-of-pre-wrap-line.html:
     20        * fast/text/whitespace/pre-wrap-line-test-simple-lines-expected.html: Added.
     21        * fast/text/whitespace/pre-wrap-line-test-simple-lines.html: Added.
     22        * fast/text/whitespace/pre-wrap-line-test.html:
     23        * fast/text/whitespace/pre-wrap-long-word-simple-lines-expected.html: Added.
     24        * fast/text/whitespace/pre-wrap-long-word-simple-lines.html: Added. New simple test for overflowing lines which was only covered by the very large basic-textareas.html.
     25        * fast/text/whitespace/pre-wrap-spaces-after-newline-simple-lines-expected.html: Added.
     26        * fast/text/whitespace/pre-wrap-spaces-after-newline-simple-lines.html: Added.
     27        * fast/text/whitespace/pre-wrap-spaces-after-newline.html:
     28
    1292013-11-10  Andreas Kling  <akling@apple.com>
    230
  • trunk/LayoutTests/fast/forms/basic-textareas.html

    r48761 r159030  
    22<body>
    33<script>
     4// Force line box path.
     5if (window.internals)
     6    internals.settings.setSimpleLineLayoutEnabled(false);
    47var docToAppendTo;
    58function addTextarea(properties, opt_innerHTML) {
  • trunk/LayoutTests/fast/text/embed-at-end-of-pre-wrap-line.html

    r30664 r159030  
     1<script>
     2// Force line box path.
     3if (window.internals)
     4    internals.settings.setSimpleLineLayoutEnabled(false);
     5</script>
    16<style>
    27    div { white-space: pre-wrap; border: 1px solid; padding: 4px; width: 70px; margin: 8px 0; }
  • trunk/LayoutTests/fast/text/whitespace/pre-wrap-line-test.html

    r21383 r159030  
    1                                                                        
     1<script>
     2// Force line box path.
     3if (window.internals)
     4    internals.settings.setSimpleLineLayoutEnabled(false);
     5</script>
    26<table><tr><td><div style="white-space:pre-wrap">Three cheers    for OldVet and the letter he wrote to Senator Dodd (see above Comment).  We all need to be proactive and contact our senators and representatives to let them know our strong feelings on this subject.  I would lose what little faith I have left in our government if they engineered a tax payer bailout.
  • trunk/LayoutTests/fast/text/whitespace/pre-wrap-spaces-after-newline.html

    r48921 r159030  
     1<script>
     2// Force line box path.
     3if (window.internals)
     4    internals.settings.setSimpleLineLayoutEnabled(false);
     5</script>
    16<style>
    27    pre { white-space: pre-wrap; background: silver; width: 7ex; }
  • trunk/LayoutTests/platform/mac/compositing/repaint/invalidations-on-composited-layers-expected.txt

    r146531 r159030  
    11(repaint rects
     2  (rect 8 13 784 15)
    23  (rect 8 13 784 15)
    34  (rect 8 413 784 28)
  • trunk/Source/WebCore/ChangeLog

    r159029 r159030  
     12013-11-10  Antti Koivisto  <antti@apple.com>
     2
     3        Implement white-space property on simple line layout path
     4        https://bugs.webkit.org/show_bug.cgi?id=124122
     5
     6        Reviewed by Andreas Kling.
     7       
     8        Support all values of the white-space property and the tab-size property.
     9
     10        Tests: fast/forms/basic-textareas-simple-lines.html
     11               fast/text/embed-at-end-of-pre-wrap-line-simple-lines.html
     12               fast/text/whitespace/pre-wrap-line-test-simple-lines.html
     13               fast/text/whitespace/pre-wrap-long-word-simple-lines.html
     14               fast/text/whitespace/pre-wrap-spaces-after-newline-simple-lines.html
     15
     16        * rendering/SimpleLineLayout.cpp:
     17        (WebCore::SimpleLineLayout::canUseFor):
     18        (WebCore::SimpleLineLayout::isWhitespace):
     19        (WebCore::SimpleLineLayout::skipWhitespaces):
     20        (WebCore::SimpleLineLayout::textWidth):
     21        (WebCore::SimpleLineLayout::measureWord):
     22        (WebCore::SimpleLineLayout::createTextRuns):
     23        * rendering/SimpleLineLayoutFunctions.cpp:
     24        (WebCore::SimpleLineLayout::paintDebugBorders):
     25        (WebCore::SimpleLineLayout::paintFlow):
     26
    1272013-11-10  Sergio Correia  <sergio.correia@openbossa.org>
    228
  • trunk/Source/WebCore/rendering/SimpleLineLayout.cpp

    r158918 r159030  
    131131    if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
    132132        return false;
    133     // Pre/no-wrap would be very helpful to support.
    134     if (style.whiteSpace() != NORMAL)
    135         return false;
    136133    if (!style.textIndent().isZero())
    137134        return false;
     
    198195}
    199196
    200 static inline bool isWhitespace(UChar character)
    201 {
    202     return character == ' ' || character == '\t' || character == '\n';
     197static inline bool isWhitespace(UChar character, bool preserveNewline)
     198{
     199    return character == ' ' || character == '\t' || (!preserveNewline && character == '\n');
    203200}
    204201
    205202template <typename CharacterType>
    206 static inline unsigned skipWhitespaces(const CharacterType* text, unsigned offset, unsigned length)
     203static inline unsigned skipWhitespaces(const CharacterType* text, unsigned offset, unsigned length, bool preserveNewline)
    207204{
    208205    for (; offset < length; ++offset) {
    209         if (!isWhitespace(text[offset]))
     206        if (!isWhitespace(text[offset], preserveNewline))
    210207            return offset;
    211208    }
     
    214211
    215212template <typename CharacterType>
    216 static float textWidth(const RenderText& renderText, const CharacterType* text, unsigned textLength, unsigned from, unsigned to, float xPosition, const RenderStyle& style)
    217 {
    218     if (style.font().isFixedPitch() || (!from && to == textLength))
    219         return renderText.width(from, to - from, style.font(), xPosition, nullptr, nullptr);
    220     // FIXME: Add templated UChar/LChar paths.
     213static float textWidth(const RenderText& renderText, const CharacterType* text, unsigned textLength, unsigned from, unsigned to, float xPosition, const Font& font, float tabWidth)
     214{
     215    if (font.isFixedPitch() || (!from && to == textLength))
     216        return renderText.width(from, to - from, font, xPosition, nullptr, nullptr);
     217
    221218    TextRun run(text + from, to - from);
    222219    run.setXPos(xPosition);
    223220    run.setCharactersLength(textLength - from);
     221    run.setTabSize(!!tabWidth, tabWidth);
     222
    224223    ASSERT(run.charactersLength() >= run.length());
    225224
    226     return style.font().width(run);
     225    return font.width(run);
     226}
     227
     228template <typename CharacterType>
     229static float measureWord(const RenderText& textRenderer, const CharacterType* text, unsigned textLength, unsigned start, unsigned end, float lineWidth, bool collapseWhitespace, const Font& font, float tabWidth, float spaceWidth)
     230{
     231    if (text[start] == ' ' && end == start + 1)
     232        return spaceWidth;
     233
     234    bool measureWithEndSpace = collapseWhitespace && end < textLength && text[end] == ' ';
     235    if (measureWithEndSpace)
     236        ++end;
     237    float width = textWidth(textRenderer, text, textLength, start, end, lineWidth, font, collapseWhitespace ? 0 : tabWidth);
     238
     239    return measureWithEndSpace ? width - spaceWidth : width;
    227240}
    228241
     
    262275    const RenderStyle& style = flow.style();
    263276
    264     ETextAlign textAlign = style.textAlign();
    265     float wordTrailingSpaceWidth = style.font().width(TextRun(&space, 1));
     277    // These properties are supported.
     278    const Font& font = style.font();
     279    unsigned tabWidth = style.tabSize();
     280    ETextAlign textAlign = style.textAlign(); // Not 'justify'.
     281    bool collapseWhitespace = style.collapseWhiteSpace();
     282    bool preserveNewline = style.preserveNewline();
     283    bool wrapLines = style.autoWrap();
    266284
    267285    const CharacterType* text = textRenderer.text()->getCharacters<CharacterType>();
    268286    const unsigned textLength = textRenderer.textLength();
    269287
     288    float spaceWidth = font.width(TextRun(&space, 1));
    270289    LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
    271290
    272291    unsigned lineEnd = 0;
    273292    while (lineEnd < textLength) {
    274         lineEnd = skipWhitespaces(text, lineEnd, textLength);
     293        if (collapseWhitespace)
     294            lineEnd = skipWhitespaces(text, lineEnd, textLength, preserveNewline);
    275295        unsigned lineStart = lineEnd;
    276296        unsigned wordEnd = lineEnd;
     
    281301
    282302        while (wordEnd < textLength) {
    283             ASSERT(!isWhitespace(text[wordEnd]));
    284 
    285             bool wordIsPrecededByWhitespace = wordEnd > lineStart && isWhitespace(text[wordEnd - 1]);
    286             unsigned wordStart = wordIsPrecededByWhitespace ? wordEnd - 1 : wordEnd;
    287 
    288             wordEnd = nextBreakablePosition<CharacterType, false>(lineBreakIterator, text, textLength, wordEnd + 1);
    289 
    290             bool measureWithEndSpace = wordEnd < textLength && text[wordEnd] == ' ';
    291             unsigned wordMeasureEnd = measureWithEndSpace ? wordEnd + 1 : wordEnd;
    292 
    293             float wordWidth = textWidth(textRenderer, text, textLength, wordStart, wordMeasureEnd, lineWidth.committedWidth(), style);
    294 
    295             if (measureWithEndSpace)
    296                 wordWidth -= wordTrailingSpaceWidth;
     303            ASSERT(!collapseWhitespace || !isWhitespace(text[wordEnd], preserveNewline));
     304
     305            unsigned wordStart = wordEnd;
     306
     307            if (preserveNewline && text[wordStart] == '\n') {
     308                ++wordEnd;
     309                // FIXME: This creates a dedicated run for newline. This is wasteful and unnecessary but it keeps test results unchanged.
     310                if (wordStart > lineStart)
     311                    lineRuns.append(Run(lineEnd, lineRuns.last().right));
     312                lineRuns.last().right = lineRuns.last().left;
     313                lineRuns.last().textLength = 1;
     314                lineEnd = wordEnd;
     315                break;
     316            }
     317
     318            if (!collapseWhitespace && isWhitespace(text[wordStart], preserveNewline))
     319                wordEnd = wordStart + 1;
     320            else
     321                wordEnd = nextBreakablePosition<CharacterType, false>(lineBreakIterator, text, textLength, wordStart + 1);
     322
     323            bool wordIsPrecededByWhitespace = collapseWhitespace && wordStart > lineStart && isWhitespace(text[wordStart - 1], preserveNewline);
     324            if (wordIsPrecededByWhitespace)
     325                --wordStart;
     326
     327            float wordWidth = measureWord(textRenderer, text, textLength, wordStart, wordEnd, lineWidth.committedWidth(), collapseWhitespace, font, tabWidth, spaceWidth);
    297328
    298329            lineWidth.addUncommittedWidth(wordWidth);
    299330
    300             // Move to the next line if the current one is full and we have something on it.
    301             if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
    302                 break;
     331            if (wrapLines) {
     332                // Move to the next line if the current one is full and we have something on it.
     333                if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
     334                    break;
     335
     336                // This is for white-space: pre-wrap which requires special handling for end line whitespace.
     337                if (!collapseWhitespace && lineWidth.fitsOnLine() && wordEnd < textLength && isWhitespace(text[wordEnd], preserveNewline)) {
     338                    // Look ahead to see if the next whitespace would fit.
     339                    float whitespaceWidth = textWidth(textRenderer, text, textLength, wordEnd, wordEnd + 1, lineWidth.committedWidth(), font, tabWidth);
     340                    if (!lineWidth.fitsOnLineIncludingExtraWidth(whitespaceWidth)) {
     341                        // If not eat away the rest of the whitespace on the line.
     342                        unsigned whitespaceEnd = skipWhitespaces(text, wordEnd, textLength, preserveNewline);
     343                        // Include newline to this run too.
     344                        if (whitespaceEnd < textLength && text[whitespaceEnd] == '\n')
     345                            ++whitespaceEnd;
     346                        lineRuns.last().textLength = whitespaceEnd - lineRuns.last().textOffset;
     347                        lineRuns.last().right = lineWidth.availableWidth();
     348                        lineEnd = whitespaceEnd;
     349                        break;
     350                    }
     351                }
     352            }
    303353
    304354            if (wordStart > lineEnd) {
     
    307357                // Include space to the end of the previous run.
    308358                lineRuns.last().textLength++;
    309                 lineRuns.last().right += wordTrailingSpaceWidth;
     359                lineRuns.last().right += spaceWidth;
    310360                // Start a new run on the same line.
    311361                lineRuns.append(Run(wordStart + 1, lineRuns.last().right));
     
    318368
    319369            lineEnd = wordEnd;
    320             wordEnd = skipWhitespaces(text, wordEnd, textLength);
    321 
    322             if (!lineWidth.fitsOnLine()) {
     370            if (collapseWhitespace)
     371                wordEnd = skipWhitespaces(text, wordEnd, textLength, preserveNewline);
     372
     373            if (wrapLines && !lineWidth.fitsOnLine()) {
    323374                // The first run on the line overflows.
    324375                ASSERT(lineRuns.size() == 1);
     
    329380            continue;
    330381
     382        lineRuns.last().isEndOfLine = true;
     383
    331384        adjustRunOffsets(lineRuns, textAlign, lineWidth.committedWidth(), lineWidth.availableWidth());
    332385
     
    334387            runs.append(lineRuns[i]);
    335388
    336         runs.last().isEndOfLine = true;
    337389        ++lineCount;
    338390    }
  • trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp

    r158285 r159030  
    5151static void paintDebugBorders(GraphicsContext& context, const LayoutRect& borderRect, const LayoutPoint& paintOffset)
    5252{
     53    if (borderRect.isEmpty())
     54        return;
    5355    GraphicsContextStateSaver stateSaver(context);
    5456    context.setStrokeColor(Color(0, 255, 0), ColorSpaceDeviceRGB);
     
    8183    for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
    8284        auto run = *it;
    83         context.drawText(font, TextRun(run.text()), run.baseline() + paintOffset);
     85        TextRun textRun(run.text());
     86        textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
     87        context.drawText(font, textRun, run.baseline() + paintOffset);
    8488        if (debugBordersEnabled)
    8589            paintDebugBorders(context, run.rect(), paintOffset);
Note: See TracChangeset for help on using the changeset viewer.