Simple line layout: Introduce text fragment continuation.
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Nov 2014 18:11:07 +0000 (18:11 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Nov 2014 18:11:07 +0000 (18:11 +0000)
https://bugs.webkit.org/show_bug.cgi?id=138274

Source/WebCore:

This patch extends simple line layout coverage to multiple text renderers.
When a particular render flow has multiple text renderers (but not any other type)
then we use simple line layout to process and paint the content. -other, existing requirements still apply
so that for example if the content requires decoration, we bail out of simple line layout.

FlowContent now supports multiple renderes. It continuously reads content from sibling renderers
so that the simple line layout parser sees it as one monolithic block of content. Run positions
are all relative to the block and they get resolved to renderer's positions on demand.
(painting, computing bounding rects etc)

Reviewed by Antti Koivisto.

Performance test already been added for the multiple rendere use case,
correctness is covered by existing test cases.
Test: fast/text/simple-lines-mutliple-renderers.html

* rendering/SimpleLineLayout.cpp:
(WebCore::SimpleLineLayout::canUseFor): Check if children are all 8bit RenderTexts.
(WebCore::SimpleLineLayout::removeTrailingWhitespace): Move the endofline check right before where we might overflow using end position.
(WebCore::SimpleLineLayout::initializeNewLine):
(WebCore::SimpleLineLayout::closeLineEndingAndAdjustRuns):
(WebCore::SimpleLineLayout::splitRunsAtRendererBoundary): Split runs at renderers' boundary to be in sync with inline text renderering.
(WebCore::SimpleLineLayout::createTextRuns):
(WebCore::SimpleLineLayout::create):
* rendering/SimpleLineLayoutFlowContents.cpp:
(WebCore::SimpleLineLayout::FlowContents::FlowContents):
(WebCore::SimpleLineLayout::FlowContents::findNextBreakablePosition):
(WebCore::SimpleLineLayout::FlowContents::findNextNonWhitespacePosition):
(WebCore::SimpleLineLayout::FlowContents::textWidth): Do not measure text across renderers. It could produce different width value due to
ligature which later can produce unexpected line breaks and out sync rendering in general.
(WebCore::SimpleLineLayout::FlowContents::renderer):
(WebCore::SimpleLineLayout::FlowContents::resolveRendererPositions):
(WebCore::SimpleLineLayout::FlowContents::appendNextRendererContentIfNeeded): Read the next renderer content if needed.
(WebCore::SimpleLineLayout::FlowContents::nextNonWhitespacePosition):
(WebCore::SimpleLineLayout::FlowContents::runWidth):
* rendering/SimpleLineLayoutFlowContents.h:
(WebCore::SimpleLineLayout::FlowContents::isNewlineCharacter):
(WebCore::SimpleLineLayout::FlowContents::isEndOfContent):
* rendering/SimpleLineLayoutResolver.cpp:
(WebCore::SimpleLineLayout::RunResolver::Run::text):

LayoutTests:

Rebaseline for simple line layout's multiple rendere support.

Reviewed by Antti Koivisto.

* fast/text/simple-lines-multiple-renderers-expected.html: Added.
* fast/text/simple-lines-multiple-renderers.html: Added.
* fast/tokenizer/script_extra_close-expected.txt: Multiple tab characters should collapse into a single whitespace. This
needs fixing in complex line layout.
* tables/mozilla/bugs/bug157890-expected.txt: no-op endofline run is added by complex inline layout
when multiple text content is injected through JS into a <pre>. This requires fixing complex line layout.

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

LayoutTests/ChangeLog
LayoutTests/fast/text/simple-lines-multiple-renderers-expected.html [new file with mode: 0644]
LayoutTests/fast/text/simple-lines-multiple-renderers.html [new file with mode: 0644]
LayoutTests/fast/tokenizer/script_extra_close-expected.txt
LayoutTests/tables/mozilla/bugs/bug157890-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderBlockFlow.cpp
Source/WebCore/rendering/SimpleLineLayout.cpp
Source/WebCore/rendering/SimpleLineLayoutFlowContents.cpp
Source/WebCore/rendering/SimpleLineLayoutFlowContents.h
Source/WebCore/rendering/SimpleLineLayoutResolver.cpp

index a681ba9..38a62cf 100644 (file)
@@ -1,3 +1,19 @@
+2014-11-20  Zalan Bujtas  <zalan@apple.com>
+
+        Simple line layout: Introduce text fragment continuation.
+        https://bugs.webkit.org/show_bug.cgi?id=138274
+
+        Rebaseline for simple line layout's multiple rendere support.
+
+        Reviewed by Antti Koivisto.
+
+        * fast/text/simple-lines-multiple-renderers-expected.html: Added.
+        * fast/text/simple-lines-multiple-renderers.html: Added.
+        * fast/tokenizer/script_extra_close-expected.txt: Multiple tab characters should collapse into a single whitespace. This
+        needs fixing in complex line layout.
+        * tables/mozilla/bugs/bug157890-expected.txt: no-op endofline run is added by complex inline layout
+        when multiple text content is injected through JS into a <pre>. This requires fixing complex line layout.
+
 2014-11-20  Mark Lam  <mark.lam@apple.com>
 
         WTFCrashWithSecurityImplication under SpeculativeJIT::compile() when loading a page from theblaze.com.
diff --git a/LayoutTests/fast/text/simple-lines-multiple-renderers-expected.html b/LayoutTests/fast/text/simple-lines-multiple-renderers-expected.html
new file mode 100644 (file)
index 0000000..04f1066
--- /dev/null
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests that simple line layout is applied on multiple sibling text renderers.</title>
+<script>
+  if (window.internals)
+    internals.settings.setSimpleLineLayoutDebugBordersEnabled(true);
+</script>
+</head>
+<body>
+<div id=container>
+</div>
+<script>
+  var container = document.getElementById("container");
+  container.appendChild(document.createTextNode("Quo usque tandem abutere, Catilina, patientia nostra?  quam diu etiam\n"));
+  container.appendChild(document.createTextNode("furor iste tuus nos eludet?  quem ad finem sese effrenata iactabit\n"));
+  container.appendChild(document.createTextNode("audacia?  Nihilne te nocturnum praesidium Palati, nihil urbis vigiliae,\n"));
+  container.appendChild(document.createTextNode("nihil timor populi, nihil concursus bonorum omnium, nihil hic munitissimus\n"));
+  container.appendChild(document.createTextNode("habendi senatus locus, nihil horum ora voltusque moverunt?  Patere tua\n"));
+  container.appendChild(document.createTextNode("consilia non sentis, constrictam iam horum omnium scientia teneri\n"));
+  container.appendChild(document.createTextNode("coniurationem tuam non vides?  Quid proxima, quid superiore nocte egeris,\n"));
+  container.appendChild(document.createTextNode("ubi fueris, quos convocaveris, quid consilii ceperis, quem nostrum\n"));
+  container.appendChild(document.createTextNode("ignorare arbitraris?  O tempora, o mores!\n"));
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/text/simple-lines-multiple-renderers.html b/LayoutTests/fast/text/simple-lines-multiple-renderers.html
new file mode 100644 (file)
index 0000000..d04f2f1
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests that simple line layout is applied on multiple sibling text renderers.</title>
+<script>
+  if (window.internals)
+    internals.settings.setSimpleLineLayoutDebugBordersEnabled(true);
+</script>
+</head>
+<body>
+<div>
+  Quo usque tandem abutere, Catilina, patientia nostra?  quam diu etiam
+  furor iste tuus nos eludet?  quem ad finem sese effrenata iactabit
+  audacia?  Nihilne te nocturnum praesidium Palati, nihil urbis vigiliae,
+  nihil timor populi, nihil concursus bonorum omnium, nihil hic munitissimus
+  habendi senatus locus, nihil horum ora voltusque moverunt?  Patere tua
+  consilia non sentis, constrictam iam horum omnium scientia teneri
+  coniurationem tuam non vides?  Quid proxima, quid superiore nocte egeris,
+  ubi fueris, quos convocaveris, quid consilii ceperis, quem nostrum
+  ignorare arbitraris?  O tempora, o mores!
+</div>
+</body>
+</html>
index 79eb9a6..42fa8d8 100644 (file)
@@ -1 +1 @@
-TEST...        PASSED. This text should show up.
+TEST... PASSED. This text should show up.
index 3a3b9d6..85b69a6 100644 (file)
@@ -71,10 +71,8 @@ layer at (0,0) size 800x484
       RenderBlock {PRE} at (0,13) size 784x450
         RenderText {#text} at (0,0) size 216x15
           text run at (0,0) width 216: "Specified table width: 25px"
-          text run at (216,0) width 0: " "
         RenderText {#text} at (0,15) size 216x15
           text run at (0,15) width 216: "Actual table width:    25px"
-          text run at (216,15) width 0: " "
         RenderText {#text} at (0,30) size 168x15
           text run at (0,30) width 168: "Specified TD widths: "
         RenderText {#text} at (168,30) size 32x15
@@ -111,8 +109,7 @@ layer at (0,0) size 800x484
           text run at (648,30) width 32: "1px "
         RenderText {#text} at (680,30) size 32x15
           text run at (680,30) width 32: "8px "
-        RenderText {#text} at (712,30) size 0x15
-          text run at (712,30) width 0: " "
+        RenderText {#text} at (0,0) size 0x0
         RenderText {#text} at (0,45) size 168x15
           text run at (0,45) width 168: "Actual TD widths:    "
         RenderText {#text} at (168,45) size 32x15
@@ -149,15 +146,12 @@ layer at (0,0) size 800x484
           text run at (648,45) width 32: "1px "
         RenderText {#text} at (680,45) size 32x15
           text run at (680,45) width 32: "8px "
-        RenderText {#text} at (712,45) size 712x30
-          text run at (712,45) width 0: " "
+        RenderText {#text} at (0,60) size 0x15
           text run at (0,60) width 0: " "
         RenderText {#text} at (0,75) size 216x15
           text run at (0,75) width 216: "Specified table width: 12px"
-          text run at (216,75) width 0: " "
         RenderText {#text} at (0,90) size 216x15
           text run at (0,90) width 216: "Actual table width:    12px"
-          text run at (216,90) width 0: " "
         RenderText {#text} at (0,105) size 168x15
           text run at (0,105) width 168: "Specified TD widths: "
         RenderText {#text} at (168,105) size 32x15
@@ -170,8 +164,7 @@ layer at (0,0) size 800x484
           text run at (264,105) width 32: "1px "
         RenderText {#text} at (296,105) size 32x15
           text run at (296,105) width 32: "8px "
-        RenderText {#text} at (328,105) size 0x15
-          text run at (328,105) width 0: " "
+        RenderText {#text} at (0,0) size 0x0
         RenderText {#text} at (0,120) size 168x15
           text run at (0,120) width 168: "Actual TD widths:    "
         RenderText {#text} at (168,120) size 32x15
@@ -184,15 +177,12 @@ layer at (0,0) size 800x484
           text run at (264,120) width 32: "1px "
         RenderText {#text} at (296,120) size 32x15
           text run at (296,120) width 32: "8px "
-        RenderText {#text} at (328,120) size 328x30
-          text run at (328,120) width 0: " "
+        RenderText {#text} at (0,135) size 0x15
           text run at (0,135) width 0: " "
         RenderText {#text} at (0,150) size 216x15
           text run at (0,150) width 216: "Specified table width: 13px"
-          text run at (216,150) width 0: " "
         RenderText {#text} at (0,165) size 216x15
           text run at (0,165) width 216: "Actual table width:    13px"
-          text run at (216,165) width 0: " "
         RenderText {#text} at (0,180) size 168x15
           text run at (0,180) width 168: "Specified TD widths: "
         RenderText {#text} at (168,180) size 32x15
@@ -205,8 +195,7 @@ layer at (0,0) size 800x484
           text run at (264,180) width 32: "1px "
         RenderText {#text} at (296,180) size 32x15
           text run at (296,180) width 32: "8px "
-        RenderText {#text} at (328,180) size 0x15
-          text run at (328,180) width 0: " "
+        RenderText {#text} at (0,0) size 0x0
         RenderText {#text} at (0,195) size 168x15
           text run at (0,195) width 168: "Actual TD widths:    "
         RenderText {#text} at (168,195) size 32x15
@@ -219,15 +208,12 @@ layer at (0,0) size 800x484
           text run at (264,195) width 32: "1px "
         RenderText {#text} at (296,195) size 32x15
           text run at (296,195) width 32: "8px "
-        RenderText {#text} at (328,195) size 328x30
-          text run at (328,195) width 0: " "
+        RenderText {#text} at (0,210) size 0x15
           text run at (0,210) width 0: " "
         RenderText {#text} at (0,225) size 216x15
           text run at (0,225) width 216: "Specified table width: 24px"
-          text run at (216,225) width 0: " "
         RenderText {#text} at (0,240) size 216x15
           text run at (0,240) width 216: "Actual table width:    24px"
-          text run at (216,240) width 0: " "
         RenderText {#text} at (0,255) size 168x15
           text run at (0,255) width 168: "Specified TD widths: "
         RenderText {#text} at (168,255) size 32x15
@@ -240,8 +226,7 @@ layer at (0,0) size 800x484
           text run at (264,255) width 32: "1px "
         RenderText {#text} at (296,255) size 32x15
           text run at (296,255) width 32: "8px "
-        RenderText {#text} at (328,255) size 0x15
-          text run at (328,255) width 0: " "
+        RenderText {#text} at (0,0) size 0x0
         RenderText {#text} at (0,270) size 168x15
           text run at (0,270) width 168: "Actual TD widths:    "
         RenderText {#text} at (168,270) size 32x15
@@ -254,15 +239,12 @@ layer at (0,0) size 800x484
           text run at (264,270) width 32: "2px "
         RenderText {#text} at (296,270) size 40x15
           text run at (296,270) width 40: "16px "
-        RenderText {#text} at (336,270) size 336x30
-          text run at (336,270) width 0: " "
+        RenderText {#text} at (0,285) size 0x15
           text run at (0,285) width 0: " "
         RenderText {#text} at (0,300) size 216x15
           text run at (0,300) width 216: "Specified table width: 25px"
-          text run at (216,300) width 0: " "
         RenderText {#text} at (0,315) size 216x15
           text run at (0,315) width 216: "Actual table width:    25px"
-          text run at (216,315) width 0: " "
         RenderText {#text} at (0,330) size 168x15
           text run at (0,330) width 168: "Specified TD widths: "
         RenderText {#text} at (168,330) size 32x15
@@ -275,8 +257,7 @@ layer at (0,0) size 800x484
           text run at (264,330) width 32: "1px "
         RenderText {#text} at (296,330) size 32x15
           text run at (296,330) width 32: "8px "
-        RenderText {#text} at (328,330) size 0x15
-          text run at (328,330) width 0: " "
+        RenderText {#text} at (0,0) size 0x0
         RenderText {#text} at (0,345) size 168x15
           text run at (0,345) width 168: "Actual TD widths:    "
         RenderText {#text} at (168,345) size 32x15
@@ -289,15 +270,12 @@ layer at (0,0) size 800x484
           text run at (264,345) width 32: "2px "
         RenderText {#text} at (296,345) size 40x15
           text run at (296,345) width 40: "16px "
-        RenderText {#text} at (336,345) size 336x30
-          text run at (336,345) width 0: " "
+        RenderText {#text} at (0,360) size 0x15
           text run at (0,360) width 0: " "
         RenderText {#text} at (0,375) size 216x15
           text run at (0,375) width 216: "Specified table width: 17px"
-          text run at (216,375) width 0: " "
         RenderText {#text} at (0,390) size 216x15
           text run at (0,390) width 216: "Actual table width:    17px"
-          text run at (216,390) width 0: " "
         RenderText {#text} at (0,405) size 168x15
           text run at (0,405) width 168: "Specified TD widths: "
         RenderText {#text} at (168,405) size 32x15
@@ -320,8 +298,7 @@ layer at (0,0) size 800x484
           text run at (424,405) width 32: "10% "
         RenderText {#text} at (456,405) size 32x15
           text run at (456,405) width 32: "10% "
-        RenderText {#text} at (488,405) size 0x15
-          text run at (488,405) width 0: " "
+        RenderText {#text} at (0,0) size 0x0
         RenderText {#text} at (0,420) size 168x15
           text run at (0,420) width 168: "Actual TD widths:    "
         RenderText {#text} at (168,420) size 32x15
@@ -344,6 +321,5 @@ layer at (0,0) size 800x484
           text run at (424,420) width 32: "1px "
         RenderText {#text} at (456,420) size 32x15
           text run at (456,420) width 32: "1px "
-        RenderText {#text} at (488,420) size 488x30
-          text run at (488,420) width 0: " "
+        RenderText {#text} at (0,435) size 0x15
           text run at (0,435) width 0: " "
index 321d4a6..da3c4a9 100644 (file)
@@ -1,3 +1,49 @@
+2014-11-20  Zalan Bujtas  <zalan@apple.com>
+
+        Simple line layout: Introduce text fragment continuation.
+        https://bugs.webkit.org/show_bug.cgi?id=138274
+
+        This patch extends simple line layout coverage to multiple text renderers.
+        When a particular render flow has multiple text renderers (but not any other type)
+        then we use simple line layout to process and paint the content. -other, existing requirements still apply
+        so that for example if the content requires decoration, we bail out of simple line layout.
+
+        FlowContent now supports multiple renderes. It continuously reads content from sibling renderers
+        so that the simple line layout parser sees it as one monolithic block of content. Run positions
+        are all relative to the block and they get resolved to renderer's positions on demand.
+        (painting, computing bounding rects etc)
+
+        Reviewed by Antti Koivisto.
+
+        Performance test already been added for the multiple rendere use case,
+        correctness is covered by existing test cases.
+        Test: fast/text/simple-lines-mutliple-renderers.html
+
+        * rendering/SimpleLineLayout.cpp:
+        (WebCore::SimpleLineLayout::canUseFor): Check if children are all 8bit RenderTexts.
+        (WebCore::SimpleLineLayout::removeTrailingWhitespace): Move the endofline check right before where we might overflow using end position.
+        (WebCore::SimpleLineLayout::initializeNewLine):
+        (WebCore::SimpleLineLayout::closeLineEndingAndAdjustRuns):
+        (WebCore::SimpleLineLayout::splitRunsAtRendererBoundary): Split runs at renderers' boundary to be in sync with inline text renderering.
+        (WebCore::SimpleLineLayout::createTextRuns):
+        (WebCore::SimpleLineLayout::create):
+        * rendering/SimpleLineLayoutFlowContents.cpp:
+        (WebCore::SimpleLineLayout::FlowContents::FlowContents):
+        (WebCore::SimpleLineLayout::FlowContents::findNextBreakablePosition):
+        (WebCore::SimpleLineLayout::FlowContents::findNextNonWhitespacePosition):
+        (WebCore::SimpleLineLayout::FlowContents::textWidth): Do not measure text across renderers. It could produce different width value due to
+        ligature which later can produce unexpected line breaks and out sync rendering in general.
+        (WebCore::SimpleLineLayout::FlowContents::renderer): 
+        (WebCore::SimpleLineLayout::FlowContents::resolveRendererPositions):
+        (WebCore::SimpleLineLayout::FlowContents::appendNextRendererContentIfNeeded): Read the next renderer content if needed.
+        (WebCore::SimpleLineLayout::FlowContents::nextNonWhitespacePosition):
+        (WebCore::SimpleLineLayout::FlowContents::runWidth):
+        * rendering/SimpleLineLayoutFlowContents.h:
+        (WebCore::SimpleLineLayout::FlowContents::isNewlineCharacter):
+        (WebCore::SimpleLineLayout::FlowContents::isEndOfContent):
+        * rendering/SimpleLineLayoutResolver.cpp:
+        (WebCore::SimpleLineLayout::RunResolver::Run::text):
+
 2014-11-20  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r176396.
index 47e3b1b..a358a03 100644 (file)
@@ -3497,7 +3497,9 @@ void RenderBlockFlow::deleteLineBoxesBeforeSimpleLineLayout()
 {
     ASSERT(m_lineLayoutPath == SimpleLinesPath);
     lineBoxes().deleteLineBoxes();
-    downcast<RenderText>(*firstChild()).deleteLineBoxesBeforeSimpleLineLayout();
+    ASSERT(!childrenOfType<RenderElement>(*this).first());
+    for (auto& textRenderer : childrenOfType<RenderText>(*this))
+        textRenderer.deleteLineBoxesBeforeSimpleLineLayout();
 }
 
 void RenderBlockFlow::ensureLineBoxes()
index bb7d474..f2c3345 100644 (file)
@@ -37,6 +37,7 @@
 #include "LineWidth.h"
 #include "PaintInfo.h"
 #include "RenderBlockFlow.h"
+#include "RenderChildIterator.h"
 #include "RenderStyle.h"
 #include "RenderText.h"
 #include "RenderTextControl.h"
@@ -93,14 +94,12 @@ bool canUseFor(const RenderBlockFlow& flow)
         return false;
     if (!flow.firstChild())
         return false;
-    // This currently covers <blockflow>#text</blockflow> case.
+    // This currently covers <blockflow>#text</blockflow> and mutiple (sibling) RenderText cases.
     // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
-    if (flow.firstChild() != flow.lastChild())
-        return false;
-    if (!is<RenderText>(flow.firstChild()))
-        return false;
-    if (!downcast<RenderText>(*flow.firstChild()).text()->is8Bit())
-        return false;
+    for (const auto& renderer : childrenOfType<RenderObject>(flow)) {
+        if (!is<RenderText>(renderer) || !downcast<RenderText>(renderer).text()->is8Bit())
+            return false;
+    }
     if (!flow.isHorizontalWritingMode())
         return false;
     if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
@@ -406,10 +405,8 @@ static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& li
         lineState.removeCommittedTrailingWhitespace();
     }
 
-    if (flowContents.isEndOfContent(lineState.position))
-        return;
     // If we skipped any whitespace and now the line end is a "preserved" newline, skip the newline too as we are wrapping the line here already.
-    if (lastPosition != lineState.position && style.preserveNewline && flowContents.isNewlineCharacter(lineState.position))
+    if (lastPosition != lineState.position && style.preserveNewline && !flowContents.isEndOfContent(lineState.position) && flowContents.isNewlineCharacter(lineState.position))
         ++lineState.position;
 }
 
@@ -471,7 +468,7 @@ static TextFragment splitFragmentToFitLine(TextFragment& fragmentToSplit, float
     return fragmentForNextLine;
 }
 
-static TextFragment nextFragment(unsigned previousFragmentEnd, FlowContents& flowContents, float xPosition)
+static TextFragment nextFragment(unsigned previousFragmentEnd, const FlowContents& flowContents, float xPosition)
 {
     // A fragment can have
     // 1. new line character when preserveNewline is on (not considered as whitespace) or
@@ -510,7 +507,7 @@ static TextFragment nextFragment(unsigned previousFragmentEnd, FlowContents& flo
     return fragment;
 }
 
-static bool createLineRuns(LineState& lineState, Layout::RunVector& lineRuns, FlowContents& flowContents)
+static bool createLineRuns(LineState& lineState, Layout::RunVector& lineRuns, const FlowContents& flowContents)
 {
     const auto& style = flowContents.style();
     bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow;
@@ -586,6 +583,34 @@ static void closeLineEndingAndAdjustRuns(LineState& lineState, Layout::RunVector
     ++lineCount;
 }
 
+static void splitRunsAtRendererBoundary(Layout::RunVector& lineRuns, const FlowContents& flowContents)
+{
+    if (!lineRuns.size())
+        return;
+
+    unsigned runIndex = 0;
+    do {
+        const Run& run = lineRuns.at(runIndex);
+        ASSERT(run.start != run.end);
+        const RenderText* startRenderer = flowContents.renderer(run.start);
+        const RenderText* endRenderer = flowContents.renderer(run.end - 1);
+        if (startRenderer == endRenderer)
+            continue;
+        // This run overlaps multiple renderers. Split it up.
+        unsigned rendererStartPosition = 0;
+        unsigned rendererEndPosition = 0;
+        bool found = flowContents.resolveRendererPositions(*startRenderer, rendererStartPosition, rendererEndPosition);
+        ASSERT_UNUSED(found, found);
+
+        // 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.
+        float logicalRightOfLeftRun = run.logicalLeft + flowContents.textWidth(run.start, rendererEndPosition, run.logicalLeft);
+        lineRuns.insert(runIndex, Run(run.start, rendererEndPosition, run.logicalLeft, logicalRightOfLeftRun, false));
+        Run& rightSideRun = lineRuns.at(runIndex + 1);
+        rightSideRun.start = rendererEndPosition;
+        rightSideRun.logicalLeft = logicalRightOfLeftRun;
+    } while (++runIndex < lineRuns.size());
+}
+
 static void updateLineConstrains(const RenderBlockFlow& flow, float& availableWidth, float& logicalLeftOffset)
 {
     LayoutUnit height = flow.logicalHeight();
@@ -610,6 +635,9 @@ static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsig
         isEndOfContent = createLineRuns(lineState, runs, flowContents);
         closeLineEndingAndAdjustRuns(lineState, runs, lineCount, flowContents);
     } while (!isEndOfContent);
+
+    if (flow.firstChild() != flow.lastChild())
+        splitRunsAtRendererBoundary(runs, flowContents);
     ASSERT(!lineState.uncommittedWidth);
 }
 
@@ -617,11 +645,12 @@ std::unique_ptr<Layout> create(RenderBlockFlow& flow)
 {
     unsigned lineCount = 0;
     Layout::RunVector runs;
-    RenderText& textRenderer = downcast<RenderText>(*flow.firstChild());
-    ASSERT(!textRenderer.firstTextBox());
 
     createTextRuns(runs, flow, lineCount);
-    textRenderer.clearNeedsLayout();
+    for (auto& renderer : childrenOfType<RenderObject>(flow)) {
+        ASSERT(is<RenderText>(renderer));
+        renderer.clearNeedsLayout();
+    }
     return Layout::create(runs, lineCount);
 }
 
index dfb28d4..2fc1010 100644 (file)
@@ -27,6 +27,8 @@
 #include "SimpleLineLayoutFlowContents.h"
 
 #include "RenderBlockFlow.h"
+#include "RenderChildIterator.h"
+#include "RenderText.h"
 
 namespace WebCore {
 namespace SimpleLineLayout {
@@ -35,21 +37,135 @@ FlowContents::FlowContents(const RenderBlockFlow& flow)
     : m_flow(flow)
     , m_style(flow.style())
     , m_lineBreakIterator(downcast<RenderText>(*flow.firstChild()).text(), flow.style().locale())
+    , m_lastRendererIndex(0)
 {
+    unsigned startPosition = 0;
+    for (const RenderText* textRenderer = downcast<RenderText>(m_flow.firstChild()); textRenderer; textRenderer = downcast<RenderText>(textRenderer->nextSibling())) {
+        unsigned contentLength = textRenderer->text()->length();
+        m_textRanges.append(std::make_pair(startPosition, textRenderer));
+        startPosition += contentLength;
+    }
+    // End item.
+    const RenderText* closingNullItem = nullptr;
+    m_textRanges.append(std::make_pair(startPosition, closingNullItem));
 }
 
-unsigned FlowContents::findNextBreakablePosition(unsigned position)
+unsigned FlowContents::findNextBreakablePosition(unsigned position) const
 {
     String string = m_lineBreakIterator.string();
-    return nextBreakablePosition<LChar, false>(m_lineBreakIterator, string.characters8(), string.length(), position);
+    unsigned breakablePosition = nextBreakablePosition<LChar, false>(m_lineBreakIterator, string.characters8(), string.length(), position);
+    if (appendNextRendererContentIfNeeded(breakablePosition))
+        return findNextBreakablePosition(position);
+    ASSERT(breakablePosition >= position);
+    return breakablePosition;
 }
 
 unsigned FlowContents::findNextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
 {
+    unsigned nonWhitespacePosition = nextNonWhitespacePosition(position, spaceCount);
+    if (appendNextRendererContentIfNeeded(nonWhitespacePosition))
+        return findNextNonWhitespacePosition(position, spaceCount);
+    ASSERT(nonWhitespacePosition >= position);
+    return nonWhitespacePosition;
+}
+
+float FlowContents::textWidth(unsigned from, unsigned to, float xPosition) const
+{
+    unsigned rendererStart = 0;
+    const RenderText* textRenderer = renderer(from, &rendererStart);
+    ASSERT(textRenderer);
+    // Resolved positions are relative to the renderers.
+    unsigned absoluteStart = from - rendererStart;
+    unsigned absoluteEnd = to - rendererStart;
+    if ((m_style.font.isFixedPitch() && textRenderer == renderer(to)) || (!absoluteStart && absoluteEnd == textRenderer->text()->length()))
+        return textRenderer->width(absoluteStart, to - from, m_style.font, xPosition, nullptr, nullptr);
+
+    // We need to split up the text and measure renderers individually due to ligature.
+    float textWidth = 0;
+    unsigned fragmentEnd = 0;
+    do {
+        fragmentEnd = std::min(to, rendererStart + textRenderer->text()->length());
+        unsigned absoluteFragmentEnd = fragmentEnd - rendererStart;
+        absoluteStart = from - rendererStart;
+        textWidth += runWidth(*textRenderer, absoluteStart, absoluteFragmentEnd, xPosition + textWidth);
+        from = fragmentEnd;
+        if (fragmentEnd < to)
+            textRenderer = renderer(fragmentEnd, &rendererStart);
+    } while (fragmentEnd < to && textRenderer);
+    return textWidth;
+}
+
+const RenderText* FlowContents::renderer(unsigned position, unsigned* rendererStartPosition) const
+{
+    unsigned arraySize = m_textRanges.size();
+    // Take advantage of the usage pattern.
+    if (position >= m_textRanges.at(m_lastRendererIndex).first && m_lastRendererIndex + 1 < arraySize && position < m_textRanges.at(m_lastRendererIndex + 1).first) {
+        if (rendererStartPosition)
+            *rendererStartPosition = m_textRanges.at(m_lastRendererIndex).first;
+        return m_textRanges.at(m_lastRendererIndex).second;
+    }
+    unsigned left = 0;
+    unsigned right = arraySize - 1;
+    ASSERT(arraySize);
+    ASSERT(position >= 0);
+    while (left < right) {
+        unsigned middle = (left + right) / 2;
+        unsigned endPosition = m_textRanges.at(middle + 1).first;
+        if (position > endPosition)
+            left = middle + 1;
+        else if (position < endPosition)
+            right = middle;
+        else {
+            right = middle + 1;
+            break;
+        }
+    }
+    if (rendererStartPosition)
+        *rendererStartPosition = m_textRanges.at(right).first;
+    return m_textRanges.at(right).second;
+}
+
+bool FlowContents::resolveRendererPositions(const RenderText& renderer, unsigned& startPosition, unsigned& endPosition) const
+{
+    unsigned arraySize = m_textRanges.size();
+    if (!arraySize)
+        return false;
+
+    unsigned index = 0;
+    do {
+        auto range = m_textRanges.at(index);
+        if (range.second == &renderer) {
+            startPosition = range.first;
+            ASSERT(index + 1 < arraySize);
+            endPosition = m_textRanges.at(index + 1).first;
+            return true;
+        }
+    } while (++index < arraySize);
+    return false;
+}
+
+bool FlowContents::appendNextRendererContentIfNeeded(unsigned position) const
+{
+    String string = m_lineBreakIterator.string();
+    if (position < string.length())
+        return false;
+
+    // Content needs to be requested sequentially.
+    ASSERT(position == string.length());
+    const RenderText* nextRenderer = renderer(position);
+    if (!nextRenderer)
+        return false;
+
+    ++m_lastRendererIndex;
+    m_lineBreakIterator.resetStringAndReleaseIterator(string + String(nextRenderer->text()), m_flow.style().locale());
+    return true;
+}
+
+unsigned FlowContents::nextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
+{
     String string = m_lineBreakIterator.string();
     unsigned length = string.length();
     const LChar* text = string.characters8();
-    spaceCount = 0;
     for (; position < length; ++position) {
         bool isSpace = text[position] == ' ';
         if (!(isSpace || text[position] == '\t' || (!m_style.preserveNewline && text[position] == '\n')))
@@ -60,35 +176,15 @@ unsigned FlowContents::findNextNonWhitespacePosition(unsigned position, unsigned
     return length;
 }
 
-float FlowContents::textWidth(unsigned from, unsigned to, float xPosition) const
+float FlowContents::runWidth(const RenderText& renderer, unsigned from, unsigned to, float xPosition) const
 {
-    String string = m_lineBreakIterator.string();
-    unsigned length = string.length();
-    if (m_style.font.isFixedPitch() || (!from && to == length)) {
-        const RenderText& renderer = downcast<RenderText>(*m_flow.firstChild());
-        return renderer.width(from, to - from, m_style.font, xPosition, nullptr, nullptr);
-    }
-
+    ASSERT(from < to);
+    String string = renderer.text();
     TextRun run(string.characters8() + from, to - from);
     run.setXPos(xPosition);
-    run.setCharactersLength(length - from);
     run.setTabSize(!!m_style.tabWidth, m_style.tabWidth);
-    ASSERT(run.charactersLength() >= run.length());
     return m_style.font.width(run);
 }
 
-bool FlowContents::resolveRendererPositions(const RenderText& renderer, unsigned& startPosition, unsigned& endPosition) const
-{
-    ASSERT(&renderer == downcast<RenderText>(m_flow.firstChild()));
-    startPosition = 0;
-    endPosition = renderer.text()->length();
-    return true;
-}
-
-const RenderText& FlowContents::renderer(unsigned) const
-{
-    return downcast<RenderText>(*m_flow.firstChild());
-}
-
 }
 }
index 1f2f861..33df20a 100644 (file)
@@ -40,7 +40,7 @@ class FlowContents {
 public:
     FlowContents(const RenderBlockFlow&);
 
-    unsigned findNextBreakablePosition(unsigned position);
+    unsigned findNextBreakablePosition(unsigned position) const;
     unsigned findNextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const;
 
     float textWidth(unsigned from, unsigned to, float xPosition) const;
@@ -49,7 +49,7 @@ public:
     bool isEndOfContent(unsigned position) const;
 
     bool resolveRendererPositions(const RenderText&, unsigned& startPosition, unsigned& endPosition) const;
-    const RenderText& renderer(unsigned position) const;
+    const RenderText* renderer(unsigned position, unsigned* startPosition = nullptr) const;
 
     class Style {
     public:
@@ -77,23 +77,27 @@ public:
     const Style& style() const { return m_style; }
 
 private:
+    bool appendNextRendererContentIfNeeded(unsigned position) const;
     unsigned nextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const;
-    float runWidth(unsigned from, unsigned to, float xPosition) const;
+    float runWidth(const RenderText&, unsigned from, unsigned to, float xPosition) const;
 
     const RenderBlockFlow& m_flow;
-    Style m_style;
-    LazyLineBreakIterator m_lineBreakIterator;
+    const Style m_style;
+    mutable LazyLineBreakIterator m_lineBreakIterator;
+    Vector<std::pair<unsigned, const RenderText*>> m_textRanges;
+    mutable unsigned m_lastRendererIndex;
 };
 
 inline bool FlowContents::isNewlineCharacter(unsigned position) const
 {
-    ASSERT(m_lineBreakIterator.string().length() > position);
+    appendNextRendererContentIfNeeded(position);
+    ASSERT(position < m_lineBreakIterator.string().length());
     return m_lineBreakIterator.string().at(position) == '\n';
 }
 
 inline bool FlowContents::isEndOfContent(unsigned position) const
 {
-    return position >= m_lineBreakIterator.string().length();
+    return position >= m_lineBreakIterator.string().length() && !renderer(position);
 }
 
 }
index e2ea35d..4fd0574 100644 (file)
@@ -76,9 +76,11 @@ StringView RunResolver::Run::text() const
 {
     auto& resolver = m_iterator.resolver();
     auto& run = m_iterator.simpleRun();
-    const auto& renderer = resolver.m_flowContents.renderer(run.start);
-    ASSERT(renderer.is8Bit());
-    return StringView(renderer.characters8(), renderer.textLength()).substring(run.start, run.end - run.start);
+    unsigned rendererOffset = 0;
+    const auto* renderer = resolver.m_flowContents.renderer(run.start, &rendererOffset);
+    ASSERT(renderer);
+    ASSERT(renderer->is8Bit());
+    return StringView(renderer->characters8(), renderer->textLength()).substring(run.start - rendererOffset, run.end - run.start);
 }
 
 RunResolver::Iterator::Iterator(const RunResolver& resolver, unsigned runIndex, unsigned lineIndex)