[LFC] FormattingContext base class should not declare computeStaticPosition.
[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         auto currentLength = [&] {
164             return std::min(remaningLength, inlineItem.textContent().length() - startPosition);
165         };
166
167         // 1. Inline element does not require run breaking -> add current inline element to uncommitted. Jump to the next element.
168         // 2. Break at the beginning of the inline element -> commit what we've got so far. Current element becomes the first uncommitted.
169         // 3. Break at the end of the inline element -> commit what we've got so far including the current element.
170         // 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.
171         auto detachingRules = inlineFormattingState().detachingRules(inlineItem.layoutBox());
172
173         // #1
174         if (!detachingRules) {
175             uncommittedLength += currentLength();
176             firstUncommittedInlineItem = !firstUncommittedInlineItem ? &inlineItem : firstUncommittedInlineItem;
177             continue;
178         }
179
180         auto commit = [&] {
181             if (!firstUncommittedInlineItem)
182                 return;
183
184             contentStart = split(*firstUncommittedInlineItem, startPosition, uncommittedLength, contentStart);
185
186             remaningLength -= uncommittedLength;
187             startPosition = 0;
188             uncommittedLength = 0;
189             firstUncommittedInlineItem = nullptr;
190         };
191
192         // #2
193         if (*detachingRules == InlineFormattingState::DetachingRule::BreakAtStart) {
194             commit();
195             firstUncommittedInlineItem = &inlineItem;
196             uncommittedLength = currentLength();
197             continue;
198         }
199
200         // #3
201         if (*detachingRules == InlineFormattingState::DetachingRule::BreakAtEnd) {
202             ASSERT(firstUncommittedInlineItem);
203             uncommittedLength += currentLength();
204             commit();
205             continue;
206         }
207
208         // #4
209         commit();
210         firstUncommittedInlineItem = &inlineItem;
211         uncommittedLength = currentLength();
212         commit();
213     }
214
215     // Either all inline elements needed dedicated runs or neither of them.
216     if (!remaningLength || remaningLength == inlineRun.textContext()->length())
217         return;
218
219     ASSERT(remaningLength == uncommittedLength);
220     split(*firstUncommittedInlineItem, startPosition, uncommittedLength, contentStart);
221 }
222
223 InlineFormattingContext::Line::RunRange InlineFormattingContext::splitInlineRunsIfNeeded(Line::RunRange runRange) const
224 {
225     auto& inlineRuns = inlineFormattingState().inlineRuns();
226     ASSERT(*runRange.lastRunIndex < inlineRuns.size());
227
228     auto runIndex = *runRange.firstRunIndex;
229     auto& lastInlineRun = inlineRuns[*runRange.lastRunIndex];
230     while (runIndex < inlineRuns.size()) {
231         auto& inlineRun = inlineRuns[runIndex];
232         auto isLastRunInRange = &inlineRun == &lastInlineRun;
233
234         InlineRuns splitRuns;
235         splitInlineRunIfNeeded(inlineRun, splitRuns);
236         if (!splitRuns.isEmpty()) {
237             ASSERT(splitRuns.size() > 1);
238             // Replace the continous run with new ones.
239             // Reuse the original one.
240             auto& firstRun = splitRuns.first();
241             inlineRun.setWidth(firstRun.width());
242             inlineRun.textContext()->setLength(firstRun.textContext()->length());
243             splitRuns.remove(0);
244             // Insert the rest.
245             for (auto& splitRun : splitRuns)
246                 inlineRuns.insert(++runIndex, splitRun);
247         }
248
249         if (isLastRunInRange)
250             break;
251
252         ++runIndex;
253     }
254
255     return { runRange.firstRunIndex, runIndex };
256 }
257
258 void InlineFormattingContext::postProcessInlineRuns(Line& line, IsLastLine isLastLine, Line::RunRange runRange) const
259 {
260     auto& inlineFormattingState = this->inlineFormattingState();
261     Geometry::alignRuns(inlineFormattingState, root().style().textAlign(), line, runRange, isLastLine);
262     runRange = splitInlineRunsIfNeeded(runRange);
263     placeInFlowPositionedChildren(runRange);
264 }
265
266 void InlineFormattingContext::closeLine(Line& line, IsLastLine isLastLine) const
267 {
268     auto runRange = line.close();
269     ASSERT(!runRange.firstRunIndex || runRange.lastRunIndex);
270
271     if (!runRange.firstRunIndex)
272         return;
273
274     postProcessInlineRuns(line, isLastLine, runRange);
275 }
276
277 void InlineFormattingContext::appendContentToLine(Line& line, const InlineLineBreaker::Run& run) const
278 {
279     auto lastRunType = line.lastRunType();
280     line.appendContent(run);
281
282     if (root().style().textAlign() == TextAlignMode::Justify)
283         Geometry::computeExpansionOpportunities(inlineFormattingState(), run.content, lastRunType.value_or(InlineRunProvider::Run::Type::NonWhitespace));
284 }
285
286 void InlineFormattingContext::layoutInlineContent(const InlineRunProvider& inlineRunProvider) const
287 {
288     auto& layoutState = this->layoutState();
289     auto& inlineFormattingState = this->inlineFormattingState();
290     auto floatingContext = FloatingContext { inlineFormattingState.floatingState() };
291
292     Line line(inlineFormattingState);
293     initializeNewLine(line);
294
295     InlineLineBreaker lineBreaker(layoutState, inlineFormattingState.inlineContent(), inlineRunProvider.runs());
296     while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
297         auto isFirstRun = run->position == InlineLineBreaker::Run::Position::LineBegin;
298         auto isLastRun = run->position == InlineLineBreaker::Run::Position::LineEnd;
299         auto generatesInlineRun = true;
300
301         // Position float and adjust the runs on line.
302         if (run->content.isFloat()) {
303             auto& floatBox = run->content.inlineItem().layoutBox();
304             computeFloatPosition(floatingContext, line, floatBox);
305             inlineFormattingState.floatingState().append(floatBox);
306
307             auto floatBoxWidth = layoutState.displayBoxForLayoutBox(floatBox).width();
308             // Shrink availble space for current line and move existing inline runs.
309             floatBox.isLeftFloatingPositioned() ? line.adjustLogicalLeft(floatBoxWidth) : line.adjustLogicalRight(floatBoxWidth);
310
311             generatesInlineRun = false;
312         }
313
314         // 1. Initialize new line if needed.
315         // 2. Append inline run unless it is skipped.
316         // 3. Close current line if needed.
317         if (isFirstRun) {
318             // When the first run does not generate an actual inline run, the next run comes in first-run as well.
319             // No need to spend time on closing/initializing.
320             // Skip leading whitespace.
321             if (!generatesInlineRun || isTrimmableContent(*run))
322                 continue;
323
324             if (line.hasContent()) {
325                 // Previous run ended up being at the line end. Adjust the line accordingly.
326                 if (!line.isClosed())
327                     closeLine(line, IsLastLine::No);
328                 initializeNewLine(line);
329             }
330          }
331
332         if (generatesInlineRun)
333             appendContentToLine(line, *run);
334
335         if (isLastRun)
336             closeLine(line, IsLastLine::No);
337     }
338
339     closeLine(line, IsLastLine::Yes);
340 }
341
342 void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox) const
343 {
344     auto& layoutState = this->layoutState();
345
346     WidthAndMargin widthAndMargin;
347     if (layoutBox.isFloatingPositioned())
348         widthAndMargin = Geometry::floatingWidthAndMargin(layoutState, layoutBox);
349     else if (layoutBox.isInlineBlockBox())
350         widthAndMargin = Geometry::inlineBlockWidthAndMargin(layoutState, layoutBox);
351     else if (layoutBox.replaced())
352         widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutState, layoutBox);
353     else
354         ASSERT_NOT_REACHED();
355
356     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
357     displayBox.setContentBoxWidth(widthAndMargin.width);
358     displayBox.setHorizontalMargin(widthAndMargin.margin);
359     displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
360 }
361
362 void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox) const
363 {
364     auto& layoutState = this->layoutState();
365
366     HeightAndMargin heightAndMargin;
367     if (layoutBox.isFloatingPositioned())
368         heightAndMargin = Geometry::floatingHeightAndMargin(layoutState, layoutBox);
369     else if (layoutBox.isInlineBlockBox())
370         heightAndMargin = Geometry::inlineBlockHeightAndMargin(layoutState, layoutBox);
371     else if (layoutBox.replaced())
372         heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutState, layoutBox);
373     else
374         ASSERT_NOT_REACHED();
375
376     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
377     displayBox.setContentBoxHeight(heightAndMargin.height);
378     displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
379     displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
380 }
381
382 void InlineFormattingContext::layoutFormattingContextRoot(const Box& layoutBox) const
383 {
384     ASSERT(layoutBox.isFloatingPositioned() || layoutBox.isInlineBlockBox());
385
386     auto& layoutState = this->layoutState();
387     auto& formattingState = layoutState.createFormattingStateForFormattingRootIfNeeded(layoutBox);
388     computeBorderAndPadding(layoutBox);
389     computeWidthAndMargin(layoutBox);
390     // Swich over to the new formatting context (the one that the root creates).
391     formattingState.formattingContext(layoutBox)->layout();
392     // Come back and finalize the root's height and margin.
393     computeHeightAndMargin(layoutBox);
394 }
395
396 void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Box& layoutBox) const
397 {
398     ASSERT(!layoutBox.isContainer());
399     ASSERT(!layoutBox.establishesFormattingContext());
400     ASSERT(layoutBox.replaced());
401
402     computeBorderAndPadding(layoutBox);
403     computeWidthAndMargin(layoutBox);
404     computeHeightAndMargin(layoutBox);
405 }
406
407 void InlineFormattingContext::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
408 {
409     auto& layoutState = this->layoutState();
410     ASSERT(layoutState.hasDisplayBox(floatBox));
411     auto& displayBox = layoutState.displayBoxForLayoutBox(floatBox);
412
413     // Set static position first.
414     displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
415     // Float it.
416     displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
417 }
418
419 void InlineFormattingContext::placeInFlowPositionedChildren(Line::RunRange runRange) const
420 {
421     auto& inlineRuns = inlineFormattingState().inlineRuns();
422     ASSERT(*runRange.lastRunIndex < inlineRuns.size());
423
424     for (auto runIndex = *runRange.firstRunIndex; runIndex <= *runRange.lastRunIndex; ++runIndex) {
425         auto& inlineRun = inlineRuns[runIndex];
426
427         auto positionOffset = [&](auto& layoutBox) {
428             // FIXME: Need to figure out whether in-flow offset should stick. This might very well be temporary.
429             std::optional<LayoutSize> offset;
430             for (auto* box = &layoutBox; box != &root(); box = box->parent()) {
431                 if (!box->isInFlowPositioned())
432                     continue;
433                 offset = offset.value_or(LayoutSize()) + Geometry::inFlowPositionedPositionOffset(layoutState(), *box);
434             }
435             return offset;
436         };
437
438         if (auto offset = positionOffset(inlineRun.inlineItem().layoutBox())) {
439             inlineRun.moveVertically(offset->height());
440             inlineRun.moveHorizontally(offset->width());
441         }
442     }
443 }
444
445 void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, InlineRunProvider& inlineRunProvider) const
446 {
447     // Collect inline content recursively and set breaking rules for the inline elements (for paddings, margins, positioned element etc).
448     auto& inlineFormattingState = this->inlineFormattingState();
449
450     if (root.establishesFormattingContext() && &root != &(this->root())) {
451         // Skip formatting root subtree. They are not part of this inline formatting context.
452         inlineRunProvider.append(root);
453         inlineFormattingState.addDetachingRule(root, { InlineFormattingState::DetachingRule::BreakAtStart, InlineFormattingState::DetachingRule::BreakAtEnd });
454         return;
455     }
456
457     if (!is<Container>(root)) {
458         inlineRunProvider.append(root);
459         return;
460     }
461
462     auto* lastInlineBoxBeforeContainer = inlineFormattingState.lastInlineItem();
463     auto* child = downcast<Container>(root).firstInFlowOrFloatingChild();
464     while (child) {
465         collectInlineContentForSubtree(*child, inlineRunProvider);
466         child = child->nextInFlowOrFloatingSibling();
467     }
468
469     // Setup breaking boundaries for this subtree.
470     auto* lastDescendantInlineBox = inlineFormattingState.lastInlineItem();
471     // Empty container?
472     if (lastInlineBoxBeforeContainer == lastDescendantInlineBox)
473         return;
474
475     auto rootBreaksAtStart = [&] {
476         // FIXME: add padding-inline-start, margin-inline-start etc.
477         return root.isPositioned();
478     };
479
480     auto rootBreaksAtEnd = [&] {
481         // FIXME: add padding-inline-end, margin-inline-end etc.
482         return root.isPositioned();
483     };
484
485     if (rootBreaksAtStart()) {
486         InlineItem* firstDescendantInlineBox = nullptr;
487         auto& inlineContent = inlineFormattingState.inlineContent();
488
489         if (lastInlineBoxBeforeContainer) {
490             auto iterator = inlineContent.find<const InlineItem&, InlineItemHashTranslator>(*lastInlineBoxBeforeContainer);
491             firstDescendantInlineBox = (*++iterator).get();
492         } else
493             firstDescendantInlineBox = inlineContent.first().get();
494
495         ASSERT(firstDescendantInlineBox);
496         inlineFormattingState.addDetachingRule(firstDescendantInlineBox->layoutBox(), InlineFormattingState::DetachingRule::BreakAtStart);
497     }
498
499     if (rootBreaksAtEnd())
500         inlineFormattingState.addDetachingRule(lastDescendantInlineBox->layoutBox(), InlineFormattingState::DetachingRule::BreakAtEnd);
501 }
502
503 void InlineFormattingContext::collectInlineContent(InlineRunProvider& inlineRunProvider) const
504 {
505     collectInlineContentForSubtree(root(), inlineRunProvider);
506 }
507
508 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints() const
509 {
510     auto& formattingStateForRoot = layoutState().formattingStateForBox(root());
511     if (auto instrinsicWidthConstraints = formattingStateForRoot.instrinsicWidthConstraints(root()))
512         return *instrinsicWidthConstraints;
513
514     auto& inlineFormattingState = this->inlineFormattingState();
515     InlineRunProvider inlineRunProvider(inlineFormattingState);
516     collectInlineContent(inlineRunProvider);
517
518     // Compute width for non-text content.
519     for (auto& inlineRun : inlineRunProvider.runs()) {
520         if (inlineRun.isText())
521             continue;
522
523         computeWidthAndMargin(inlineRun.inlineItem().layoutBox());
524     }
525
526     auto maximumLineWidth = [&](auto availableWidth) {
527         LayoutUnit maxContentLogicalRight;
528         InlineLineBreaker lineBreaker(layoutState(), inlineFormattingState.inlineContent(), inlineRunProvider.runs());
529         LayoutUnit lineLogicalRight;
530         while (auto run = lineBreaker.nextRun(lineLogicalRight, availableWidth, !lineLogicalRight)) {
531             if (run->position == InlineLineBreaker::Run::Position::LineBegin)
532                 lineLogicalRight = 0;
533             lineLogicalRight += run->width;
534
535             maxContentLogicalRight = std::max(maxContentLogicalRight, lineLogicalRight);
536         }
537         return maxContentLogicalRight;
538     };
539
540     auto instrinsicWidthConstraints = FormattingContext::InstrinsicWidthConstraints { maximumLineWidth(0), maximumLineWidth(LayoutUnit::max()) };
541     formattingStateForRoot.setInstrinsicWidthConstraints(root(), instrinsicWidthConstraints);
542     return instrinsicWidthConstraints;
543 }
544
545 }
546 }
547
548 #endif