[LFC][IFC] Adjust current line with float constraints.
[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 "LayoutContext.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)
50     : FormattingContext(formattingContextRoot)
51 {
52 }
53
54 void InlineFormattingContext::layout(LayoutContext& layoutContext, FormattingState& formattingState) const
55 {
56     if (!is<Container>(root()))
57         return;
58
59     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> layout context(" << &layoutContext << ") 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         if (is<Container>(layoutBox)) {
68             ASSERT(is<InlineContainer>(layoutBox));
69             layoutBox = downcast<Container>(*layoutBox).firstInFlowOrFloatingChild();
70             continue;
71         }
72
73         inlineRunProvider.append(*layoutBox);
74         computeWidthAndHeight(layoutContext, *layoutBox);
75
76         for (; layoutBox; layoutBox = layoutBox->parent()) {
77             if (layoutBox == &formattingRoot) {
78                 layoutBox = nullptr;
79                 break;
80             }
81             if (auto* nextSibling = layoutBox->nextInFlowOrFloatingSibling()) {
82                 layoutBox = nextSibling;
83                 break;
84             }
85         }
86         ASSERT(!layoutBox || layoutBox->isDescendantOf(formattingRoot));
87     }
88
89     layoutInlineContent(layoutContext, inlineFormattingState, inlineRunProvider);
90
91     LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> layout context(" << &layoutContext << ") formatting root(" << &root() << ")");
92 }
93
94 static bool trimLeadingRun(const InlineLineBreaker::Run& run)
95 {
96     ASSERT(run.position == InlineLineBreaker::Run::Position::LineBegin);
97
98     if (!run.content.isWhitespace())
99         return false;
100
101     return run.content.style().collapseWhiteSpace();
102 }
103
104 void InlineFormattingContext::initializeNewLine(const LayoutContext& layoutContext, InlineFormattingState& inlineFormattingState, Line& line) const
105 {
106     auto& formattingRoot = downcast<Container>(root());
107     auto& formattingRootDisplayBox = layoutContext.displayBoxForLayoutBox(formattingRoot);
108
109     auto lineLogicalLeft = formattingRootDisplayBox.contentBoxLeft();
110     auto lineLogicalTop = line.isFirstLine() ? formattingRootDisplayBox.contentBoxTop() : line.logicalBottom();
111     auto availableWidth = formattingRootDisplayBox.contentBoxWidth();
112
113     // Check for intruding floats and adjust logical left/available width for this line accordingly.
114     auto& floatingState = inlineFormattingState.floatingState();
115     if (!floatingState.isEmpty()) {
116         auto floatConstraints = floatingState.constraints(lineLogicalTop, formattingRoot);
117         // Check if these constraints actually put limitation on the line.
118         if (floatConstraints.left && *floatConstraints.left <= formattingRootDisplayBox.contentBoxLeft())
119             floatConstraints.left = { };
120
121         if (floatConstraints.right && *floatConstraints.right >= formattingRootDisplayBox.contentBoxRight())
122             floatConstraints.right = { };
123
124         if (floatConstraints.left && floatConstraints.right) {
125             ASSERT(*floatConstraints.left < *floatConstraints.right);
126             availableWidth = *floatConstraints.right - *floatConstraints.left;
127             lineLogicalLeft = *floatConstraints.left;
128         } else if (floatConstraints.left) {
129             ASSERT(*floatConstraints.left > lineLogicalLeft);
130             availableWidth -= (*floatConstraints.left - lineLogicalLeft);
131             lineLogicalLeft = *floatConstraints.left;
132         } else if (floatConstraints.right) {
133             ASSERT(*floatConstraints.right > lineLogicalLeft);
134             availableWidth = *floatConstraints.right - lineLogicalLeft;
135         }
136     }
137
138     Display::Box::Rect logicalRect;
139     logicalRect.setTop(lineLogicalTop);
140     logicalRect.setLeft(lineLogicalLeft);
141     logicalRect.setWidth(availableWidth);
142     logicalRect.setHeight(formattingRoot.style().computedLineHeight());
143
144     line.init(logicalRect);
145 }
146
147 void InlineFormattingContext::layoutInlineContent(const LayoutContext& layoutContext, InlineFormattingState& inlineFormattingState, const InlineRunProvider& inlineRunProvider) const
148 {
149     auto previousRunPositionIsLineEnd = false;
150
151     Line line(inlineFormattingState, root());
152     initializeNewLine(layoutContext, inlineFormattingState, line);
153
154     InlineLineBreaker lineBreaker(layoutContext, inlineFormattingState.inlineContent(), inlineRunProvider.runs());
155     while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
156
157         if (run->position == InlineLineBreaker::Run::Position::LineBegin) {
158             // First run on line.
159
160             // Previous run ended up being at the line end. Adjust the line accordingly.
161             if (!previousRunPositionIsLineEnd) {
162                 line.close();
163                 initializeNewLine(layoutContext, inlineFormattingState, line);
164             }
165             // Skip leading whitespace.
166             if (!trimLeadingRun(*run))
167                 line.appendContent(*run);
168             continue;
169         }
170
171         if (run->position == InlineLineBreaker::Run::Position::LineEnd) {
172             // Last run on line.
173             previousRunPositionIsLineEnd = true;
174             line.appendContent(*run);
175             // Move over to the next line.
176             line.close();
177             initializeNewLine(layoutContext, inlineFormattingState, line);
178             continue;
179         }
180
181         // This may or may not be the last run on line -but definitely not the first one.
182         line.appendContent(*run);
183     }
184     line.close(Line::LastLine::Yes);
185 }
186
187 void InlineFormattingContext::computeWidthAndHeight(LayoutContext& layoutContext, const Box& layoutBox) const
188 {
189     if (is<InlineBox>(layoutBox) && downcast<InlineBox>(layoutBox).hasTextContent()) {
190         // 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
191         // split up (or concatenated).
192         return;
193     }
194
195     if (layoutBox.isInlineBlockBox()) {
196         ASSERT_NOT_IMPLEMENTED_YET();
197         return;
198     }
199
200     computeBorderAndPadding(layoutContext, layoutBox);
201
202     WidthAndMargin widthAndMargin;
203     HeightAndMargin heightAndMargin;
204
205     if (layoutBox.isFloatingPositioned()) {
206         widthAndMargin = Geometry::floatingWidthAndMargin(layoutContext, *this, layoutBox);
207         heightAndMargin = Geometry::floatingHeightAndMargin(layoutContext, layoutBox);
208     } else if (layoutBox.replaced()) {
209         widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutContext, layoutBox);
210         heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutContext, layoutBox);
211     } else
212         ASSERT_NOT_REACHED();
213
214     auto& displayBox = layoutContext.displayBoxForLayoutBox(layoutBox);
215     displayBox.setContentBoxWidth(widthAndMargin.width);
216     displayBox.setHorizontalMargin(widthAndMargin.margin);
217     displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
218
219     displayBox.setContentBoxHeight(heightAndMargin.height);
220     displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
221     displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
222     return;
223 }
224
225 void InlineFormattingContext::computeFloatPosition(const LayoutContext& layoutContext, const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
226 {
227     ASSERT(layoutContext.hasDisplayBox(floatBox));
228     auto& displayBox = layoutContext.displayBoxForLayoutBox(floatBox);
229
230     // Set static position first.
231     displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
232     // Float it.
233     displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
234 }
235
236 void InlineFormattingContext::computeStaticPosition(const LayoutContext&, const Box&) const
237 {
238 }
239
240 void InlineFormattingContext::computeInFlowPositionedPosition(const LayoutContext&, const Box&) const
241 {
242 }
243
244 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints(LayoutContext&, const Box&) const
245 {
246     return { };
247 }
248
249 }
250 }
251
252 #endif