[LFC] Do not pass LayoutState& to compute* and layout* functions
[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     auto& formattingRoot = downcast<Container>(root());
64     auto* layoutBox = formattingRoot.firstInFlowOrFloatingChild();
65     // Casually walk through the block's descendants and place the inline boxes one after the other as much as we can (yeah, I am looking at you floats).
66     while (layoutBox) {
67
68         if (layoutBox->establishesFormattingContext()) {
69             layoutFormattingContextRoot(*layoutBox);
70             // Formatting context roots take care of their entire subtree. Continue with next sibling.
71             inlineRunProvider.append(*layoutBox);
72             layoutBox = layoutBox->nextInFlowOrFloatingSibling();
73             continue;
74         }
75
76         if (is<Container>(layoutBox)) {
77             ASSERT(is<InlineContainer>(layoutBox));
78             layoutBox = downcast<Container>(*layoutBox).firstInFlowOrFloatingChild();
79             continue;
80         }
81
82         inlineRunProvider.append(*layoutBox);
83         computeWidthAndHeightForInlineBox(*layoutBox);
84
85         for (; layoutBox; layoutBox = layoutBox->parent()) {
86             if (layoutBox == &formattingRoot) {
87                 layoutBox = nullptr;
88                 break;
89             }
90             if (auto* nextSibling = layoutBox->nextInFlowOrFloatingSibling()) {
91                 layoutBox = nextSibling;
92                 break;
93             }
94         }
95         ASSERT(!layoutBox || layoutBox->isDescendantOf(formattingRoot));
96     }
97
98     layoutInlineContent(inlineRunProvider);
99
100     LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
101 }
102
103 static bool isTrimmableContent(const InlineLineBreaker::Run& run)
104 {
105     return run.content.isWhitespace() && run.content.style().collapseWhiteSpace();
106 }
107
108 void InlineFormattingContext::initializeNewLine(Line& line) const
109 {
110     auto& formattingRoot = downcast<Container>(root());
111     auto& formattingRootDisplayBox = layoutState().displayBoxForLayoutBox(formattingRoot);
112
113     auto lineLogicalLeft = formattingRootDisplayBox.contentBoxLeft();
114     auto lineLogicalTop = line.isFirstLine() ? formattingRootDisplayBox.contentBoxTop() : line.logicalBottom();
115     auto availableWidth = formattingRootDisplayBox.contentBoxWidth();
116
117     // Check for intruding floats and adjust logical left/available width for this line accordingly.
118     auto& floatingState = formattingState().floatingState();
119     if (!floatingState.isEmpty()) {
120         auto floatConstraints = floatingState.constraints(lineLogicalTop, formattingRoot);
121         // Check if these constraints actually put limitation on the line.
122         if (floatConstraints.left && *floatConstraints.left <= formattingRootDisplayBox.contentBoxLeft())
123             floatConstraints.left = { };
124
125         if (floatConstraints.right && *floatConstraints.right >= formattingRootDisplayBox.contentBoxRight())
126             floatConstraints.right = { };
127
128         if (floatConstraints.left && floatConstraints.right) {
129             ASSERT(*floatConstraints.left < *floatConstraints.right);
130             availableWidth = *floatConstraints.right - *floatConstraints.left;
131             lineLogicalLeft = *floatConstraints.left;
132         } else if (floatConstraints.left) {
133             ASSERT(*floatConstraints.left > lineLogicalLeft);
134             availableWidth -= (*floatConstraints.left - lineLogicalLeft);
135             lineLogicalLeft = *floatConstraints.left;
136         } else if (floatConstraints.right) {
137             ASSERT(*floatConstraints.right > lineLogicalLeft);
138             availableWidth = *floatConstraints.right - lineLogicalLeft;
139         }
140     }
141
142     Display::Box::Rect logicalRect;
143     logicalRect.setTop(lineLogicalTop);
144     logicalRect.setLeft(lineLogicalLeft);
145     logicalRect.setWidth(availableWidth);
146     logicalRect.setHeight(formattingRoot.style().computedLineHeight());
147
148     line.init(logicalRect);
149 }
150
151 void InlineFormattingContext::layoutInlineContent(const InlineRunProvider& inlineRunProvider) const
152 {
153     auto& layoutState = this->layoutState();
154     auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
155     auto floatingContext = FloatingContext { inlineFormattingState.floatingState() };
156
157     Line line(inlineFormattingState, root());
158     initializeNewLine(line);
159
160     InlineLineBreaker lineBreaker(layoutState, inlineFormattingState.inlineContent(), inlineRunProvider.runs());
161     while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
162         auto isFirstRun = run->position == InlineLineBreaker::Run::Position::LineBegin;
163         auto isLastRun = run->position == InlineLineBreaker::Run::Position::LineEnd;
164         auto generatesInlineRun = true;
165
166         // Position float and adjust the runs on line.
167         if (run->content.isFloat()) {
168             auto& floatBox = run->content.inlineItem().layoutBox();
169             computeFloatPosition(floatingContext, line, floatBox);
170             inlineFormattingState.floatingState().append(floatBox);
171
172             auto floatBoxWidth = layoutState.displayBoxForLayoutBox(floatBox).width();
173             // Shrink availble space for current line and move existing inline runs.
174             floatBox.isLeftFloatingPositioned() ? line.adjustLogicalLeft(floatBoxWidth) : line.adjustLogicalRight(floatBoxWidth);
175
176             generatesInlineRun = false;
177         }
178
179         // 1. Initialize new line if needed.
180         // 2. Append inline run unless it is skipped.
181         // 3. Close current line if needed.
182         if (isFirstRun) {
183             // When the first run does not generate an actual inline run, the next run comes in first-run as well.
184             // No need to spend time on closing/initializing.
185             // Skip leading whitespace.
186             if (!generatesInlineRun || isTrimmableContent(*run))
187                 continue;
188
189             if (line.hasContent()) {
190                 // Previous run ended up being at the line end. Adjust the line accordingly.
191                 if (!line.isClosed())
192                     line.close(Line::LastLine::No);
193                 initializeNewLine(line);
194             }
195          }
196
197         if (generatesInlineRun)
198              line.appendContent(*run);
199
200         if (isLastRun)
201             line.close(Line::LastLine::No);
202     }
203
204     line.close(Line::LastLine::Yes);
205 }
206
207 void InlineFormattingContext::layoutFormattingContextRoot(const Box& layoutBox) const
208 {
209     auto& layoutState = this->layoutState();
210
211     ASSERT(layoutBox.isFloatingPositioned() || layoutBox.isInlineBlockBox());
212     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
213
214     auto computeWidthAndMargin = [&]() {
215         WidthAndMargin widthAndMargin;
216
217         if (layoutBox.isFloatingPositioned())
218             widthAndMargin = Geometry::floatingWidthAndMargin(layoutState, *this, layoutBox);
219         else if (layoutBox.isInlineBlockBox())
220             widthAndMargin = Geometry::inlineBlockWidthAndMargin(layoutState, layoutBox);
221         else
222             ASSERT_NOT_REACHED();
223
224         displayBox.setContentBoxWidth(widthAndMargin.width);
225         displayBox.setHorizontalMargin(widthAndMargin.margin);
226         displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
227     };
228
229     auto computeHeightAndMargin = [&]() {
230         HeightAndMargin heightAndMargin;
231
232         if (layoutBox.isFloatingPositioned())
233             heightAndMargin = Geometry::floatingHeightAndMargin(layoutState, layoutBox);
234         else if (layoutBox.isInlineBlockBox())
235             heightAndMargin = Geometry::inlineBlockHeightAndMargin(layoutState, layoutBox);
236         else
237             ASSERT_NOT_REACHED();
238
239         displayBox.setContentBoxHeight(heightAndMargin.height);
240         displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
241         displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
242     };
243
244     computeBorderAndPadding(layoutBox);
245     computeWidthAndMargin();
246
247     // Swich over to the new formatting context (the one that the root creates).
248     layoutState.createFormattingStateForFormattingRootIfNeeded(layoutBox).formattingContext(layoutBox)->layout();
249
250     // Come back and finalize the root's height and margin.
251     computeHeightAndMargin();
252 }
253
254 void InlineFormattingContext::computeWidthAndHeightForInlineBox(const Box& layoutBox) const
255 {
256     ASSERT(!layoutBox.isContainer());
257     ASSERT(!layoutBox.establishesFormattingContext());
258
259     if (is<InlineBox>(layoutBox) && downcast<InlineBox>(layoutBox).hasTextContent()) {
260         // Text content width is computed during text run generation. -It does not make any sense to measure unprocessed text here, since it will likely be
261         // split up (or concatenated).
262         return;
263     }
264
265     auto& layoutState = this->layoutState();
266     // This is pretty much only for replaced inline boxes atm.
267     ASSERT(layoutBox.replaced());
268     computeBorderAndPadding(layoutBox);
269
270     auto widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutState, layoutBox);
271     auto heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutState, layoutBox);
272
273     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
274     displayBox.setContentBoxWidth(widthAndMargin.width);
275     displayBox.setHorizontalMargin(widthAndMargin.margin);
276     displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
277
278     displayBox.setContentBoxHeight(heightAndMargin.height);
279     displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
280     displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
281 }
282
283 void InlineFormattingContext::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
284 {
285     auto& layoutState = this->layoutState();
286     ASSERT(layoutState.hasDisplayBox(floatBox));
287     auto& displayBox = layoutState.displayBoxForLayoutBox(floatBox);
288
289     // Set static position first.
290     displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
291     // Float it.
292     displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
293 }
294
295 void InlineFormattingContext::computeStaticPosition(const Box&) const
296 {
297 }
298
299 void InlineFormattingContext::computeInFlowPositionedPosition(const Box&) const
300 {
301 }
302
303 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints(const Box&) const
304 {
305     return { };
306 }
307
308 }
309 }
310
311 #endif