047d7b2f0b9b68fbeca9775bc4abf2f790c406ab
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayout.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "SimpleLineLayout.h"
28
29 #include "FontCache.h"
30 #include "Frame.h"
31 #include "GraphicsContext.h"
32 #include "HTMLTextFormControlElement.h"
33 #include "HitTestLocation.h"
34 #include "HitTestRequest.h"
35 #include "HitTestResult.h"
36 #include "InlineTextBox.h"
37 #include "LineWidth.h"
38 #include "PaintInfo.h"
39 #include "RenderBlockFlow.h"
40 #include "RenderChildIterator.h"
41 #include "RenderStyle.h"
42 #include "RenderText.h"
43 #include "RenderTextControl.h"
44 #include "RenderView.h"
45 #include "Settings.h"
46 #include "SimpleLineLayoutFlowContents.h"
47 #include "SimpleLineLayoutFunctions.h"
48 #include "Text.h"
49 #include "TextPaintStyle.h"
50
51 namespace WebCore {
52 namespace SimpleLineLayout {
53
54 template <typename CharacterType>
55 static bool canUseForText(const CharacterType* text, unsigned length, const Font& font)
56 {
57     // FIXME: <textarea maxlength=0> generates empty text node.
58     if (!length)
59         return false;
60     for (unsigned i = 0; i < length; ++i) {
61         UChar character = text[i];
62         if (character == ' ')
63             continue;
64
65         // These would be easy to support.
66         if (character == noBreakSpace)
67             return false;
68         if (character == softHyphen)
69             return false;
70
71         UCharDirection direction = u_charDirection(character);
72         if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
73             || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
74             || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE
75             || direction == U_POP_DIRECTIONAL_FORMAT || direction == U_BOUNDARY_NEUTRAL)
76             return false;
77
78         if (!font.glyphForCharacter(character))
79             return false;
80     }
81     return true;
82 }
83
84 static bool canUseForText(const RenderText& textRenderer, const Font& font)
85 {
86     if (textRenderer.is8Bit())
87         return canUseForText(textRenderer.characters8(), textRenderer.textLength(), font);
88     return canUseForText(textRenderer.characters16(), textRenderer.textLength(), font);
89 }
90
91 bool canUseFor(const RenderBlockFlow& flow)
92 {
93     if (!flow.frame().settings().simpleLineLayoutEnabled())
94         return false;
95     if (!flow.firstChild())
96         return false;
97     // This currently covers <blockflow>#text</blockflow> and mutiple (sibling) RenderText cases.
98     // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
99     for (const auto& renderer : childrenOfType<RenderObject>(flow)) {
100         if (!is<RenderText>(renderer))
101             return false;
102     }
103     if (!flow.isHorizontalWritingMode())
104         return false;
105     if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
106         return false;
107     // Printing does pagination without a flow thread.
108     if (flow.document().paginated())
109         return false;
110     if (flow.hasOutline())
111         return false;
112     if (flow.isRubyText() || flow.isRubyBase())
113         return false;
114     if (flow.parent()->isDeprecatedFlexibleBox())
115         return false;
116     // FIXME: Implementation of wrap=hard looks into lineboxes.
117     if (flow.parent()->isTextArea() && flow.parent()->element()->fastHasAttribute(HTMLNames::wrapAttr))
118         return false;
119     // FIXME: Placeholders do something strange.
120     if (is<RenderTextControl>(*flow.parent()) && downcast<RenderTextControl>(*flow.parent()).textFormControlElement().placeholderElement())
121         return false;
122     const RenderStyle& style = flow.style();
123     if (style.textDecorationsInEffect() != TextDecorationNone)
124         return false;
125     if (style.textAlign() == JUSTIFY)
126         return false;
127     // Non-visible overflow should be pretty easy to support.
128     if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
129         return false;
130     if (!style.textIndent().isZero())
131         return false;
132     if (!style.wordSpacing().isZero() || style.letterSpacing())
133         return false;
134     if (!style.isLeftToRightDirection())
135         return false;
136     if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
137         return false;
138     if (style.writingMode() != TopToBottomWritingMode)
139         return false;
140     if (style.lineBreak() != LineBreakAuto)
141         return false;
142     if (style.wordBreak() != NormalWordBreak)
143         return false;
144     if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder)
145         return false;
146     if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone)
147         return false;
148     if (style.hyphens() == HyphensAuto)
149         return false;
150     if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
151         return false;
152     if (style.textShadow())
153         return false;
154     if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style().textOverflow()))
155         return false;
156     if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER))
157         return false;
158     if (style.hasTextCombine())
159         return false;
160     if (style.backgroundClip() == TextFillBox)
161         return false;
162     if (style.borderFit() == BorderFitLines)
163         return false;
164     if (style.lineBreak() != LineBreakAuto)
165         return false;
166
167     // We can't use the code path if any lines would need to be shifted below floats. This is because we don't keep per-line y coordinates.
168     if (flow.containsFloats()) {
169         float minimumWidthNeeded = std::numeric_limits<float>::max();
170         for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
171             minimumWidthNeeded = std::min(minimumWidthNeeded, textRenderer.minLogicalWidth());
172
173             for (auto& floatingObject : *flow.floatingObjectSet()) {
174                 ASSERT(floatingObject);
175 #if ENABLE(CSS_SHAPES)
176                 // if a float has a shape, we cannot tell if content will need to be shifted until after we lay it out,
177                 // since the amount of space is not uniform for the height of the float.
178                 if (floatingObject->renderer().shapeOutsideInfo())
179                     return false;
180 #endif
181                 float availableWidth = flow.availableLogicalWidthForLine(floatingObject->y(), false);
182                 if (availableWidth < minimumWidthNeeded)
183                     return false;
184             }
185         }
186     }
187     if (style.fontCascade().primaryFont().isSVGFont())
188         return false;
189     // We assume that all lines have metrics based purely on the primary font.
190     auto& primaryFont = style.fontCascade().primaryFont();
191     if (primaryFont.isLoading())
192         return false;
193     for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
194         if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isTextFragment()
195             || textRenderer.isSVGInlineText())
196             return false;
197         if (style.fontCascade().codePath(TextRun(textRenderer.text())) != FontCascade::Simple)
198             return false;
199         if (!canUseForText(textRenderer, primaryFont))
200             return false;
201     }
202     return true;
203 }
204
205 static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset)
206 {
207     float remainingWidth = availableWidth - committedWidth;
208     float left = logicalLeftOffset;
209     switch (textAlign) {
210     case LEFT:
211     case WEBKIT_LEFT:
212     case TASTART:
213         return left;
214     case RIGHT:
215     case WEBKIT_RIGHT:
216     case TAEND:
217         return left + std::max<float>(remainingWidth, 0);
218     case CENTER:
219     case WEBKIT_CENTER:
220         return left + std::max<float>(remainingWidth / 2, 0);
221     case JUSTIFY:
222         ASSERT_NOT_REACHED();
223         break;
224     }
225     ASSERT_NOT_REACHED();
226     return 0;
227 }
228
229 struct LineState {
230     void commitAndCreateRun(Layout::RunVector& lineRuns)
231     {
232         if (uncommittedStart == uncommittedEnd)
233             return;
234
235         lineRuns.append(Run(uncommittedStart, uncommittedEnd, committedLogicalRight, committedLogicalRight + uncommittedWidth, false));
236         // Move uncommitted to committed.
237         committedWidth += uncommittedWidth;
238         committedLogicalRight += committedWidth;
239         committedTrailingWhitespaceWidth = uncomittedTrailingWhitespaceWidth;
240         committedTrailingWhitespaceLength = uncomittedTrailingWhitespaceLength;
241
242         uncommittedStart = uncommittedEnd;
243         uncommittedWidth = 0;
244         uncomittedTrailingWhitespaceWidth = 0;
245         uncomittedTrailingWhitespaceLength = 0;
246     }
247
248     void addUncommitted(const FlowContents::TextFragment& fragment)
249     {
250         unsigned uncomittedFragmentLength = fragment.end - uncommittedEnd;
251         uncommittedWidth += fragment.width;
252         uncommittedEnd = fragment.end;
253         position = uncommittedEnd;
254         uncomittedTrailingWhitespaceWidth = fragment.type == FlowContents::TextFragment::Whitespace ? fragment.width : 0;
255         uncomittedTrailingWhitespaceLength = fragment.type == FlowContents::TextFragment::Whitespace ? uncomittedFragmentLength  : 0;
256     }
257
258     void addUncommittedWhitespace(float whitespaceWidth)
259     {
260         addUncommitted(FlowContents::TextFragment(uncommittedEnd, uncommittedEnd + 1, whitespaceWidth, true));
261     }
262
263     void jumpTo(unsigned newPositon, float logicalRight)
264     {
265         position = newPositon;
266
267         uncommittedStart = newPositon;
268         uncommittedEnd = newPositon;
269         uncommittedWidth = 0;
270         committedLogicalRight = logicalRight;
271     }
272
273     bool hasWhitespaceOnly() const
274     {
275         return committedTrailingWhitespaceWidth && committedWidth == committedTrailingWhitespaceWidth;
276     }
277
278     float width() const
279     {
280         return committedWidth + uncommittedWidth;
281     }
282
283     bool fits(float extra) const
284     {
285         return availableWidth >= width() + extra;
286     }
287
288     void removeTrailingWhitespace()
289     {
290         committedWidth -= committedTrailingWhitespaceWidth;
291         committedLogicalRight -= committedTrailingWhitespaceWidth;
292         committedTrailingWhitespaceWidth = 0;
293         committedTrailingWhitespaceLength = 0;
294     }
295
296     float availableWidth { 0 };
297     float logicalLeftOffset { 0 };
298     unsigned lineStartRunIndex { 0 }; // The run that the line starts with.
299     unsigned position { 0 };
300
301     unsigned uncommittedStart { 0 };
302     unsigned uncommittedEnd { 0 };
303     float uncommittedWidth { 0 };
304     float committedWidth { 0 };
305
306     float committedLogicalRight { 0 }; // Last committed X (coordinate) position.
307     float committedTrailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
308     unsigned committedTrailingWhitespaceLength { 0 };
309
310     FlowContents::TextFragment oveflowedFragment;
311
312 private:
313     float uncomittedTrailingWhitespaceWidth { 0 };
314     unsigned uncomittedTrailingWhitespaceLength { 0 };
315 };
316
317 static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& lineRuns, const FlowContents& flowContents)
318 {
319     const auto& style = flowContents.style();
320     bool preWrap = style.wrapLines && !style.collapseWhitespace;
321     // Trailing whitespace gets removed when we either collapse whitespace or pre-wrap is present.
322     if (!(style.collapseWhitespace || preWrap))
323         return;
324
325     ASSERT(lineRuns.size());
326     Run& lastRun = lineRuns.last();
327
328     unsigned lastPosition = lineState.position;
329     bool trailingPreWrapWhitespaceNeedsToBeRemoved = false;
330     if (preWrap) {
331         // Special overflow pre-wrap fragment handling: Ignore the overflow whitespace fragment if we managed to fit at least one character on this line.
332         // When the line is too short to fit one character (thought it still stays on the line) we keep the overflow whitespace content as it is.
333         if (lineState.oveflowedFragment.type == FlowContents::TextFragment::Whitespace && !lineState.oveflowedFragment.isEmpty() && lineState.availableWidth >= lineState.committedWidth) {
334             lineState.position = lineState.oveflowedFragment.end;
335             lineState.oveflowedFragment = FlowContents::TextFragment();
336         }
337         // Remove whitespace, unless it's the only fragment on the line -so removing the whitesapce would produce an empty line.
338         trailingPreWrapWhitespaceNeedsToBeRemoved = !lineState.hasWhitespaceOnly();
339     }
340     if (lineState.committedTrailingWhitespaceLength && (style.collapseWhitespace || trailingPreWrapWhitespaceNeedsToBeRemoved)) {
341         lastRun.logicalRight -= lineState.committedTrailingWhitespaceWidth;
342         lastRun.end -= lineState.committedTrailingWhitespaceLength;
343         if (lastRun.start == lastRun.end)
344             lineRuns.removeLast();
345         lineState.removeTrailingWhitespace();
346     }
347
348     // If we skipped any whitespace and now the line end is a hard newline, skip the newline too as we are wrapping the line here already.
349     if (lastPosition != lineState.position && !flowContents.isEnd(lineState.position) && flowContents.isLineBreak(lineState.position))
350         ++lineState.position;
351 }
352
353 static void updateLineConstrains(const RenderBlockFlow& flow, float& availableWidth, float& logicalLeftOffset)
354 {
355     LayoutUnit height = flow.logicalHeight();
356     LayoutUnit logicalHeight = flow.minLineHeightForReplacedRenderer(false, 0);
357     float logicalRightOffset = flow.logicalRightOffsetForLine(height, false, logicalHeight);
358     logicalLeftOffset = flow.logicalLeftOffsetForLine(height, false, logicalHeight);
359     availableWidth = std::max<float>(0, logicalRightOffset - logicalLeftOffset);
360 }
361
362 static LineState initializeNewLine(const LineState& previousLine, const RenderBlockFlow& flow, const FlowContents& flowContents, unsigned lineStartRunIndex)
363 {
364     LineState lineState;
365     lineState.jumpTo(previousLine.position, 0);
366     lineState.lineStartRunIndex = lineStartRunIndex;
367     updateLineConstrains(flow, lineState.availableWidth, lineState.logicalLeftOffset);
368     // Skip leading whitespace if collapsing whitespace, unless there's an uncommitted fragment pushed from the previous line.
369     // FIXME: Be smarter when the run from the previous line does not fit the current line. Right now, we just reprocess it.
370     if (previousLine.oveflowedFragment.width) {
371         if (lineState.fits(previousLine.oveflowedFragment.width))
372             lineState.addUncommitted(previousLine.oveflowedFragment);
373         else {
374             // Start over with this fragment.
375             lineState.jumpTo(previousLine.oveflowedFragment.start, 0);
376         }
377     } else {
378         unsigned spaceCount = 0;
379         lineState.jumpTo(flowContents.style().collapseWhitespace ? flowContents.findNextNonWhitespacePosition(previousLine.position, spaceCount) : previousLine.position, 0);
380     }
381     return lineState;
382 }
383
384 static FlowContents::TextFragment splitFragmentToFitLine(FlowContents::TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const FlowContents& flowContents)
385 {
386     // Fast path for single char fragments.
387     if (fragmentToSplit.start + 1 == fragmentToSplit.end) {
388         if (keepAtLeastOneCharacter)
389             return FlowContents::TextFragment();
390
391         FlowContents::TextFragment fragmentForNextLine(fragmentToSplit);
392         fragmentToSplit.end = fragmentToSplit.start;
393         fragmentToSplit.width = 0;
394         return fragmentForNextLine;
395     }
396     // Simple binary search to find out what fits the current line.
397     // FIXME: add surrogate pair support.
398     unsigned left = fragmentToSplit.start;
399     unsigned right = fragmentToSplit.end - 1; // We can ignore the last character. It surely does not fit.
400     float width = 0;
401     while (left < right) {
402         unsigned middle = (left + right) / 2;
403         width = flowContents.textWidth(fragmentToSplit.start, middle + 1, 0);
404         if (availableWidth > width)
405             left = middle + 1;
406         else if (availableWidth < width)
407             right = middle;
408         else {
409             right = middle + 1;
410             break;
411         }
412     }
413
414     if (keepAtLeastOneCharacter && right == fragmentToSplit.start)
415         ++right;
416     FlowContents::TextFragment fragmentForNextLine(fragmentToSplit);
417     fragmentToSplit.end = right;
418     fragmentToSplit.width = fragmentToSplit.isEmpty() ? 0 : flowContents.textWidth(fragmentToSplit.start, fragmentToSplit.end, 0);
419
420     fragmentForNextLine.start = fragmentToSplit.end;
421     fragmentForNextLine.width -= fragmentToSplit.width;
422     return fragmentForNextLine;
423 }
424
425 static bool createLineRuns(LineState& lineState, Layout::RunVector& lineRuns, const FlowContents& flowContents)
426 {
427     const auto& style = flowContents.style();
428     bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow;
429     while (!flowContents.isEnd(lineState.position)) {
430         // Find the next text fragment. Start from the end of the previous fragment -current line end.
431         FlowContents::TextFragment fragment = flowContents.nextTextFragment(lineState.position, lineState.width());
432         if ((lineCanBeWrapped && !lineState.fits(fragment.width)) || fragment.type == FlowContents::TextFragment::LineBreak) {
433             // Overflow wrapping behaviour:
434             // 1. Newline character: wraps the line unless it's treated as whitespace.
435             // 2. Whitesapce collapse on: whitespace is skipped.
436             // 3. Whitespace collapse off: whitespace is wrapped.
437             // 4. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap)
438             // 5. Non-whitespace fragment when there's already another fragment on the line gets pushed to the next line.
439             bool isFirstFragment = !lineState.width();
440             if (fragment.type == FlowContents::TextFragment::LineBreak) {
441                 if (isFirstFragment)
442                     lineState.addUncommitted(fragment);
443                 else {
444                     // No need to add the new line fragment if there's already content on the line. We are about to close this line anyway.
445                     ++lineState.position;
446                 }
447             } else if (style.collapseWhitespace && fragment.type == FlowContents::TextFragment::Whitespace) {
448                 // Whitespace collapse is on: whitespace that doesn't fit is simply skipped.
449                 lineState.position = fragment.end;
450             } else if (fragment.type == FlowContents::TextFragment::Whitespace || ((isFirstFragment && style.breakWordOnOverflow) || !style.wrapLines)) { // !style.wrapLines: bug138102(preserve existing behavior)
451                 // Whitespace collapse is off or non-whitespace content. split the fragment; (modified)fragment -> this lineState, oveflowedFragment -> next line.
452                 // When this is the only (first) fragment, the first character stays on the line, even if it does not fit.
453                 lineState.oveflowedFragment = splitFragmentToFitLine(fragment, lineState.availableWidth - lineState.width(), isFirstFragment, flowContents);
454                 if (!fragment.isEmpty()) {
455                     // Whitespace fragments can get pushed entirely to the next line.
456                     lineState.addUncommitted(fragment);
457                 }
458             } else if (isFirstFragment) {
459                 // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though.
460                 lineState.addUncommitted(fragment);
461             } else {
462                 // Non-breakable non-whitespace fragment when there's already a fragment on the line. Push it to the next line.
463                 lineState.oveflowedFragment = fragment;
464             }
465             break;
466         }
467         // When the current fragment is collapsed whitespace, we need to create a run for what we've processed so far.
468         if (fragment.isCollapsed) {
469             // One trailing whitespace to preserve.
470             lineState.addUncommittedWhitespace(style.spaceWidth);
471             lineState.commitAndCreateRun(lineRuns);
472             // And skip the collapsed whitespace.
473             lineState.jumpTo(fragment.end, lineState.width() + fragment.width - style.spaceWidth);
474         } else
475             lineState.addUncommitted(fragment);
476     }
477     lineState.commitAndCreateRun(lineRuns);
478     return flowContents.isEnd(lineState.position) && lineState.oveflowedFragment.isEmpty();
479 }
480
481 static void closeLineEndingAndAdjustRuns(LineState& lineState, Layout::RunVector& lineRuns, unsigned& lineCount, const FlowContents& flowContents)
482 {
483     if (lineState.lineStartRunIndex == lineRuns.size())
484         return;
485
486     ASSERT(lineRuns.size());
487     removeTrailingWhitespace(lineState, lineRuns, flowContents);
488     // Adjust runs' position by taking line's alignment into account.
489     if (float lineLogicalLeft = computeLineLeft(flowContents.style().textAlign, lineState.availableWidth, lineState.committedWidth, lineState.logicalLeftOffset)) {
490         for (unsigned i = lineState.lineStartRunIndex; i < lineRuns.size(); ++i) {
491             lineRuns[i].logicalLeft += lineLogicalLeft;
492             lineRuns[i].logicalRight += lineLogicalLeft;
493         }
494     }
495     lineRuns.last().isEndOfLine = true;
496     lineState.committedWidth = 0;
497     lineState.committedLogicalRight = 0;
498     ++lineCount;
499 }
500
501 static void splitRunsAtRendererBoundary(Layout::RunVector& lineRuns, const FlowContents& flowContents)
502 {
503     // FIXME: We should probably split during run construction instead of as a separate pass.
504     if (lineRuns.isEmpty())
505         return;
506     if (flowContents.hasOneSegment())
507         return;
508
509     unsigned runIndex = 0;
510     do {
511         const Run& run = lineRuns.at(runIndex);
512         ASSERT(run.start != run.end);
513         auto& startSegment = flowContents.segmentForPosition(run.start);
514         if (run.end <= startSegment.end)
515             continue;
516         // This run overlaps multiple renderers. Split it up.
517         // 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.
518         float logicalRightOfLeftRun = run.logicalLeft + flowContents.textWidth(run.start, startSegment.end, run.logicalLeft);
519         lineRuns.insert(runIndex, Run(run.start, startSegment.end, run.logicalLeft, logicalRightOfLeftRun, false));
520         Run& rightSideRun = lineRuns.at(runIndex + 1);
521         rightSideRun.start = startSegment.end;
522         rightSideRun.logicalLeft = logicalRightOfLeftRun;
523     } while (++runIndex < lineRuns.size());
524 }
525
526 static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
527 {
528     LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
529     LayoutUnit lineHeight = lineHeightFromFlow(flow);
530     LineState lineState;
531     bool isEndOfContent = false;
532     FlowContents flowContents = FlowContents(flow);
533
534     do {
535         flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
536         lineState = initializeNewLine(lineState, flow, flowContents, runs.size());
537         isEndOfContent = createLineRuns(lineState, runs, flowContents);
538         closeLineEndingAndAdjustRuns(lineState, runs, lineCount, flowContents);
539     } while (!isEndOfContent);
540
541     splitRunsAtRendererBoundary(runs, flowContents);
542     ASSERT(!lineState.uncommittedWidth);
543 }
544
545 std::unique_ptr<Layout> create(RenderBlockFlow& flow)
546 {
547     unsigned lineCount = 0;
548     Layout::RunVector runs;
549
550     createTextRuns(runs, flow, lineCount);
551     for (auto& renderer : childrenOfType<RenderObject>(flow)) {
552         ASSERT(is<RenderText>(renderer));
553         renderer.clearNeedsLayout();
554     }
555     return Layout::create(runs, lineCount);
556 }
557
558 std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
559 {
560     void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
561     return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
562 }
563
564 Layout::Layout(const RunVector& runVector, unsigned lineCount)
565     : m_lineCount(lineCount)
566     , m_runCount(runVector.size())
567 {
568     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
569 }
570
571 }
572 }