[LFC][IFC] Skip non-inflow boxes while splitting the inline runs.
[WebKit-https.git] / Source / WebCore / layout / inlineformatting / InlineFormattingContext.cpp
1 /*
2  * Copyright (C) 2018 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 "InlineFormattingContext.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "FloatingState.h"
32 #include "InlineFormattingState.h"
33 #include "InlineLineBreaker.h"
34 #include "InlineRunProvider.h"
35 #include "LayoutBox.h"
36 #include "LayoutContainer.h"
37 #include "LayoutFormattingState.h"
38 #include "LayoutInlineBox.h"
39 #include "LayoutInlineContainer.h"
40 #include "Logging.h"
41 #include <wtf/IsoMallocInlines.h>
42 #include <wtf/text/TextStream.h>
43
44 namespace WebCore {
45 namespace Layout {
46
47 WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingContext);
48
49 InlineFormattingContext::InlineFormattingContext(const Box& formattingContextRoot, FormattingState& formattingState)
50     : FormattingContext(formattingContextRoot, formattingState)
51 {
52 }
53
54 void InlineFormattingContext::layout() const
55 {
56     if (!is<Container>(root()))
57         return;
58
59     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> formatting root(" << &root() << ")");
60
61     InlineRunProvider inlineRunProvider(inlineFormattingState());
62     collectInlineContent(inlineRunProvider);
63     // Compute width/height for non-text content.
64     for (auto& inlineRun : inlineRunProvider.runs()) {
65         if (inlineRun.isText())
66             continue;
67
68         auto& layoutBox = inlineRun.inlineItem().layoutBox();
69         if (layoutBox.establishesFormattingContext()) {
70             layoutFormattingContextRoot(layoutBox);
71             continue;
72         }
73         computeWidthAndHeightForReplacedInlineBox(layoutBox);
74     }
75
76     layoutInlineContent(inlineRunProvider);
77     LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
78 }
79
80 static bool isTrimmableContent(const InlineLineBreaker::Run& run)
81 {
82     return run.content.isWhitespace() && run.content.style().collapseWhiteSpace();
83 }
84
85 void InlineFormattingContext::initializeNewLine(Line& line) const
86 {
87     auto& formattingRoot = downcast<Container>(root());
88     auto& formattingRootDisplayBox = layoutState().displayBoxForLayoutBox(formattingRoot);
89
90     auto lineLogicalLeft = formattingRootDisplayBox.contentBoxLeft();
91     auto lineLogicalTop = line.isFirstLine() ? formattingRootDisplayBox.contentBoxTop() : line.logicalBottom();
92     auto availableWidth = formattingRootDisplayBox.contentBoxWidth();
93
94     // Check for intruding floats and adjust logical left/available width for this line accordingly.
95     auto& floatingState = formattingState().floatingState();
96     if (!floatingState.isEmpty()) {
97         auto floatConstraints = floatingState.constraints(lineLogicalTop, formattingRoot);
98         // Check if these constraints actually put limitation on the line.
99         if (floatConstraints.left && *floatConstraints.left <= formattingRootDisplayBox.contentBoxLeft())
100             floatConstraints.left = { };
101
102         if (floatConstraints.right && *floatConstraints.right >= formattingRootDisplayBox.contentBoxRight())
103             floatConstraints.right = { };
104
105         if (floatConstraints.left && floatConstraints.right) {
106             ASSERT(*floatConstraints.left < *floatConstraints.right);
107             availableWidth = *floatConstraints.right - *floatConstraints.left;
108             lineLogicalLeft = *floatConstraints.left;
109         } else if (floatConstraints.left) {
110             ASSERT(*floatConstraints.left > lineLogicalLeft);
111             availableWidth -= (*floatConstraints.left - lineLogicalLeft);
112             lineLogicalLeft = *floatConstraints.left;
113         } else if (floatConstraints.right) {
114             ASSERT(*floatConstraints.right > lineLogicalLeft);
115             availableWidth = *floatConstraints.right - lineLogicalLeft;
116         }
117     }
118
119     Display::Box::Rect logicalRect;
120     logicalRect.setTop(lineLogicalTop);
121     logicalRect.setLeft(lineLogicalLeft);
122     logicalRect.setWidth(availableWidth);
123     logicalRect.setHeight(formattingRoot.style().computedLineHeight());
124
125     line.init(logicalRect);
126 }
127
128 void InlineFormattingContext::splitInlineRunIfNeeded(const InlineRun& inlineRun, InlineRuns& splitRuns) const
129 {
130     if (!inlineRun.overlapsMultipleInlineItems())
131         return;
132
133     ASSERT(inlineRun.textContext());
134     // In certain cases, a run can overlap multiple inline elements like this:
135     // <span>normal text content</span><span style="position: relative; left: 10px;">but this one needs a dedicated run</span><span>end of text</span>
136     // The content above generates one long run <normal text contentbut this one needs dedicated runend of text>
137     // However, since the middle run is positioned, it needs to be moved independently from the rest of the content, hence it needs a dedicated inline run.
138
139     // 1. Start with the first inline item (element) and travers the list until
140     // 2. either find an inline item that needs a dedicated run or we reach the end of the run
141     // 3. Create dedicate inline runs.
142     auto& inlineContent = inlineFormattingState().inlineContent();
143     auto textUtil = TextUtil { inlineContent };
144
145     auto split=[&](const auto& inlineItem, auto startPosition, auto length, auto contentStart) {
146         auto width = textUtil.width(inlineItem, startPosition, length, contentStart);
147
148         auto run = InlineRun { { inlineRun.logicalTop(), contentStart, width, inlineRun.height() }, inlineItem };
149         run.setTextContext({ startPosition, length });
150         splitRuns.append(run);
151         return contentStart + width;
152     };
153
154     auto contentStart = inlineRun.logicalLeft();
155     auto startPosition = inlineRun.textContext()->start();
156     auto remaningLength = inlineRun.textContext()->length();
157
158     unsigned uncommittedLength = 0;
159     InlineItem* firstUncommittedInlineItem = nullptr;
160     for (auto iterator = inlineContent.find<const InlineItem&, InlineItemHashTranslator>(inlineRun.inlineItem()); iterator != inlineContent.end() && remaningLength > 0; ++iterator) {
161         auto& inlineItem = **iterator;
162
163         // Skip all non-inflow boxes (floats, out-of-flow positioned elements). They don't participate in the inline run context.
164         if (!inlineItem.layoutBox().isInFlow())
165             continue;
166
167         auto currentLength = [&] {
168             return std::min(remaningLength, inlineItem.textContent().length() - startPosition);
169         };
170
171         // 1. Inline element does not require run breaking -> add current inline element to uncommitted. Jump to the next element.
172         // 2. Break at the beginning of the inline element -> commit what we've got so far. Current element becomes the first uncommitted.
173         // 3. Break at the end of the inline element -> commit what we've got so far including the current element.
174         // 4. Break before/after -> requires dedicated run -> commit what we've got so far and also commit the current inline element as a separate inline run.
175         auto detachingRules = inlineFormattingState().detachingRules(inlineItem.layoutBox());
176
177         // #1
178         if (!detachingRules) {
179             uncommittedLength += currentLength();
180             firstUncommittedInlineItem = !firstUncommittedInlineItem ? &inlineItem : firstUncommittedInlineItem;
181             continue;
182         }
183
184         auto commit = [&] {
185             if (!firstUncommittedInlineItem)
186                 return;
187
188             contentStart = split(*firstUncommittedInlineItem, startPosition, uncommittedLength, contentStart);
189
190             remaningLength -= uncommittedLength;
191             startPosition = 0;
192             uncommittedLength = 0;
193             firstUncommittedInlineItem = nullptr;
194         };
195
196         // #2
197         if (*detachingRules == InlineFormattingState::DetachingRule::BreakAtStart) {
198             commit();
199             firstUncommittedInlineItem = &inlineItem;
200             uncommittedLength = currentLength();
201             continue;
202         }
203
204         // #3
205         if (*detachingRules == InlineFormattingState::DetachingRule::BreakAtEnd) {
206             ASSERT(firstUncommittedInlineItem);
207             uncommittedLength += currentLength();
208             commit();
209             continue;
210         }
211
212         // #4
213         commit();
214         firstUncommittedInlineItem = &inlineItem;
215         uncommittedLength = currentLength();
216         commit();
217     }
218
219     // Either all inline elements needed dedicated runs or neither of them.
220     if (!remaningLength || remaningLength == inlineRun.textContext()->length())
221         return;
222
223     ASSERT(remaningLength == uncommittedLength);
224     split(*firstUncommittedInlineItem, startPosition, uncommittedLength, contentStart);
225 }
226
227 InlineFormattingContext::Line::RunRange InlineFormattingContext::splitInlineRunsIfNeeded(Line::RunRange runRange) const
228 {
229     auto& inlineRuns = inlineFormattingState().inlineRuns();
230     ASSERT(*runRange.lastRunIndex < inlineRuns.size());
231
232     auto runIndex = *runRange.firstRunIndex;
233     auto& lastInlineRun = inlineRuns[*runRange.lastRunIndex];
234     while (runIndex < inlineRuns.size()) {
235         auto& inlineRun = inlineRuns[runIndex];
236         auto isLastRunInRange = &inlineRun == &lastInlineRun;
237
238         InlineRuns splitRuns;
239         splitInlineRunIfNeeded(inlineRun, splitRuns);
240         if (!splitRuns.isEmpty()) {
241             ASSERT(splitRuns.size() > 1);
242             // Replace the continous run with new ones.
243             // Reuse the original one.
244             auto& firstRun = splitRuns.first();
245             inlineRun.setWidth(firstRun.width());
246             inlineRun.textContext()->setLength(firstRun.textContext()->length());
247             splitRuns.remove(0);
248             // Insert the rest.
249             for (auto& splitRun : splitRuns)
250                 inlineRuns.insert(++runIndex, splitRun);
251         }
252
253         if (isLastRunInRange)
254             break;
255
256         ++runIndex;
257     }
258
259     return { runRange.firstRunIndex, runIndex };
260 }
261
262 void InlineFormattingContext::postProcessInlineRuns(Line& line, IsLastLine isLastLine, Line::RunRange runRange) const
263 {
264     auto& inlineFormattingState = this->inlineFormattingState();
265     Geometry::alignRuns(inlineFormattingState, root().style().textAlign(), line, runRange, isLastLine);
266     runRange = splitInlineRunsIfNeeded(runRange);
267     placeInFlowPositionedChildren(runRange);
268 }
269
270 void InlineFormattingContext::closeLine(Line& line, IsLastLine isLastLine) const
271 {
272     auto runRange = line.close();
273     ASSERT(!runRange.firstRunIndex || runRange.lastRunIndex);
274
275     if (!runRange.firstRunIndex)
276         return;
277
278     postProcessInlineRuns(line, isLastLine, runRange);
279 }
280
281 void InlineFormattingContext::appendContentToLine(Line& line, const InlineLineBreaker::Run& run) const
282 {
283     auto lastRunType = line.lastRunType();
284     line.appendContent(run);
285
286     if (root().style().textAlign() == TextAlignMode::Justify)
287         Geometry::computeExpansionOpportunities(inlineFormattingState(), run.content, lastRunType.value_or(InlineRunProvider::Run::Type::NonWhitespace));
288 }
289
290 void InlineFormattingContext::layoutInlineContent(const InlineRunProvider& inlineRunProvider) const
291 {
292     auto& layoutState = this->layoutState();
293     auto& inlineFormattingState = this->inlineFormattingState();
294     auto floatingContext = FloatingContext { inlineFormattingState.floatingState() };
295
296     Line line(inlineFormattingState);
297     initializeNewLine(line);
298
299     InlineLineBreaker lineBreaker(layoutState, inlineFormattingState.inlineContent(), inlineRunProvider.runs());
300     while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
301         auto isFirstRun = run->position == InlineLineBreaker::Run::Position::LineBegin;
302         auto isLastRun = run->position == InlineLineBreaker::Run::Position::LineEnd;
303         auto generatesInlineRun = true;
304
305         // Position float and adjust the runs on line.
306         if (run->content.isFloat()) {
307             auto& floatBox = run->content.inlineItem().layoutBox();
308             computeFloatPosition(floatingContext, line, floatBox);
309             inlineFormattingState.floatingState().append(floatBox);
310
311             auto floatBoxWidth = layoutState.displayBoxForLayoutBox(floatBox).width();
312             // Shrink availble space for current line and move existing inline runs.
313             floatBox.isLeftFloatingPositioned() ? line.adjustLogicalLeft(floatBoxWidth) : line.adjustLogicalRight(floatBoxWidth);
314
315             generatesInlineRun = false;
316         }
317
318         // 1. Initialize new line if needed.
319         // 2. Append inline run unless it is skipped.
320         // 3. Close current line if needed.
321         if (isFirstRun) {
322             // When the first run does not generate an actual inline run, the next run comes in first-run as well.
323             // No need to spend time on closing/initializing.
324             // Skip leading whitespace.
325             if (!generatesInlineRun || isTrimmableContent(*run))
326                 continue;
327
328             if (line.hasContent()) {
329                 // Previous run ended up being at the line end. Adjust the line accordingly.
330                 if (!line.isClosed())
331                     closeLine(line, IsLastLine::No);
332                 initializeNewLine(line);
333             }
334          }
335
336         if (generatesInlineRun)
337             appendContentToLine(line, *run);
338
339         if (isLastRun)
340             closeLine(line, IsLastLine::No);
341     }
342
343     closeLine(line, IsLastLine::Yes);
344 }
345
346 void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox) const
347 {
348     auto& layoutState = this->layoutState();
349
350     WidthAndMargin widthAndMargin;
351     if (layoutBox.isFloatingPositioned())
352         widthAndMargin = Geometry::floatingWidthAndMargin(layoutState, layoutBox);
353     else if (layoutBox.isInlineBlockBox())
354         widthAndMargin = Geometry::inlineBlockWidthAndMargin(layoutState, layoutBox);
355     else if (layoutBox.replaced())
356         widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutState, layoutBox);
357     else
358         ASSERT_NOT_REACHED();
359
360     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
361     displayBox.setContentBoxWidth(widthAndMargin.width);
362     displayBox.setHorizontalMargin(widthAndMargin.margin);
363     displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
364 }
365
366 void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox) const
367 {
368     auto& layoutState = this->layoutState();
369
370     HeightAndMargin heightAndMargin;
371     if (layoutBox.isFloatingPositioned())
372         heightAndMargin = Geometry::floatingHeightAndMargin(layoutState, layoutBox);
373     else if (layoutBox.isInlineBlockBox())
374         heightAndMargin = Geometry::inlineBlockHeightAndMargin(layoutState, layoutBox);
375     else if (layoutBox.replaced())
376         heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutState, layoutBox);
377     else
378         ASSERT_NOT_REACHED();
379
380     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
381     displayBox.setContentBoxHeight(heightAndMargin.height);
382     displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
383     displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
384 }
385
386 void InlineFormattingContext::layoutFormattingContextRoot(const Box& layoutBox) const
387 {
388     ASSERT(layoutBox.isFloatingPositioned() || layoutBox.isInlineBlockBox());
389
390     auto& layoutState = this->layoutState();
391     auto& formattingState = layoutState.createFormattingStateForFormattingRootIfNeeded(layoutBox);
392     computeBorderAndPadding(layoutBox);
393     computeWidthAndMargin(layoutBox);
394     // Swich over to the new formatting context (the one that the root creates).
395     formattingState.formattingContext(layoutBox)->layout();
396     // Come back and finalize the root's height and margin.
397     computeHeightAndMargin(layoutBox);
398 }
399
400 void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Box& layoutBox) const
401 {
402     ASSERT(!layoutBox.isContainer());
403     ASSERT(!layoutBox.establishesFormattingContext());
404     ASSERT(layoutBox.replaced());
405
406     computeBorderAndPadding(layoutBox);
407     computeWidthAndMargin(layoutBox);
408     computeHeightAndMargin(layoutBox);
409 }
410
411 void InlineFormattingContext::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
412 {
413     auto& layoutState = this->layoutState();
414     ASSERT(layoutState.hasDisplayBox(floatBox));
415     auto& displayBox = layoutState.displayBoxForLayoutBox(floatBox);
416
417     // Set static position first.
418     displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
419     // Float it.
420     displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
421 }
422
423 void InlineFormattingContext::placeInFlowPositionedChildren(Line::RunRange runRange) const
424 {
425     auto& inlineRuns = inlineFormattingState().inlineRuns();
426     ASSERT(*runRange.lastRunIndex < inlineRuns.size());
427
428     for (auto runIndex = *runRange.firstRunIndex; runIndex <= *runRange.lastRunIndex; ++runIndex) {
429         auto& inlineRun = inlineRuns[runIndex];
430
431         auto positionOffset = [&](auto& layoutBox) {
432             // FIXME: Need to figure out whether in-flow offset should stick. This might very well be temporary.
433             std::optional<LayoutSize> offset;
434             for (auto* box = &layoutBox; box != &root(); box = box->parent()) {
435                 if (!box->isInFlowPositioned())
436                     continue;
437                 offset = offset.value_or(LayoutSize()) + Geometry::inFlowPositionedPositionOffset(layoutState(), *box);
438             }
439             return offset;
440         };
441
442         if (auto offset = positionOffset(inlineRun.inlineItem().layoutBox())) {
443             inlineRun.moveVertically(offset->height());
444             inlineRun.moveHorizontally(offset->width());
445         }
446     }
447 }
448
449 void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, InlineRunProvider& inlineRunProvider) const
450 {
451     // Collect inline content recursively and set breaking rules for the inline elements (for paddings, margins, positioned element etc).
452     auto& inlineFormattingState = this->inlineFormattingState();
453
454     if (root.establishesFormattingContext() && &root != &(this->root())) {
455         // Skip formatting root subtree. They are not part of this inline formatting context.
456         inlineRunProvider.append(root);
457         inlineFormattingState.addDetachingRule(root, { InlineFormattingState::DetachingRule::BreakAtStart, InlineFormattingState::DetachingRule::BreakAtEnd });
458         return;
459     }
460
461     if (!is<Container>(root)) {
462         inlineRunProvider.append(root);
463         return;
464     }
465
466     auto* lastInlineBoxBeforeContainer = inlineFormattingState.lastInlineItem();
467     auto* child = downcast<Container>(root).firstInFlowOrFloatingChild();
468     while (child) {
469         collectInlineContentForSubtree(*child, inlineRunProvider);
470         child = child->nextInFlowOrFloatingSibling();
471     }
472
473     // Setup breaking boundaries for this subtree.
474     auto* lastDescendantInlineBox = inlineFormattingState.lastInlineItem();
475     // Empty container?
476     if (lastInlineBoxBeforeContainer == lastDescendantInlineBox)
477         return;
478
479     auto rootBreaksAtStart = [&] {
480         // FIXME: add padding-inline-start, margin-inline-start etc.
481         return root.isPositioned();
482     };
483
484     auto rootBreaksAtEnd = [&] {
485         // FIXME: add padding-inline-end, margin-inline-end etc.
486         return root.isPositioned();
487     };
488
489     if (rootBreaksAtStart()) {
490         InlineItem* firstDescendantInlineBox = nullptr;
491         auto& inlineContent = inlineFormattingState.inlineContent();
492
493         if (lastInlineBoxBeforeContainer) {
494             auto iterator = inlineContent.find<const InlineItem&, InlineItemHashTranslator>(*lastInlineBoxBeforeContainer);
495             firstDescendantInlineBox = (*++iterator).get();
496         } else
497             firstDescendantInlineBox = inlineContent.first().get();
498
499         ASSERT(firstDescendantInlineBox);
500         inlineFormattingState.addDetachingRule(firstDescendantInlineBox->layoutBox(), InlineFormattingState::DetachingRule::BreakAtStart);
501     }
502
503     if (rootBreaksAtEnd())
504         inlineFormattingState.addDetachingRule(lastDescendantInlineBox->layoutBox(), InlineFormattingState::DetachingRule::BreakAtEnd);
505 }
506
507 void InlineFormattingContext::collectInlineContent(InlineRunProvider& inlineRunProvider) const
508 {
509     collectInlineContentForSubtree(root(), inlineRunProvider);
510 }
511
512 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints() const
513 {
514     auto& formattingStateForRoot = layoutState().formattingStateForBox(root());
515     if (auto instrinsicWidthConstraints = formattingStateForRoot.instrinsicWidthConstraints(root()))
516         return *instrinsicWidthConstraints;
517
518     auto& inlineFormattingState = this->inlineFormattingState();
519     InlineRunProvider inlineRunProvider(inlineFormattingState);
520     collectInlineContent(inlineRunProvider);
521
522     // Compute width for non-text content.
523     for (auto& inlineRun : inlineRunProvider.runs()) {
524         if (inlineRun.isText())
525             continue;
526
527         computeWidthAndMargin(inlineRun.inlineItem().layoutBox());
528     }
529
530     auto maximumLineWidth = [&](auto availableWidth) {
531         LayoutUnit maxContentLogicalRight;
532         InlineLineBreaker lineBreaker(layoutState(), inlineFormattingState.inlineContent(), inlineRunProvider.runs());
533         LayoutUnit lineLogicalRight;
534         while (auto run = lineBreaker.nextRun(lineLogicalRight, availableWidth, !lineLogicalRight)) {
535             if (run->position == InlineLineBreaker::Run::Position::LineBegin)
536                 lineLogicalRight = 0;
537             lineLogicalRight += run->width;
538
539             maxContentLogicalRight = std::max(maxContentLogicalRight, lineLogicalRight);
540         }
541         return maxContentLogicalRight;
542     };
543
544     auto instrinsicWidthConstraints = FormattingContext::InstrinsicWidthConstraints { maximumLineWidth(0), maximumLineWidth(LayoutUnit::max()) };
545     formattingStateForRoot.setInstrinsicWidthConstraints(root(), instrinsicWidthConstraints);
546     return instrinsicWidthConstraints;
547 }
548
549 }
550 }
551
552 #endif