Simple line layout: Use only FlowContents::nextTextFragment() to read fragments from...
[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         if (!m_firstCharacterFits)
242             m_firstCharacterFits = uncommittedStart + 1 > uncommittedEnd || committedWidth <= availableWidth;
243
244         uncommittedStart = uncommittedEnd;
245         uncommittedWidth = 0;
246         uncomittedTrailingWhitespaceWidth = 0;
247         uncomittedTrailingWhitespaceLength = 0;
248     }
249
250     void addUncommitted(const FlowContents::TextFragment& fragment)
251     {
252         unsigned uncomittedFragmentLength = fragment.end - uncommittedEnd;
253         uncommittedWidth += fragment.width;
254         uncommittedEnd = fragment.end;
255         position = uncommittedEnd;
256         uncomittedTrailingWhitespaceWidth = fragment.type == FlowContents::TextFragment::Whitespace ? fragment.width : 0;
257         uncomittedTrailingWhitespaceLength = fragment.type == FlowContents::TextFragment::Whitespace ? uncomittedFragmentLength  : 0;
258     }
259
260     void addUncommittedWhitespace(float whitespaceWidth)
261     {
262         addUncommitted(FlowContents::TextFragment(uncommittedEnd, uncommittedEnd + 1, whitespaceWidth, true));
263     }
264
265     void jumpTo(unsigned newPositon, float logicalRight)
266     {
267         position = newPositon;
268
269         uncommittedStart = newPositon;
270         uncommittedEnd = newPositon;
271         uncommittedWidth = 0;
272         committedLogicalRight = logicalRight;
273     }
274
275     bool hasWhitespaceOnly() const
276     {
277         return committedTrailingWhitespaceWidth && committedWidth == committedTrailingWhitespaceWidth;
278     }
279
280     float width() const
281     {
282         return committedWidth + uncommittedWidth;
283     }
284
285     bool fits(float extra) const
286     {
287         return availableWidth >= width() + extra;
288     }
289
290     void removeTrailingWhitespace()
291     {
292         committedWidth -= committedTrailingWhitespaceWidth;
293         committedLogicalRight -= committedTrailingWhitespaceWidth;
294         committedTrailingWhitespaceWidth = 0;
295         committedTrailingWhitespaceLength = 0;
296     }
297
298     float availableWidth { 0 };
299     float logicalLeftOffset { 0 };
300     unsigned lineStartRunIndex { 0 }; // The run that the line starts with.
301     unsigned position { 0 };
302
303     unsigned uncommittedStart { 0 };
304     unsigned uncommittedEnd { 0 };
305     float uncommittedWidth { 0 };
306     float committedWidth { 0 };
307
308     float committedLogicalRight { 0 }; // Last committed X (coordinate) position.
309     float committedTrailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
310     unsigned committedTrailingWhitespaceLength { 0 };
311     // Having one character on the line does not necessarily mean it actually fits.
312     // First character of the first fragment might be forced on to the current line even if it does not fit.
313     bool m_firstCharacterFits { false };
314
315     FlowContents::TextFragment oveflowedFragment;
316
317 private:
318     float uncomittedTrailingWhitespaceWidth { 0 };
319     unsigned uncomittedTrailingWhitespaceLength { 0 };
320 };
321
322 static bool preWrap(const FlowContents::Style& style)
323 {
324     return style.wrapLines && !style.collapseWhitespace;
325 }
326     
327 static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& lineRuns, const FlowContents& flowContents)
328 {
329     if (!lineState.committedTrailingWhitespaceLength)
330         return;
331     
332     // Remove collapsed whitespace, or non-collapsed pre-wrap whitespace, unless it's the only content on the line -so removing the whitesapce would produce an empty line.
333     const auto& style = flowContents.style();
334     bool collapseWhitespace = style.collapseWhitespace | preWrap(style);
335     if (!collapseWhitespace)
336         return;
337
338     if (preWrap(style) && lineState.hasWhitespaceOnly())
339         return;
340
341     ASSERT(lineRuns.size());
342     Run& lastRun = lineRuns.last();
343     lastRun.logicalRight -= lineState.committedTrailingWhitespaceWidth;
344     lastRun.end -= lineState.committedTrailingWhitespaceLength;
345     if (lastRun.start == lastRun.end)
346         lineRuns.removeLast();
347     lineState.removeTrailingWhitespace();
348 }
349
350 static void updateLineConstrains(const RenderBlockFlow& flow, float& availableWidth, float& logicalLeftOffset)
351 {
352     LayoutUnit height = flow.logicalHeight();
353     LayoutUnit logicalHeight = flow.minLineHeightForReplacedRenderer(false, 0);
354     float logicalRightOffset = flow.logicalRightOffsetForLine(height, false, logicalHeight);
355     logicalLeftOffset = flow.logicalLeftOffsetForLine(height, false, logicalHeight);
356     availableWidth = std::max<float>(0, logicalRightOffset - logicalLeftOffset);
357 }
358
359 static LineState initializeNewLine(const LineState& previousLine, const RenderBlockFlow& flow, const FlowContents& flowContents, unsigned lineStartRunIndex)
360 {
361     LineState lineState;
362     lineState.jumpTo(previousLine.position, 0);
363     lineState.lineStartRunIndex = lineStartRunIndex;
364     updateLineConstrains(flow, lineState.availableWidth, lineState.logicalLeftOffset);
365
366     // Handle overflowed fragment from previous line.
367     auto overflowedFragment = previousLine.oveflowedFragment;
368     unsigned linePositon = previousLine.position;
369     // Special overflow pre-wrap whitespace handling: ignore the overflowed whitespace if we managed to fit at least one character on the previous line.
370     // When the line is too short to fit one character (thought it still stays on the line) we continue with the overflow whitespace content on this line.
371     const auto& style = flowContents.style();
372     if (overflowedFragment.type == FlowContents::TextFragment::Whitespace && preWrap(style) && previousLine.m_firstCharacterFits) {
373         linePositon = overflowedFragment.end;
374         overflowedFragment = FlowContents::TextFragment();
375         // If skipping the whitespace puts us on a hard newline, skip the newline too as we already wrapped the line.
376         if (flowContents.isLineBreak(linePositon))
377             ++linePositon;
378     }
379
380     if (overflowedFragment.isEmpty()) {
381         if (style.collapseWhitespace) {
382             auto firstFragment = flowContents.nextTextFragment(linePositon, 0);
383             if (firstFragment.type == FlowContents::TextFragment::Whitespace)
384                 linePositon = firstFragment.end;
385         }
386         lineState.jumpTo(linePositon, 0);
387         return lineState;
388     }
389
390     if (lineState.fits(overflowedFragment.width)) {
391         lineState.addUncommitted(overflowedFragment);
392         return lineState;
393     }
394
395     // Start over with this fragment.
396     lineState.jumpTo(overflowedFragment.start, 0);
397     return lineState;
398 }
399
400 static FlowContents::TextFragment splitFragmentToFitLine(FlowContents::TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const FlowContents& flowContents)
401 {
402     // Fast path for single char fragments.
403     if (fragmentToSplit.start + 1 == fragmentToSplit.end) {
404         if (keepAtLeastOneCharacter)
405             return FlowContents::TextFragment();
406
407         FlowContents::TextFragment fragmentForNextLine(fragmentToSplit);
408         fragmentToSplit.end = fragmentToSplit.start;
409         fragmentToSplit.width = 0;
410         return fragmentForNextLine;
411     }
412     // Simple binary search to find out what fits the current line.
413     // FIXME: add surrogate pair support.
414     unsigned left = fragmentToSplit.start;
415     unsigned right = fragmentToSplit.end - 1; // We can ignore the last character. It surely does not fit.
416     float width = 0;
417     while (left < right) {
418         unsigned middle = (left + right) / 2;
419         width = flowContents.textWidth(fragmentToSplit.start, middle + 1, 0);
420         if (availableWidth > width)
421             left = middle + 1;
422         else if (availableWidth < width)
423             right = middle;
424         else {
425             right = middle + 1;
426             break;
427         }
428     }
429
430     if (keepAtLeastOneCharacter && right == fragmentToSplit.start)
431         ++right;
432     FlowContents::TextFragment fragmentForNextLine(fragmentToSplit);
433     fragmentToSplit.end = right;
434     fragmentToSplit.width = fragmentToSplit.isEmpty() ? 0 : flowContents.textWidth(fragmentToSplit.start, fragmentToSplit.end, 0);
435
436     fragmentForNextLine.start = fragmentToSplit.end;
437     fragmentForNextLine.width -= fragmentToSplit.width;
438     return fragmentForNextLine;
439 }
440
441 static bool createLineRuns(LineState& lineState, Layout::RunVector& lineRuns, const FlowContents& flowContents)
442 {
443     const auto& style = flowContents.style();
444     bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow;
445     while (!flowContents.isEnd(lineState.position)) {
446         // Find the next text fragment. Start from the end of the previous fragment -current line end.
447         FlowContents::TextFragment fragment = flowContents.nextTextFragment(lineState.position, lineState.width());
448         if ((lineCanBeWrapped && !lineState.fits(fragment.width)) || fragment.type == FlowContents::TextFragment::LineBreak) {
449             // Overflow wrapping behaviour:
450             // 1. Newline character: wraps the line unless it's treated as whitespace.
451             // 2. Whitesapce collapse on: whitespace is skipped.
452             // 3. Whitespace collapse off: whitespace is wrapped.
453             // 4. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap)
454             // 5. Non-whitespace fragment when there's already another fragment on the line gets pushed to the next line.
455             bool isFirstFragment = !lineState.width();
456             if (fragment.type == FlowContents::TextFragment::LineBreak) {
457                 if (isFirstFragment)
458                     lineState.addUncommitted(fragment);
459                 else {
460                     // No need to add the new line fragment if there's already content on the line. We are about to close this line anyway.
461                     ++lineState.position;
462                 }
463             } else if (style.collapseWhitespace && fragment.type == FlowContents::TextFragment::Whitespace) {
464                 // Whitespace collapse is on: whitespace that doesn't fit is simply skipped.
465                 lineState.position = fragment.end;
466             } else if (fragment.type == FlowContents::TextFragment::Whitespace || ((isFirstFragment && style.breakWordOnOverflow) || !style.wrapLines)) { // !style.wrapLines: bug138102(preserve existing behavior)
467                 // Whitespace collapse is off or non-whitespace content. split the fragment; (modified)fragment -> this lineState, oveflowedFragment -> next line.
468                 // When this is the only (first) fragment, the first character stays on the line, even if it does not fit.
469                 lineState.oveflowedFragment = splitFragmentToFitLine(fragment, lineState.availableWidth - lineState.width(), isFirstFragment, flowContents);
470                 if (!fragment.isEmpty()) {
471                     // Whitespace fragments can get pushed entirely to the next line.
472                     lineState.addUncommitted(fragment);
473                 }
474             } else if (isFirstFragment) {
475                 // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though.
476                 lineState.addUncommitted(fragment);
477             } else {
478                 // Non-breakable non-whitespace fragment when there's already a fragment on the line. Push it to the next line.
479                 lineState.oveflowedFragment = fragment;
480             }
481             break;
482         }
483         // When the current fragment is collapsed whitespace, we need to create a run for what we've processed so far.
484         if (fragment.isCollapsed) {
485             // One trailing whitespace to preserve.
486             lineState.addUncommittedWhitespace(style.spaceWidth);
487             lineState.commitAndCreateRun(lineRuns);
488             // And skip the collapsed whitespace.
489             lineState.jumpTo(fragment.end, lineState.width() + fragment.width - style.spaceWidth);
490         } else
491             lineState.addUncommitted(fragment);
492     }
493     lineState.commitAndCreateRun(lineRuns);
494     return flowContents.isEnd(lineState.position) && lineState.oveflowedFragment.isEmpty();
495 }
496
497 static void closeLineEndingAndAdjustRuns(LineState& lineState, Layout::RunVector& lineRuns, unsigned& lineCount, const FlowContents& flowContents)
498 {
499     if (lineState.lineStartRunIndex == lineRuns.size())
500         return;
501
502     ASSERT(lineRuns.size());
503     removeTrailingWhitespace(lineState, lineRuns, flowContents);
504     // Adjust runs' position by taking line's alignment into account.
505     if (float lineLogicalLeft = computeLineLeft(flowContents.style().textAlign, lineState.availableWidth, lineState.committedWidth, lineState.logicalLeftOffset)) {
506         for (unsigned i = lineState.lineStartRunIndex; i < lineRuns.size(); ++i) {
507             lineRuns[i].logicalLeft += lineLogicalLeft;
508             lineRuns[i].logicalRight += lineLogicalLeft;
509         }
510     }
511     lineRuns.last().isEndOfLine = true;
512     lineState.committedWidth = 0;
513     lineState.committedLogicalRight = 0;
514     ++lineCount;
515 }
516
517 static void splitRunsAtRendererBoundary(Layout::RunVector& lineRuns, const FlowContents& flowContents)
518 {
519     // FIXME: We should probably split during run construction instead of as a separate pass.
520     if (lineRuns.isEmpty())
521         return;
522     if (flowContents.hasOneSegment())
523         return;
524
525     unsigned runIndex = 0;
526     do {
527         const Run& run = lineRuns.at(runIndex);
528         ASSERT(run.start != run.end);
529         auto& startSegment = flowContents.segmentForPosition(run.start);
530         if (run.end <= startSegment.end)
531             continue;
532         // This run overlaps multiple renderers. Split it up.
533         // 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.
534         float logicalRightOfLeftRun = run.logicalLeft + flowContents.textWidth(run.start, startSegment.end, run.logicalLeft);
535         lineRuns.insert(runIndex, Run(run.start, startSegment.end, run.logicalLeft, logicalRightOfLeftRun, false));
536         Run& rightSideRun = lineRuns.at(runIndex + 1);
537         rightSideRun.start = startSegment.end;
538         rightSideRun.logicalLeft = logicalRightOfLeftRun;
539     } while (++runIndex < lineRuns.size());
540 }
541
542 static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
543 {
544     LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
545     LayoutUnit lineHeight = lineHeightFromFlow(flow);
546     LineState lineState;
547     bool isEndOfContent = false;
548     FlowContents flowContents = FlowContents(flow);
549
550     do {
551         flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
552         lineState = initializeNewLine(lineState, flow, flowContents, runs.size());
553         isEndOfContent = createLineRuns(lineState, runs, flowContents);
554         closeLineEndingAndAdjustRuns(lineState, runs, lineCount, flowContents);
555     } while (!isEndOfContent);
556
557     splitRunsAtRendererBoundary(runs, flowContents);
558     ASSERT(!lineState.uncommittedWidth);
559 }
560
561 std::unique_ptr<Layout> create(RenderBlockFlow& flow)
562 {
563     unsigned lineCount = 0;
564     Layout::RunVector runs;
565
566     createTextRuns(runs, flow, lineCount);
567     for (auto& renderer : childrenOfType<RenderObject>(flow)) {
568         ASSERT(is<RenderText>(renderer));
569         renderer.clearNeedsLayout();
570     }
571     return Layout::create(runs, lineCount);
572 }
573
574 std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
575 {
576     void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
577     return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
578 }
579
580 Layout::Layout(const RunVector& runVector, unsigned lineCount)
581     : m_lineCount(lineCount)
582     , m_runCount(runVector.size())
583 {
584     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
585 }
586
587 }
588 }