Use Optional::valueOr() instead of Optional::value_or()
[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         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.valueOr(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.setVerticalMargin(heightAndMargin.margin);
366 }
367
368 void InlineFormattingContext::layoutFormattingContextRoot(const Box& root) const
369 {
370     ASSERT(root.isFloatingPositioned() || root.isInlineBlockBox());
371
372     computeBorderAndPadding(root);
373     computeWidthAndMargin(root);
374     // Swich over to the new formatting context (the one that the root creates).
375     auto formattingContext = layoutState().createFormattingStateForFormattingRootIfNeeded(root).createFormattingContext(root);
376     formattingContext->layout();
377     // Come back and finalize the root's height and margin.
378     computeHeightAndMargin(root);
379     // Now that we computed the root's height, we can go back and layout the out-of-flow descedants (if any).
380     formattingContext->layoutOutOfFlowDescendants(root);
381 }
382
383 void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Box& layoutBox) const
384 {
385     ASSERT(!layoutBox.isContainer());
386     ASSERT(!layoutBox.establishesFormattingContext());
387     ASSERT(layoutBox.replaced());
388
389     computeBorderAndPadding(layoutBox);
390     computeWidthAndMargin(layoutBox);
391     computeHeightAndMargin(layoutBox);
392 }
393
394 void InlineFormattingContext::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
395 {
396     auto& layoutState = this->layoutState();
397     ASSERT(layoutState.hasDisplayBox(floatBox));
398     auto& displayBox = layoutState.displayBoxForLayoutBox(floatBox);
399
400     // Set static position first.
401     displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
402     // Float it.
403     displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
404 }
405
406 void InlineFormattingContext::placeInFlowPositionedChildren(unsigned fistRunIndex) const
407 {
408     auto& inlineRuns = inlineFormattingState().inlineRuns();
409     for (auto runIndex = fistRunIndex; runIndex < inlineRuns.size(); ++runIndex) {
410         auto& inlineRun = inlineRuns[runIndex];
411
412         auto positionOffset = [&](auto& layoutBox) {
413             // FIXME: Need to figure out whether in-flow offset should stick. This might very well be temporary.
414             Optional<LayoutSize> offset;
415             for (auto* box = &layoutBox; box != &root(); box = box->parent()) {
416                 if (!box->isInFlowPositioned())
417                     continue;
418                 offset = offset.valueOr(LayoutSize()) + Geometry::inFlowPositionedPositionOffset(layoutState(), *box);
419             }
420             return offset;
421         };
422
423         if (auto offset = positionOffset(inlineRun.inlineItem().layoutBox())) {
424             inlineRun.moveVertically(offset->height());
425             inlineRun.moveHorizontally(offset->width());
426         }
427     }
428 }
429
430 void InlineFormattingContext::collectInlineContentForSubtree(const Box& root, InlineRunProvider& inlineRunProvider) const
431 {
432     // Collect inline content recursively and set breaking rules for the inline elements (for paddings, margins, positioned element etc).
433     auto& inlineFormattingState = this->inlineFormattingState();
434
435     auto createAndAppendInlineItem = [&] {
436         auto inlineItem = std::make_unique<InlineItem>(root);
437         inlineRunProvider.append(*inlineItem);
438         inlineFormattingState.inlineContent().add(WTFMove(inlineItem));
439     };
440
441     if (root.establishesFormattingContext() && &root != &(this->root())) {
442         createAndAppendInlineItem();
443         auto& inlineRun = *inlineFormattingState.inlineContent().last();
444
445         auto horizontalMargin = Geometry::computedNonCollapsedHorizontalMarginValue(layoutState(), root);
446         inlineRun.addDetachingRule({ InlineItem::DetachingRule::BreakAtStart, InlineItem::DetachingRule::BreakAtEnd });
447         inlineRun.addNonBreakableStart(horizontalMargin.start);
448         inlineRun.addNonBreakableEnd(horizontalMargin.end);
449         // Skip formatting root subtree. They are not part of this inline formatting context.
450         return;
451     }
452
453     if (!is<Container>(root)) {
454         createAndAppendInlineItem();
455         return;
456     }
457
458     auto* lastInlineBoxBeforeContainer = inlineFormattingState.lastInlineItem();
459     auto* child = downcast<Container>(root).firstInFlowOrFloatingChild();
460     while (child) {
461         collectInlineContentForSubtree(*child, inlineRunProvider);
462         child = child->nextInFlowOrFloatingSibling();
463     }
464
465     // FIXME: Revisit this when we figured out how inline boxes fit the display tree.
466     auto padding = Geometry::computedPadding(layoutState(), root);
467     auto border = Geometry::computedBorder(layoutState(), root);
468     auto horizontalMargin = Geometry::computedNonCollapsedHorizontalMarginValue(layoutState(), root);
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         if (&root == &(this->root()))
477             return false;
478         return (padding && padding->horizontal.left) || border.horizontal.left || horizontalMargin.start || root.isPositioned();
479     };
480
481     auto rootBreaksAtEnd = [&] {
482         if (&root == &(this->root()))
483             return false;
484         return (padding && padding->horizontal.right) || border.horizontal.right || horizontalMargin.end || root.isPositioned();
485     };
486
487     if (rootBreaksAtStart()) {
488         InlineItem* firstDescendantInlineBox = nullptr;
489         auto& inlineContent = inlineFormattingState.inlineContent();
490
491         if (lastInlineBoxBeforeContainer) {
492             auto iterator = inlineContent.find(lastInlineBoxBeforeContainer);
493             firstDescendantInlineBox = (*++iterator).get();
494         } else
495             firstDescendantInlineBox = inlineContent.first().get();
496
497         ASSERT(firstDescendantInlineBox);
498         firstDescendantInlineBox->addDetachingRule(InlineItem::DetachingRule::BreakAtStart);
499         auto startOffset = border.horizontal.left + horizontalMargin.start;
500         if (padding)
501             startOffset += padding->horizontal.left;
502         firstDescendantInlineBox->addNonBreakableStart(startOffset);
503     }
504
505     if (rootBreaksAtEnd()) {
506         lastDescendantInlineBox->addDetachingRule(InlineItem::DetachingRule::BreakAtEnd);
507         auto endOffset = border.horizontal.right + horizontalMargin.end;
508         if (padding)
509             endOffset += padding->horizontal.right;
510         lastDescendantInlineBox->addNonBreakableEnd(endOffset);
511     }
512 }
513
514 void InlineFormattingContext::collectInlineContent(InlineRunProvider& inlineRunProvider) const
515 {
516     collectInlineContentForSubtree(root(), inlineRunProvider);
517 }
518
519 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints() const
520 {
521     auto& formattingStateForRoot = layoutState().formattingStateForBox(root());
522     if (auto instrinsicWidthConstraints = formattingStateForRoot.instrinsicWidthConstraints(root()))
523         return *instrinsicWidthConstraints;
524
525     auto& inlineFormattingState = this->inlineFormattingState();
526     InlineRunProvider inlineRunProvider;
527     collectInlineContent(inlineRunProvider);
528
529     // Compute width for non-text content.
530     for (auto& inlineRun : inlineRunProvider.runs()) {
531         if (inlineRun.isText())
532             continue;
533
534         computeWidthAndMargin(inlineRun.inlineItem().layoutBox());
535     }
536
537     auto maximumLineWidth = [&](auto availableWidth) {
538         LayoutUnit maxContentLogicalRight;
539         InlineLineBreaker lineBreaker(layoutState(), inlineFormattingState.inlineContent(), inlineRunProvider.runs());
540         LayoutUnit lineLogicalRight;
541         while (auto run = lineBreaker.nextRun(lineLogicalRight, availableWidth, !lineLogicalRight)) {
542             if (run->position == InlineLineBreaker::Run::Position::LineBegin)
543                 lineLogicalRight = 0;
544             lineLogicalRight += run->width;
545
546             maxContentLogicalRight = std::max(maxContentLogicalRight, lineLogicalRight);
547         }
548         return maxContentLogicalRight;
549     };
550
551     return FormattingContext::InstrinsicWidthConstraints { maximumLineWidth(0), maximumLineWidth(LayoutUnit::max()) };
552 }
553
554 }
555 }
556
557 #endif