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