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