[LFC][IFC] Add support for intrinsic width calculation
[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     auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
62     InlineRunProvider inlineRunProvider(inlineFormattingState);
63
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     Display::Box::Rect logicalRect;
122     logicalRect.setTop(lineLogicalTop);
123     logicalRect.setLeft(lineLogicalLeft);
124     logicalRect.setWidth(availableWidth);
125     logicalRect.setHeight(formattingRoot.style().computedLineHeight());
126
127     line.init(logicalRect);
128 }
129
130 void InlineFormattingContext::layoutInlineContent(const InlineRunProvider& inlineRunProvider) const
131 {
132     auto& layoutState = this->layoutState();
133     auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
134     auto floatingContext = FloatingContext { inlineFormattingState.floatingState() };
135
136     Line line(inlineFormattingState, root());
137     initializeNewLine(line);
138
139     InlineLineBreaker lineBreaker(layoutState, inlineFormattingState.inlineContent(), inlineRunProvider.runs());
140     while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
141         auto isFirstRun = run->position == InlineLineBreaker::Run::Position::LineBegin;
142         auto isLastRun = run->position == InlineLineBreaker::Run::Position::LineEnd;
143         auto generatesInlineRun = true;
144
145         // Position float and adjust the runs on line.
146         if (run->content.isFloat()) {
147             auto& floatBox = run->content.inlineItem().layoutBox();
148             computeFloatPosition(floatingContext, line, floatBox);
149             inlineFormattingState.floatingState().append(floatBox);
150
151             auto floatBoxWidth = layoutState.displayBoxForLayoutBox(floatBox).width();
152             // Shrink availble space for current line and move existing inline runs.
153             floatBox.isLeftFloatingPositioned() ? line.adjustLogicalLeft(floatBoxWidth) : line.adjustLogicalRight(floatBoxWidth);
154
155             generatesInlineRun = false;
156         }
157
158         // 1. Initialize new line if needed.
159         // 2. Append inline run unless it is skipped.
160         // 3. Close current line if needed.
161         if (isFirstRun) {
162             // When the first run does not generate an actual inline run, the next run comes in first-run as well.
163             // No need to spend time on closing/initializing.
164             // Skip leading whitespace.
165             if (!generatesInlineRun || isTrimmableContent(*run))
166                 continue;
167
168             if (line.hasContent()) {
169                 // Previous run ended up being at the line end. Adjust the line accordingly.
170                 if (!line.isClosed())
171                     line.close(Line::LastLine::No);
172                 initializeNewLine(line);
173             }
174          }
175
176         if (generatesInlineRun)
177              line.appendContent(*run);
178
179         if (isLastRun)
180             line.close(Line::LastLine::No);
181     }
182
183     line.close(Line::LastLine::Yes);
184 }
185
186 void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox) const
187 {
188     auto& layoutState = this->layoutState();
189
190     WidthAndMargin widthAndMargin;
191     if (layoutBox.isFloatingPositioned())
192         widthAndMargin = Geometry::floatingWidthAndMargin(layoutState, layoutBox);
193     else if (layoutBox.isInlineBlockBox())
194         widthAndMargin = Geometry::inlineBlockWidthAndMargin(layoutState, layoutBox);
195     else if (layoutBox.replaced())
196         widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutState, layoutBox);
197     else
198         ASSERT_NOT_REACHED();
199
200     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
201     displayBox.setContentBoxWidth(widthAndMargin.width);
202     displayBox.setHorizontalMargin(widthAndMargin.margin);
203     displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
204 }
205
206 void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox) const
207 {
208     auto& layoutState = this->layoutState();
209
210     HeightAndMargin heightAndMargin;
211     if (layoutBox.isFloatingPositioned())
212         heightAndMargin = Geometry::floatingHeightAndMargin(layoutState, layoutBox);
213     else if (layoutBox.isInlineBlockBox())
214         heightAndMargin = Geometry::inlineBlockHeightAndMargin(layoutState, layoutBox);
215     else if (layoutBox.replaced())
216         heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutState, layoutBox);
217     else
218         ASSERT_NOT_REACHED();
219
220     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
221     displayBox.setContentBoxHeight(heightAndMargin.height);
222     displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
223     displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
224 }
225
226 void InlineFormattingContext::layoutFormattingContextRoot(const Box& layoutBox) const
227 {
228     ASSERT(layoutBox.isFloatingPositioned() || layoutBox.isInlineBlockBox());
229
230     auto& layoutState = this->layoutState();
231     auto& formattingState = layoutState.createFormattingStateForFormattingRootIfNeeded(layoutBox);
232     computeBorderAndPadding(layoutBox);
233     computeWidthAndMargin(layoutBox);
234     // Swich over to the new formatting context (the one that the root creates).
235     formattingState.formattingContext(layoutBox)->layout();
236     // Come back and finalize the root's height and margin.
237     computeHeightAndMargin(layoutBox);
238 }
239
240 void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Box& layoutBox) const
241 {
242     ASSERT(!layoutBox.isContainer());
243     ASSERT(!layoutBox.establishesFormattingContext());
244     ASSERT(layoutBox.replaced());
245
246     computeBorderAndPadding(layoutBox);
247     computeWidthAndMargin(layoutBox);
248     computeHeightAndMargin(layoutBox);
249 }
250
251 void InlineFormattingContext::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
252 {
253     auto& layoutState = this->layoutState();
254     ASSERT(layoutState.hasDisplayBox(floatBox));
255     auto& displayBox = layoutState.displayBoxForLayoutBox(floatBox);
256
257     // Set static position first.
258     displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
259     // Float it.
260     displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
261 }
262
263 void InlineFormattingContext::computeStaticPosition(const Box&) const
264 {
265 }
266
267 void InlineFormattingContext::computeInFlowPositionedPosition(const Box&) const
268 {
269 }
270
271 void InlineFormattingContext::collectInlineContent(InlineRunProvider& inlineRunProvider) const
272 {
273     if (!is<Container>(root()))
274         return;
275
276     auto& formattingRoot = downcast<Container>(root());
277     auto* layoutBox = formattingRoot.firstInFlowOrFloatingChild();
278
279     while (layoutBox) {
280         ASSERT(layoutBox->isDescendantOf(formattingRoot));
281
282         if (layoutBox->establishesFormattingContext()) {
283             inlineRunProvider.append(*layoutBox);
284             layoutBox = layoutBox->nextInFlowOrFloatingSibling();
285             continue;
286         }
287
288         if (is<Container>(layoutBox)) {
289             layoutBox = downcast<Container>(*layoutBox).firstInFlowOrFloatingChild();
290             continue;
291         }
292
293         inlineRunProvider.append(*layoutBox);
294
295         while (true) {
296             if (auto* nextSibling = layoutBox->nextInFlowOrFloatingSibling()) {
297                 layoutBox = nextSibling;
298                 break;
299             }
300
301             layoutBox = layoutBox->parent();
302
303             if (layoutBox == &formattingRoot)
304                 return;
305         }
306     }
307 }
308
309 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints() const
310 {
311     auto& formattingStateForRoot = layoutState().formattingStateForBox(root());
312     if (auto instrinsicWidthConstraints = formattingStateForRoot.instrinsicWidthConstraints(root()))
313         return *instrinsicWidthConstraints;
314
315     auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
316     InlineRunProvider inlineRunProvider(inlineFormattingState);
317     collectInlineContent(inlineRunProvider);
318
319     // Compute width for non-text content.
320     for (auto& inlineRun : inlineRunProvider.runs()) {
321         if (inlineRun.isText())
322             continue;
323
324         computeWidthAndMargin(inlineRun.inlineItem().layoutBox());
325     }
326
327     auto maximumLineWidth = [&](auto availableWidth) {
328         LayoutUnit maxContentLogicalRight;
329         InlineLineBreaker lineBreaker(layoutState(), inlineFormattingState.inlineContent(), inlineRunProvider.runs());
330         LayoutUnit lineLogicalRight;
331         while (auto run = lineBreaker.nextRun(lineLogicalRight, availableWidth, !lineLogicalRight)) {
332             if (run->position == InlineLineBreaker::Run::Position::LineBegin)
333                 lineLogicalRight = 0;
334             lineLogicalRight += run->width;
335
336             maxContentLogicalRight = std::max(maxContentLogicalRight, lineLogicalRight);
337         }
338         return maxContentLogicalRight;
339     };
340
341     auto instrinsicWidthConstraints = FormattingContext::InstrinsicWidthConstraints { maximumLineWidth(0), maximumLineWidth(LayoutUnit::max()) };
342     formattingStateForRoot.setInstrinsicWidthConstraints(root(), instrinsicWidthConstraints);
343     return instrinsicWidthConstraints;
344 }
345
346 }
347 }
348
349 #endif