Simple line layout: Make LineState fragment handling simpler.
[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 "SimpleLineLayoutFlowContentsIterator.h"
48 #include "SimpleLineLayoutFunctions.h"
49 #include "Text.h"
50 #include "TextPaintStyle.h"
51
52 namespace WebCore {
53 namespace SimpleLineLayout {
54
55 template <typename CharacterType>
56 static bool canUseForText(const CharacterType* text, unsigned length, const Font& font)
57 {
58     // FIXME: <textarea maxlength=0> generates empty text node.
59     if (!length)
60         return false;
61     for (unsigned i = 0; i < length; ++i) {
62         UChar character = text[i];
63         if (character == ' ')
64             continue;
65
66         // These would be easy to support.
67         if (character == noBreakSpace)
68             return false;
69         if (character == softHyphen)
70             return false;
71
72         UCharDirection direction = u_charDirection(character);
73         if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
74             || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
75             || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE
76             || direction == U_POP_DIRECTIONAL_FORMAT || direction == U_BOUNDARY_NEUTRAL)
77             return false;
78
79         if (!font.glyphForCharacter(character))
80             return false;
81     }
82     return true;
83 }
84
85 static bool canUseForText(const RenderText& textRenderer, const Font& font)
86 {
87     if (textRenderer.is8Bit())
88         return canUseForText(textRenderer.characters8(), textRenderer.textLength(), font);
89     return canUseForText(textRenderer.characters16(), textRenderer.textLength(), font);
90 }
91
92 bool canUseFor(const RenderBlockFlow& flow)
93 {
94     if (!flow.frame().settings().simpleLineLayoutEnabled())
95         return false;
96     if (!flow.firstChild())
97         return false;
98     // This currently covers <blockflow>#text</blockflow> and mutiple (sibling) RenderText cases.
99     // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
100     for (const auto& renderer : childrenOfType<RenderObject>(flow)) {
101         if (!is<RenderText>(renderer))
102             return false;
103     }
104     if (!flow.isHorizontalWritingMode())
105         return false;
106     if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
107         return false;
108     // Printing does pagination without a flow thread.
109     if (flow.document().paginated())
110         return false;
111     if (flow.hasOutline())
112         return false;
113     if (flow.isRubyText() || flow.isRubyBase())
114         return false;
115     if (flow.parent()->isDeprecatedFlexibleBox())
116         return false;
117     // FIXME: Implementation of wrap=hard looks into lineboxes.
118     if (flow.parent()->isTextArea() && flow.parent()->element()->fastHasAttribute(HTMLNames::wrapAttr))
119         return false;
120     // FIXME: Placeholders do something strange.
121     if (is<RenderTextControl>(*flow.parent()) && downcast<RenderTextControl>(*flow.parent()).textFormControlElement().placeholderElement())
122         return false;
123     const RenderStyle& style = flow.style();
124     if (style.textDecorationsInEffect() != TextDecorationNone)
125         return false;
126     if (style.textAlign() == JUSTIFY)
127         return false;
128     // Non-visible overflow should be pretty easy to support.
129     if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
130         return false;
131     if (!style.textIndent().isZero())
132         return false;
133     if (!style.wordSpacing().isZero() || style.letterSpacing())
134         return false;
135     if (!style.isLeftToRightDirection())
136         return false;
137     if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
138         return false;
139     if (style.writingMode() != TopToBottomWritingMode)
140         return false;
141     if (style.lineBreak() != LineBreakAuto)
142         return false;
143     if (style.wordBreak() != NormalWordBreak)
144         return false;
145     if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder)
146         return false;
147     if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone)
148         return false;
149     if (style.hyphens() == HyphensAuto)
150         return false;
151     if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
152         return false;
153     if (style.textShadow())
154         return false;
155     if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style().textOverflow()))
156         return false;
157     if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER))
158         return false;
159     if (style.hasTextCombine())
160         return false;
161     if (style.backgroundClip() == TextFillBox)
162         return false;
163     if (style.borderFit() == BorderFitLines)
164         return false;
165     if (style.lineBreak() != LineBreakAuto)
166         return false;
167
168     // 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.
169     if (flow.containsFloats()) {
170         float minimumWidthNeeded = std::numeric_limits<float>::max();
171         for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
172             minimumWidthNeeded = std::min(minimumWidthNeeded, textRenderer.minLogicalWidth());
173
174             for (auto& floatingObject : *flow.floatingObjectSet()) {
175                 ASSERT(floatingObject);
176 #if ENABLE(CSS_SHAPES)
177                 // if a float has a shape, we cannot tell if content will need to be shifted until after we lay it out,
178                 // since the amount of space is not uniform for the height of the float.
179                 if (floatingObject->renderer().shapeOutsideInfo())
180                     return false;
181 #endif
182                 float availableWidth = flow.availableLogicalWidthForLine(floatingObject->y(), false);
183                 if (availableWidth < minimumWidthNeeded)
184                     return false;
185             }
186         }
187     }
188     if (style.fontCascade().primaryFont().isSVGFont())
189         return false;
190     // We assume that all lines have metrics based purely on the primary font.
191     auto& primaryFont = style.fontCascade().primaryFont();
192     if (primaryFont.isLoading())
193         return false;
194     for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
195         if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isTextFragment()
196             || textRenderer.isSVGInlineText())
197             return false;
198         if (style.fontCascade().codePath(TextRun(textRenderer.text())) != FontCascade::Simple)
199             return false;
200         if (!canUseForText(textRenderer, primaryFont))
201             return false;
202     }
203     return true;
204 }
205
206 static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset)
207 {
208     float remainingWidth = availableWidth - committedWidth;
209     float left = logicalLeftOffset;
210     switch (textAlign) {
211     case LEFT:
212     case WEBKIT_LEFT:
213     case TASTART:
214         return left;
215     case RIGHT:
216     case WEBKIT_RIGHT:
217     case TAEND:
218         return left + std::max<float>(remainingWidth, 0);
219     case CENTER:
220     case WEBKIT_CENTER:
221         return left + std::max<float>(remainingWidth / 2, 0);
222     case JUSTIFY:
223         ASSERT_NOT_REACHED();
224         break;
225     }
226     ASSERT_NOT_REACHED();
227     return 0;
228 }
229
230 class LineState {
231 public:
232     void setAvailableWidth(float width) { m_availableWidth = width; }
233     void setLogicalLeftOffset(float offset) { m_logicalLeftOffset = offset; }
234     void setOverflowedFragment(const FlowContentsIterator::TextFragment& fragment) { m_overflowedFragment = fragment; }
235
236     float availableWidth() const { return m_availableWidth; }
237     float logicalLeftOffset() const { return m_logicalLeftOffset; }
238     FlowContentsIterator::TextFragment overflowedFragment() const { return m_overflowedFragment; }
239     bool hasTrailingWhitespace() const { return m_trailingWhitespaceLength; }
240     bool isWhitespaceOnly() const { return m_trailingWhitespaceWidth && m_runsWidth == m_trailingWhitespaceWidth; }
241     bool fits(float extra) const { return m_availableWidth >= m_runsWidth + extra; }
242     bool firstCharacterFits() const { return m_firstCharacterFits; }
243     float width() const { return m_runsWidth; }
244
245     void appendFragment(const FlowContentsIterator::TextFragment& fragment, Layout::RunVector& runs)
246     {
247         // Adjust end position while collapsing.
248         unsigned endPosition = fragment.isCollapsed ? fragment.start + 1 : fragment.end;
249
250         if (m_createNewRun)
251             runs.append(Run(fragment.start, endPosition, m_runsWidth, m_runsWidth + fragment.width, false));
252         else {
253             ASSERT(runs.size());
254             Run& lastRun = runs.last();
255             lastRun.end = endPosition;
256             lastRun.logicalRight = m_runsWidth + fragment.width;
257         }
258         m_createNewRun = fragment.isCollapsed;
259         m_runsWidth += fragment.width;
260
261         if (fragment.type == FlowContentsIterator::TextFragment::Whitespace) {
262             m_trailingWhitespaceLength += endPosition - fragment.start;
263             m_trailingWhitespaceWidth += fragment.width;
264         } else {
265             m_trailingWhitespaceLength = 0;
266             m_trailingWhitespaceWidth = 0;
267         }
268
269         if (!m_firstCharacterFits)
270             m_firstCharacterFits = fragment.start + 1 > fragment.end || m_runsWidth <= m_availableWidth;
271     }
272
273     void removeTrailingWhitespace(Layout::RunVector& runs)
274     {
275         ASSERT(runs.size());
276         Run& lastRun = runs.last();
277         lastRun.logicalRight -= m_trailingWhitespaceWidth;
278         lastRun.end -= m_trailingWhitespaceLength;
279         if (lastRun.start == lastRun.end)
280             runs.removeLast();
281
282         m_runsWidth -= m_trailingWhitespaceWidth;
283         m_trailingWhitespaceWidth = 0;
284         m_trailingWhitespaceLength = 0;
285     }
286
287 private:
288     float m_availableWidth { 0 };
289     float m_logicalLeftOffset { 0 };
290     FlowContentsIterator::TextFragment m_overflowedFragment;
291     float m_runsWidth { 0 };
292     bool m_createNewRun { true };
293     float m_trailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
294     unsigned m_trailingWhitespaceLength { 0 };
295     // Having one character on the line does not necessarily mean it actually fits.
296     // First character of the first fragment might be forced on to the current line even if it does not fit.
297     bool m_firstCharacterFits { false };
298 };
299
300 static bool preWrap(const FlowContentsIterator::Style& style)
301 {
302     return style.wrapLines && !style.collapseWhitespace;
303 }
304     
305 static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& runs, const FlowContentsIterator& flowContentsIterator)
306 {
307     if (!lineState.hasTrailingWhitespace())
308         return;
309     
310     // 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.
311     const auto& style = flowContentsIterator.style();
312     bool collapseWhitespace = style.collapseWhitespace | preWrap(style);
313     if (!collapseWhitespace)
314         return;
315
316     if (preWrap(style) && lineState.isWhitespaceOnly())
317         return;
318
319     lineState.removeTrailingWhitespace(runs);
320 }
321
322 static void updateLineConstrains(const RenderBlockFlow& flow, LineState& line)
323 {
324     LayoutUnit height = flow.logicalHeight();
325     LayoutUnit logicalHeight = flow.minLineHeightForReplacedRenderer(false, 0);
326     float logicalRightOffset = flow.logicalRightOffsetForLine(height, false, logicalHeight);
327     line.setLogicalLeftOffset(flow.logicalLeftOffsetForLine(height, false, logicalHeight));
328     line.setAvailableWidth(std::max<float>(0, logicalRightOffset - line.logicalLeftOffset()));
329 }
330
331 static FlowContentsIterator::TextFragment splitFragmentToFitLine(FlowContentsIterator::TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const FlowContentsIterator& flowContentsIterator)
332 {
333     // Fast path for single char fragments.
334     if (fragmentToSplit.start + 1 == fragmentToSplit.end) {
335         if (keepAtLeastOneCharacter)
336             return FlowContentsIterator::TextFragment();
337
338         FlowContentsIterator::TextFragment fragmentForNextLine(fragmentToSplit);
339         fragmentToSplit.end = fragmentToSplit.start;
340         fragmentToSplit.width = 0;
341         return fragmentForNextLine;
342     }
343     // Simple binary search to find out what fits the current line.
344     // FIXME: add surrogate pair support.
345     unsigned left = fragmentToSplit.start;
346     unsigned right = fragmentToSplit.end - 1; // We can ignore the last character. It surely does not fit.
347     float width = 0;
348     while (left < right) {
349         unsigned middle = (left + right) / 2;
350         width = flowContentsIterator.textWidth(fragmentToSplit.start, middle + 1, 0);
351         if (availableWidth > width)
352             left = middle + 1;
353         else if (availableWidth < width)
354             right = middle;
355         else {
356             right = middle + 1;
357             break;
358         }
359     }
360
361     if (keepAtLeastOneCharacter && right == fragmentToSplit.start)
362         ++right;
363     FlowContentsIterator::TextFragment fragmentForNextLine(fragmentToSplit);
364     fragmentToSplit.end = right;
365     fragmentToSplit.width = fragmentToSplit.isEmpty() ? 0 : flowContentsIterator.textWidth(fragmentToSplit.start, fragmentToSplit.end, 0);
366
367     fragmentForNextLine.start = fragmentToSplit.end;
368     fragmentForNextLine.width -= fragmentToSplit.width;
369     return fragmentForNextLine;
370 }
371
372 static FlowContentsIterator::TextFragment firstFragment(FlowContentsIterator& flowContentsIterator, const LineState& previousLine)
373 {
374     // Handle overflowed fragment from previous line.
375     FlowContentsIterator::TextFragment firstFragment = previousLine.overflowedFragment();
376     const auto& style = flowContentsIterator.style();
377
378     if (firstFragment.isEmpty())
379         firstFragment = flowContentsIterator.nextTextFragment();
380     else {
381         // Special overflow pre-wrap whitespace handling: ignore the overflowed whitespace if we managed to fit at least one character on the previous line.
382         // 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.
383         if (firstFragment.type == FlowContentsIterator::TextFragment::Whitespace && preWrap(style) && previousLine.firstCharacterFits()) {
384             firstFragment = flowContentsIterator.nextTextFragment();
385             // If skipping the whitespace puts us on a hard newline, skip the newline too as we already wrapped the line.
386             if (firstFragment.type == FlowContentsIterator::TextFragment::LineBreak)
387                 firstFragment = flowContentsIterator.nextTextFragment();
388         }
389     }
390
391     // Check if we need to skip the leading whitespace.
392     if (style.collapseWhitespace && firstFragment.type == FlowContentsIterator::TextFragment::Whitespace)
393         firstFragment = flowContentsIterator.nextTextFragment();
394     return firstFragment;
395 }
396
397 static bool createLineRuns(LineState& line, const LineState& previousLine, Layout::RunVector& runs, FlowContentsIterator& flowContentsIterator)
398 {
399     const auto& style = flowContentsIterator.style();
400     bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow;
401     auto fragment = firstFragment(flowContentsIterator, previousLine);
402     while (!fragment.isEmpty()) {
403         // Hard linebreak.
404         if (fragment.type == FlowContentsIterator::TextFragment::LineBreak) {
405             // Add the new line fragment only if there's nothing on the line. (otherwise the extra new line character would show up at the end of the content.)
406             if (!line.width())
407                 line.appendFragment(fragment, runs);
408             break;
409         }
410         if (lineCanBeWrapped && !line.fits(fragment.width)) {
411             // Overflow wrapping behaviour:
412             // 1. Whitesapce collapse on: whitespace is skipped. Jump to next line.
413             // 2. Whitespace collapse off: whitespace is wrapped.
414             // 3. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap)
415             // 4. Non-whitespace fragment when there's already another fragment on the line gets pushed to the next line.
416             bool emptyLine = !line.width();
417             // Whitespace fragment.
418             if (fragment.type == FlowContentsIterator::TextFragment::Whitespace) {
419                 if (!style.collapseWhitespace) {
420                     // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
421                     line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, flowContentsIterator));
422                     line.appendFragment(fragment, runs);
423                 }
424                 // When whitespace collapse is on, whitespace that doesn't fit is simply skipped.
425                 break;
426             }
427             // Non-whitespace fragment. (!style.wrapLines: bug138102(preserve existing behavior)
428             if ((emptyLine && style.breakWordOnOverflow) || !style.wrapLines) {
429                 // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
430                 line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, flowContentsIterator));
431                 line.appendFragment(fragment, runs);
432                 break;
433             }
434             // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though.
435             if (emptyLine) {
436                 line.appendFragment(fragment, runs);
437                 break;
438             }
439             // Non-breakable non-whitespace fragment when there's already content on the line. Push it to the next line.
440             line.setOverflowedFragment(fragment);
441             break;
442         }
443         line.appendFragment(fragment, runs);
444         // Find the next text fragment.
445         fragment = flowContentsIterator.nextTextFragment(line.width());
446     }
447     return fragment.isEmpty() && line.overflowedFragment().isEmpty();
448 }
449
450 static void closeLineEndingAndAdjustRuns(LineState& line, Layout::RunVector& runs, unsigned previousRunCount, unsigned& lineCount, const FlowContentsIterator& flowContentsIterator)
451 {
452     if (previousRunCount == runs.size())
453         return;
454     ASSERT(runs.size());
455     removeTrailingWhitespace(line, runs, flowContentsIterator);
456     if (!runs.size())
457         return;
458     // Adjust runs' position by taking line's alignment into account.
459     if (float lineLogicalLeft = computeLineLeft(flowContentsIterator.style().textAlign, line.availableWidth(), line.width(), line.logicalLeftOffset())) {
460         for (unsigned i = previousRunCount; i < runs.size(); ++i) {
461             runs[i].logicalLeft += lineLogicalLeft;
462             runs[i].logicalRight += lineLogicalLeft;
463         }
464     }
465     runs.last().isEndOfLine = true;
466     ++lineCount;
467 }
468
469 static void splitRunsAtRendererBoundary(Layout::RunVector& lineRuns, const FlowContentsIterator& flowContentsIterator)
470 {
471     // FIXME: We should probably split during run construction instead of as a separate pass.
472     if (lineRuns.isEmpty())
473         return;
474     unsigned runIndex = 0;
475     do {
476         const Run& run = lineRuns.at(runIndex);
477         ASSERT(run.start != run.end);
478         auto& startSegment = flowContentsIterator.segmentForPosition(run.start);
479         if (run.end <= startSegment.end)
480             continue;
481         // This run overlaps multiple renderers. Split it up.
482         // 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.
483         float logicalRightOfLeftRun = run.logicalLeft + flowContentsIterator.textWidth(run.start, startSegment.end, run.logicalLeft);
484         lineRuns.insert(runIndex, Run(run.start, startSegment.end, run.logicalLeft, logicalRightOfLeftRun, false));
485         Run& rightSideRun = lineRuns.at(runIndex + 1);
486         rightSideRun.start = startSegment.end;
487         rightSideRun.logicalLeft = logicalRightOfLeftRun;
488     } while (++runIndex < lineRuns.size());
489 }
490
491 static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
492 {
493     LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
494     LayoutUnit lineHeight = lineHeightFromFlow(flow);
495     LineState line;
496     bool isEndOfContent = false;
497     FlowContentsIterator flowContentsIterator = FlowContentsIterator(flow);
498
499     do {
500         flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
501         LineState previousLine = line;
502         unsigned previousRunCount = runs.size();
503         line = LineState();
504         updateLineConstrains(flow, line);
505         isEndOfContent = createLineRuns(line, previousLine, runs, flowContentsIterator);
506         closeLineEndingAndAdjustRuns(line, runs, previousRunCount, lineCount, flowContentsIterator);
507     } while (!isEndOfContent);
508
509     if (flow.firstChild() != flow.lastChild())
510         splitRunsAtRendererBoundary(runs, flowContentsIterator);
511 }
512
513 std::unique_ptr<Layout> create(RenderBlockFlow& flow)
514 {
515     unsigned lineCount = 0;
516     Layout::RunVector runs;
517
518     createTextRuns(runs, flow, lineCount);
519     for (auto& renderer : childrenOfType<RenderObject>(flow)) {
520         ASSERT(is<RenderText>(renderer));
521         renderer.clearNeedsLayout();
522     }
523     return Layout::create(runs, lineCount);
524 }
525
526 std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
527 {
528     void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
529     return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
530 }
531
532 Layout::Layout(const RunVector& runVector, unsigned lineCount)
533     : m_lineCount(lineCount)
534     , m_runCount(runVector.size())
535 {
536     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
537 }
538
539 }
540 }