Templated LChar/UChar paths for simple line layout
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Nov 2013 12:53:19 +0000 (12:53 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Nov 2013 12:53:19 +0000 (12:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=124035

Reviewed by Andreas Kling.

* rendering/SimpleLineLayout.cpp:
(WebCore::SimpleLineLayout::canUseForText):
(WebCore::SimpleLineLayout::canUseFor):

    Use a templated function to check for illegal characters.

(WebCore::SimpleLineLayout::skipWhitespaces):

    Make a template function.

(WebCore::SimpleLineLayout::textWidth):

    Make a template function plus some argument changes.

(WebCore::SimpleLineLayout::createTextRuns):

    Template function for creating runs while operating with either LChars or UChar.
    Also simplified line breaking and text width measuring logic.

(WebCore::SimpleLineLayout::create):

    Pick the template.

* rendering/break_lines.cpp:
* rendering/break_lines.h:

    Move the implementation to the header (except for the table) so we can use the template
    versions directly.

(WebCore::isBreakableSpace):
(WebCore::shouldBreakAfter):
(WebCore::needsLineBreakIterator):
(WebCore::nextBreakablePosition):
(WebCore::nextBreakablePositionIgnoringNBSP):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@158918 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/rendering/SimpleLineLayout.cpp
Source/WebCore/rendering/break_lines.cpp
Source/WebCore/rendering/break_lines.h

index 99c1945..142753e 100644 (file)
@@ -1,3 +1,45 @@
+2013-11-08  Antti Koivisto  <antti@apple.com>
+
+        Templated LChar/UChar paths for simple line layout
+        https://bugs.webkit.org/show_bug.cgi?id=124035
+
+        Reviewed by Andreas Kling.
+
+        * rendering/SimpleLineLayout.cpp:
+        (WebCore::SimpleLineLayout::canUseForText):
+        (WebCore::SimpleLineLayout::canUseFor):
+        
+            Use a templated function to check for illegal characters.
+
+        (WebCore::SimpleLineLayout::skipWhitespaces):
+        
+            Make a template function.
+
+        (WebCore::SimpleLineLayout::textWidth):
+        
+            Make a template function plus some argument changes.
+
+        (WebCore::SimpleLineLayout::createTextRuns):
+        
+            Template function for creating runs while operating with either LChars or UChar.
+            Also simplified line breaking and text width measuring logic.
+
+        (WebCore::SimpleLineLayout::create):
+        
+            Pick the template.
+
+        * rendering/break_lines.cpp:
+        * rendering/break_lines.h:
+        
+            Move the implementation to the header (except for the table) so we can use the template
+            versions directly.
+
+        (WebCore::isBreakableSpace):
+        (WebCore::shouldBreakAfter):
+        (WebCore::needsLineBreakIterator):
+        (WebCore::nextBreakablePosition):
+        (WebCore::nextBreakablePositionIgnoringNBSP):
+
 2013-11-08  Mario Sanchez Prada  <mario.prada@samsung.com>
 
         AX: [ATK] <span> elements exposed through ATK when not needed
index c854e43..1786465 100644 (file)
 namespace WebCore {
 namespace SimpleLineLayout {
 
-static inline bool isWhitespace(UChar character)
+template <typename CharacterType>
+static bool canUseForText(const CharacterType* text, unsigned length, const SimpleFontData& fontData)
 {
-    return character == ' ' || character == '\t' || character == '\n';
+    for (unsigned i = 0; i < length; ++i) {
+        UChar character = text[i];
+        if (character == ' ')
+            continue;
+
+        // These would be easy to support.
+        if (character == noBreakSpace)
+            return false;
+        if (character == softHyphen)
+            return false;
+
+        UCharDirection direction = u_charDirection(character);
+        if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
+            || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
+            || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE
+            || direction == U_POP_DIRECTIONAL_FORMAT || direction == U_BOUNDARY_NEUTRAL)
+            return false;
+
+        if (!fontData.glyphForCharacter(character))
+            return false;
+    }
+    return true;
+}
+
+static bool canUseForText(const RenderText& textRenderer, const SimpleFontData& fontData)
+{
+    if (textRenderer.is8Bit())
+        return canUseForText(textRenderer.characters8(), textRenderer.textLength(), fontData);
+    return canUseForText(textRenderer.characters16(), textRenderer.textLength(), fontData);
 }
 
 bool canUseFor(const RenderBlockFlow& flow)
@@ -162,51 +191,38 @@ bool canUseFor(const RenderBlockFlow& flow)
     auto& primaryFontData = *style.font().primaryFont();
     if (primaryFontData.isLoading())
         return false;
+    if (!canUseForText(textRenderer, primaryFontData))
+        return false;
 
-    unsigned length = textRenderer.textLength();
-    for (unsigned i = 0; i < length; ++i) {
-        UChar character = textRenderer.characterAt(i);
-        if (character == ' ')
-            continue;
-
-        // These would be easy to support.
-        if (character == noBreakSpace)
-            return false;
-        if (character == softHyphen)
-            return false;
-
-        UCharDirection direction = u_charDirection(character);
-        if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
-            || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
-            || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE
-            || direction == U_POP_DIRECTIONAL_FORMAT || direction == U_BOUNDARY_NEUTRAL)
-            return false;
-
-        if (!primaryFontData.glyphForCharacter(character))
-            return false;
-    }
     return true;
 }
 
-static inline unsigned skipWhitespaces(const RenderText& textRenderer, unsigned offset, unsigned length)
+static inline bool isWhitespace(UChar character)
+{
+    return character == ' ' || character == '\t' || character == '\n';
+}
+
+template <typename CharacterType>
+static inline unsigned skipWhitespaces(const CharacterType* text, unsigned offset, unsigned length)
 {
     for (; offset < length; ++offset) {
-        if (!isWhitespace(textRenderer.characterAt(offset)))
-            break;
+        if (!isWhitespace(text[offset]))
+            return offset;
     }
-    return offset;
+    return length;
 }
 
-static float textWidth(const RenderText& text, unsigned from, unsigned length, float xPosition, const RenderStyle& style)
+template <typename CharacterType>
+static float textWidth(const RenderText& renderText, const CharacterType* text, unsigned textLength, unsigned from, unsigned to, float xPosition, const RenderStyle& style)
 {
-    if (style.font().isFixedPitch() || (!from && length == text.textLength()))
-        return text.width(from, length, style.font(), xPosition, nullptr, nullptr);
+    if (style.font().isFixedPitch() || (!from && to == textLength))
+        return renderText.width(from, to - from, style.font(), xPosition, nullptr, nullptr);
     // FIXME: Add templated UChar/LChar paths.
-    TextRun run = text.is8Bit() ? TextRun(text.characters8() + from, length) : TextRun(text.characters16() + from, length);
-    run.setCharactersLength(text.textLength() - from);
+    TextRun run(text + from, to - from);
+    run.setXPos(xPosition);
+    run.setCharactersLength(textLength - from);
     ASSERT(run.charactersLength() >= run.length());
 
-    run.setXPos(xPosition);
     return style.font().width(run);
 }
 
@@ -240,53 +256,44 @@ static void adjustRunOffsets(Vector<Run, 4>& lineRuns, ETextAlign textAlign, flo
     }
 }
 
-std::unique_ptr<Layout> create(RenderBlockFlow& flow)
+template <typename CharacterType>
+void createTextRuns(Layout::RunVector& runs, unsigned& lineCount, RenderBlockFlow& flow, RenderText& textRenderer)
 {
-    RenderText& textRenderer = toRenderText(*flow.firstChild());
-    ASSERT(!textRenderer.firstTextBox());
-
     const RenderStyle& style = flow.style();
-    const unsigned textLength = textRenderer.textLength();
 
     ETextAlign textAlign = style.textAlign();
     float wordTrailingSpaceWidth = style.font().width(TextRun(&space, 1));
 
-    LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
-    int nextBreakable = -1;
+    const CharacterType* text = textRenderer.text()->getCharacters<CharacterType>();
+    const unsigned textLength = textRenderer.textLength();
 
-    Layout::RunVector runs;
-    unsigned lineCount = 0;
+    LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
 
-    unsigned lineEndOffset = 0;
-    while (lineEndOffset < textLength) {
-        lineEndOffset = skipWhitespaces(textRenderer, lineEndOffset, textLength);
-        unsigned lineStartOffset = lineEndOffset;
-        unsigned wordEndOffset = lineEndOffset;
+    unsigned lineEnd = 0;
+    while (lineEnd < textLength) {
+        lineEnd = skipWhitespaces(text, lineEnd, textLength);
+        unsigned lineStart = lineEnd;
+        unsigned wordEnd = lineEnd;
         LineWidth lineWidth(flow, false, DoNotIndentText);
 
         Vector<Run, 4> lineRuns;
-        lineRuns.uncheckedAppend(Run(lineStartOffset, 0));
+        lineRuns.uncheckedAppend(Run(lineStart, 0));
 
-        while (wordEndOffset < textLength) {
-            ASSERT(!isWhitespace(textRenderer.characterAt(wordEndOffset)));
+        while (wordEnd < textLength) {
+            ASSERT(!isWhitespace(text[wordEnd]));
 
-            bool previousWasSpaceBetweenWords = wordEndOffset > lineStartOffset && isWhitespace(textRenderer.characterAt(wordEndOffset - 1));
-            unsigned wordStartOffset = previousWasSpaceBetweenWords ? wordEndOffset - 1 : wordEndOffset;
+            bool wordIsPrecededByWhitespace = wordEnd > lineStart && isWhitespace(text[wordEnd - 1]);
+            unsigned wordStart = wordIsPrecededByWhitespace ? wordEnd - 1 : wordEnd;
 
-            ++wordEndOffset;
-            while (wordEndOffset < textLength) {
-                if (wordEndOffset > lineStartOffset && isBreakable(lineBreakIterator, wordEndOffset, nextBreakable, false))
-                    break;
-                ++wordEndOffset;
-            }
+            wordEnd = nextBreakablePosition<CharacterType, false>(lineBreakIterator, text, textLength, wordEnd + 1);
+
+            bool measureWithEndSpace = wordEnd < textLength && text[wordEnd] == ' ';
+            unsigned wordMeasureEnd = measureWithEndSpace ? wordEnd + 1 : wordEnd;
 
-            unsigned wordLength = wordEndOffset - wordStartOffset;
-            bool includeEndSpace = wordEndOffset < textLength && textRenderer.characterAt(wordEndOffset) == ' ';
-            float wordWidth;
-            if (includeEndSpace)
-                wordWidth = textWidth(textRenderer, wordStartOffset, wordLength + 1, lineWidth.committedWidth(), style) - wordTrailingSpaceWidth;
-            else
-                wordWidth = textWidth(textRenderer, wordStartOffset, wordLength, lineWidth.committedWidth(), style);
+            float wordWidth = textWidth(textRenderer, text, textLength, wordStart, wordMeasureEnd, lineWidth.committedWidth(), style);
+
+            if (measureWithEndSpace)
+                wordWidth -= wordTrailingSpaceWidth;
 
             lineWidth.addUncommittedWidth(wordWidth);
 
@@ -294,23 +301,23 @@ std::unique_ptr<Layout> create(RenderBlockFlow& flow)
             if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
                 break;
 
-            if (wordStartOffset > lineEndOffset) {
+            if (wordStart > lineEnd) {
                 // There were more than one consecutive whitespace.
-                ASSERT(previousWasSpaceBetweenWords);
+                ASSERT(wordIsPrecededByWhitespace);
                 // Include space to the end of the previous run.
                 lineRuns.last().textLength++;
                 lineRuns.last().right += wordTrailingSpaceWidth;
                 // Start a new run on the same line.
-                lineRuns.append(Run(wordStartOffset + 1, lineRuns.last().right));
+                lineRuns.append(Run(wordStart + 1, lineRuns.last().right));
             }
 
             lineWidth.commit();
 
             lineRuns.last().right = lineWidth.committedWidth();
-            lineRuns.last().textLength = wordEndOffset - lineRuns.last().textOffset;
+            lineRuns.last().textLength = wordEnd - lineRuns.last().textOffset;
 
-            lineEndOffset = wordEndOffset;
-            wordEndOffset = skipWhitespaces(textRenderer, wordEndOffset, textLength);
+            lineEnd = wordEnd;
+            wordEnd = skipWhitespaces(text, wordEnd, textLength);
 
             if (!lineWidth.fitsOnLine()) {
                 // The first run on the line overflows.
@@ -318,7 +325,7 @@ std::unique_ptr<Layout> create(RenderBlockFlow& flow)
                 break;
             }
         }
-        if (lineStartOffset == lineEndOffset)
+        if (lineStart == lineEnd)
             continue;
 
         adjustRunOffsets(lineRuns, textAlign, lineWidth.committedWidth(), lineWidth.availableWidth());
@@ -329,6 +336,20 @@ std::unique_ptr<Layout> create(RenderBlockFlow& flow)
         runs.last().isEndOfLine = true;
         ++lineCount;
     }
+}
+
+std::unique_ptr<Layout> create(RenderBlockFlow& flow)
+{
+    Layout::RunVector runs;
+    unsigned lineCount = 0;
+
+    RenderText& textRenderer = toRenderText(*flow.firstChild());
+    ASSERT(!textRenderer.firstTextBox());
+
+    if (textRenderer.is8Bit())
+        createTextRuns<LChar>(runs, lineCount, flow, textRenderer);
+    else
+        createTextRuns<UChar>(runs, lineCount, flow, textRenderer);
 
     textRenderer.clearNeedsLayout();
 
index 04b040d..593feef 100644 (file)
 
 namespace WebCore {
 
-template<bool treatNoBreakSpaceAsBreak>
-static inline bool isBreakableSpace(UChar ch)
-{
-    switch (ch) {
-    case ' ':
-    case '\n':
-    case '\t':
-        return true;
-    case noBreakSpace:
-        return treatNoBreakSpaceAsBreak;
-    default:
-        return false;
-    }
-}
-
-static const UChar asciiLineBreakTableFirstChar = '!';
-static const UChar asciiLineBreakTableLastChar = 127;
-
 // Pack 8 bits into one byte
 #define B(a, b, c, d, e, f, g, h) \
     ((a) | ((b) << 1) | ((c) << 2) | ((d) << 3) | ((e) << 4) | ((f) << 5) | ((g) << 6) | ((h) << 7))
@@ -73,7 +55,7 @@ static const UChar asciiLineBreakTableLastChar = 127;
 // - after '-' and '?' (backward-compatible, and compatible with Internet Explorer).
 // Please refer to <https://bugs.webkit.org/show_bug.cgi?id=37698> for line breaking matrixes of different browsers
 // and the ICU standard.
-static const unsigned char asciiLineBreakTable[][(asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar) / 8 + 1] = {
+const unsigned char asciiLineBreakTable[][asciiLineBreakTableColumnCount] = {
     //  !  "  #  $  %  &  '  (     )  *  +  ,  -  .  /  0  1-8   9  :  ;  <  =  >  ?  @     A-X      Y  Z  [  \  ]  ^  _  `     a-x      y  z  {  |  }  ~  DEL
     { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // !
     { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // "
@@ -120,85 +102,4 @@ static const unsigned char asciiLineBreakTable[][(asciiLineBreakTableLastChar -
 
 COMPILE_ASSERT(WTF_ARRAY_LENGTH(asciiLineBreakTable) == asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar + 1, TestLineBreakTableConsistency);
 
-static inline bool shouldBreakAfter(UChar lastCh, UChar ch, UChar nextCh)
-{
-    // Don't allow line breaking between '-' and a digit if the '-' may mean a minus sign in the context,
-    // while allow breaking in 'ABCD-1234' and '1234-5678' which may be in long URLs.
-    if (ch == '-' && isASCIIDigit(nextCh))
-        return isASCIIAlphanumeric(lastCh);
-
-    // If both ch and nextCh are ASCII characters, use a lookup table for enhanced speed and for compatibility
-    // with other browsers (see comments for asciiLineBreakTable for details).
-    if (ch >= asciiLineBreakTableFirstChar && ch <= asciiLineBreakTableLastChar
-            && nextCh >= asciiLineBreakTableFirstChar && nextCh <= asciiLineBreakTableLastChar) {
-        const unsigned char* tableRow = asciiLineBreakTable[ch - asciiLineBreakTableFirstChar];
-        int nextChIndex = nextCh - asciiLineBreakTableFirstChar;
-        return tableRow[nextChIndex / 8] & (1 << (nextChIndex % 8));
-    }
-    // Otherwise defer to the Unicode algorithm by returning false.
-    return false;
-}
-
-template<bool treatNoBreakSpaceAsBreak>
-inline bool needsLineBreakIterator(UChar ch)
-{
-    if (treatNoBreakSpaceAsBreak)
-        return ch > asciiLineBreakTableLastChar;
-    return ch > asciiLineBreakTableLastChar && ch != noBreakSpace;
-}
-
-template<typename CharacterType, bool treatNoBreakSpaceAsBreak>
-static inline int nextBreakablePosition(LazyLineBreakIterator& lazyBreakIterator, const CharacterType* str, unsigned length, int pos)
-{
-    int len = static_cast<int>(length);
-    int nextBreak = -1;
-
-    CharacterType lastLastCh = pos > 1 ? str[pos - 2] : static_cast<CharacterType>(lazyBreakIterator.secondToLastCharacter());
-    CharacterType lastCh = pos > 0 ? str[pos - 1] : static_cast<CharacterType>(lazyBreakIterator.lastCharacter());
-    unsigned priorContextLength = lazyBreakIterator.priorContextLength();
-    for (int i = pos; i < len; i++) {
-        CharacterType ch = str[i];
-
-        if (isBreakableSpace<treatNoBreakSpaceAsBreak>(ch) || shouldBreakAfter(lastLastCh, lastCh, ch))
-            return i;
-
-        if (needsLineBreakIterator<treatNoBreakSpaceAsBreak>(ch) || needsLineBreakIterator<treatNoBreakSpaceAsBreak>(lastCh)) {
-            if (nextBreak < i) {
-                // Don't break if positioned at start of primary context and there is no prior context.
-                if (i || priorContextLength) {
-                    TextBreakIterator* breakIterator = lazyBreakIterator.get(priorContextLength);
-                    if (breakIterator) {
-                        nextBreak = textBreakFollowing(breakIterator, i - 1 + priorContextLength);
-                        if (nextBreak >= 0)
-                            nextBreak -= priorContextLength;
-                    }
-                }
-            }
-            if (i == nextBreak && !isBreakableSpace<treatNoBreakSpaceAsBreak>(lastCh))
-                return i;
-        }
-
-        lastLastCh = lastCh;
-        lastCh = ch;
-    }
-
-    return len;
-}
-
-int nextBreakablePositionIgnoringNBSP(LazyLineBreakIterator& lazyBreakIterator, int pos)
-{
-    String string = lazyBreakIterator.string();
-    if (string.is8Bit())
-        return nextBreakablePosition<LChar, false>(lazyBreakIterator, string.characters8(), string.length(), pos);
-    return nextBreakablePosition<UChar, false>(lazyBreakIterator, string.characters16(), string.length(), pos);
-}
-
-int nextBreakablePosition(LazyLineBreakIterator& lazyBreakIterator, int pos)
-{
-    String string = lazyBreakIterator.string();
-    if (string.is8Bit())
-        return nextBreakablePosition<LChar, true>(lazyBreakIterator, string.characters8(), string.length(), pos);
-    return nextBreakablePosition<UChar, true>(lazyBreakIterator, string.characters16(), string.length(), pos);
-}
-
 } // namespace WebCore
index 6923630..25f3afa 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2005 Apple Computer, Inc.
+ * Copyright (C) 2005, 2007, 2010, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #ifndef break_lines_h
 #define break_lines_h
 
+#include "TextBreakIterator.h"
+#include <wtf/ASCIICType.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/unicode/CharacterNames.h>
 #include <wtf/unicode/Unicode.h>
 
 namespace WebCore {
 
-class LazyLineBreakIterator;
+static const UChar asciiLineBreakTableFirstChar = '!';
+static const UChar asciiLineBreakTableLastChar = 127;
+static const unsigned asciiLineBreakTableColumnCount = (asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar) / 8 + 1;
+
+extern const unsigned char asciiLineBreakTable[][asciiLineBreakTableColumnCount];
 
 int nextBreakablePositionIgnoringNBSP(LazyLineBreakIterator&, int pos);
 int nextBreakablePosition(LazyLineBreakIterator&, int pos);
 
+template<bool treatNoBreakSpaceAsBreak>
+static inline bool isBreakableSpace(UChar ch)
+{
+    switch (ch) {
+    case ' ':
+    case '\n':
+    case '\t':
+        return true;
+    case noBreakSpace:
+        return treatNoBreakSpaceAsBreak;
+    default:
+        return false;
+    }
+}
+
+inline bool shouldBreakAfter(UChar lastCh, UChar ch, UChar nextCh)
+{
+    // Don't allow line breaking between '-' and a digit if the '-' may mean a minus sign in the context,
+    // while allow breaking in 'ABCD-1234' and '1234-5678' which may be in long URLs.
+    if (ch == '-' && isASCIIDigit(nextCh))
+        return isASCIIAlphanumeric(lastCh);
+
+    // If both ch and nextCh are ASCII characters, use a lookup table for enhanced speed and for compatibility
+    // with other browsers (see comments for asciiLineBreakTable for details).
+    if (ch >= asciiLineBreakTableFirstChar && ch <= asciiLineBreakTableLastChar && nextCh >= asciiLineBreakTableFirstChar && nextCh <= asciiLineBreakTableLastChar) {
+        const unsigned char* tableRow = asciiLineBreakTable[ch - asciiLineBreakTableFirstChar];
+        int nextChIndex = nextCh - asciiLineBreakTableFirstChar;
+        return tableRow[nextChIndex / 8] & (1 << (nextChIndex % 8));
+    }
+    // Otherwise defer to the Unicode algorithm by returning false.
+    return false;
+}
+
+template<bool treatNoBreakSpaceAsBreak>
+inline bool needsLineBreakIterator(UChar ch)
+{
+    if (treatNoBreakSpaceAsBreak)
+        return ch > asciiLineBreakTableLastChar;
+    return ch > asciiLineBreakTableLastChar && ch != noBreakSpace;
+}
+
+template<typename CharacterType, bool treatNoBreakSpaceAsBreak>
+inline int nextBreakablePosition(LazyLineBreakIterator& lazyBreakIterator, const CharacterType* str, unsigned length, int pos)
+{
+    int len = static_cast<int>(length);
+    int nextBreak = -1;
+
+    CharacterType lastLastCh = pos > 1 ? str[pos - 2] : static_cast<CharacterType>(lazyBreakIterator.secondToLastCharacter());
+    CharacterType lastCh = pos > 0 ? str[pos - 1] : static_cast<CharacterType>(lazyBreakIterator.lastCharacter());
+    unsigned priorContextLength = lazyBreakIterator.priorContextLength();
+    for (int i = pos; i < len; i++) {
+        CharacterType ch = str[i];
+
+        if (isBreakableSpace<treatNoBreakSpaceAsBreak>(ch) || shouldBreakAfter(lastLastCh, lastCh, ch))
+            return i;
+
+        if (needsLineBreakIterator<treatNoBreakSpaceAsBreak>(ch) || needsLineBreakIterator<treatNoBreakSpaceAsBreak>(lastCh)) {
+            if (nextBreak < i) {
+                // Don't break if positioned at start of primary context and there is no prior context.
+                if (i || priorContextLength) {
+                    TextBreakIterator* breakIterator = lazyBreakIterator.get(priorContextLength);
+                    if (breakIterator) {
+                        nextBreak = textBreakFollowing(breakIterator, i - 1 + priorContextLength);
+                        if (nextBreak >= 0)
+                            nextBreak -= priorContextLength;
+                    }
+                }
+            }
+            if (i == nextBreak && !isBreakableSpace<treatNoBreakSpaceAsBreak>(lastCh))
+                return i;
+        }
+
+        lastLastCh = lastCh;
+        lastCh = ch;
+    }
+
+    return len;
+}
+
+inline int nextBreakablePositionIgnoringNBSP(LazyLineBreakIterator& lazyBreakIterator, int pos)
+{
+    String string = lazyBreakIterator.string();
+    if (string.is8Bit())
+        return nextBreakablePosition<LChar, false>(lazyBreakIterator, string.characters8(), string.length(), pos);
+    return nextBreakablePosition<UChar, false>(lazyBreakIterator, string.characters16(), string.length(), pos);
+}
+
+inline int nextBreakablePosition(LazyLineBreakIterator& lazyBreakIterator, int pos)
+{
+    String string = lazyBreakIterator.string();
+    if (string.is8Bit())
+        return nextBreakablePosition<LChar, true>(lazyBreakIterator, string.characters8(), string.length(), pos);
+    return nextBreakablePosition<UChar, true>(lazyBreakIterator, string.characters16(), string.length(), pos);
+}
+
 inline bool isBreakable(LazyLineBreakIterator& lazyBreakIterator, int pos, int& nextBreakable, bool breakNBSP)
 {
     if (pos > nextBreakable) {