2 * Copyright (C) 2018 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "InlineFormattingContext.h"
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
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"
41 #include <wtf/IsoMallocInlines.h>
42 #include <wtf/text/TextStream.h>
47 WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingContext);
49 InlineFormattingContext::InlineFormattingContext(const Box& formattingContextRoot)
50 : FormattingContext(formattingContextRoot)
54 void InlineFormattingContext::layout(LayoutContext& layoutContext, FormattingState& formattingState) const
56 if (!is<Container>(root()))
59 LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> layout context(" << &layoutContext << ") formatting root(" << &root() << ")");
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).
67 if (is<Container>(layoutBox)) {
68 ASSERT(is<InlineContainer>(layoutBox));
69 layoutBox = downcast<Container>(*layoutBox).firstInFlowOrFloatingChild();
73 inlineRunProvider.append(*layoutBox);
74 computeWidthAndHeight(layoutContext, *layoutBox);
76 for (; layoutBox; layoutBox = layoutBox->parent()) {
77 if (layoutBox == &formattingRoot) {
81 if (auto* nextSibling = layoutBox->nextInFlowOrFloatingSibling()) {
82 layoutBox = nextSibling;
86 ASSERT(!layoutBox || layoutBox->isDescendantOf(formattingRoot));
89 layoutInlineContent(layoutContext, inlineFormattingState, inlineRunProvider);
91 LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> layout context(" << &layoutContext << ") formatting root(" << &root() << ")");
94 static bool trimLeadingRun(const InlineLineBreaker::Run& run)
96 ASSERT(run.position == InlineLineBreaker::Run::Position::LineBegin);
98 if (!run.content.isWhitespace())
101 return run.content.style().collapseWhiteSpace();
104 void InlineFormattingContext::initializeNewLine(const LayoutContext& layoutContext, Line& line) const
106 auto& formattingRoot = downcast<Container>(root());
107 auto& formattingRootDisplayBox = layoutContext.displayBoxForLayoutBox(formattingRoot);
109 auto lineLogicalLeft = formattingRootDisplayBox.contentBoxLeft();
110 auto lineLogicalTop = line.isFirstLine() ? formattingRootDisplayBox.contentBoxTop() : line.logicalBottom();
111 auto availableWidth = formattingRootDisplayBox.contentBoxWidth();
113 Display::Box::Rect logicalRect;
114 logicalRect.setTop(lineLogicalTop);
115 logicalRect.setLeft(lineLogicalLeft);
116 logicalRect.setWidth(availableWidth);
117 logicalRect.setHeight(formattingRoot.style().computedLineHeight());
119 line.init(logicalRect);
122 void InlineFormattingContext::layoutInlineContent(const LayoutContext& layoutContext, InlineFormattingState& inlineFormattingState, const InlineRunProvider& inlineRunProvider) const
124 auto previousRunPositionIsLineEnd = false;
126 Line line(inlineFormattingState, root());
127 initializeNewLine(layoutContext, line);
129 InlineLineBreaker lineBreaker(layoutContext, inlineFormattingState.inlineContent(), inlineRunProvider.runs());
130 while (auto run = lineBreaker.nextRun(line.contentLogicalRight(), line.availableWidth(), !line.hasContent())) {
132 if (run->position == InlineLineBreaker::Run::Position::LineBegin) {
133 // First run on line.
135 // Previous run ended up being at the line end. Adjust the line accordingly.
136 if (!previousRunPositionIsLineEnd) {
138 initializeNewLine(layoutContext, line);
140 // Skip leading whitespace.
141 if (!trimLeadingRun(*run))
142 line.appendContent(*run);
146 if (run->position == InlineLineBreaker::Run::Position::LineEnd) {
148 previousRunPositionIsLineEnd = true;
149 line.appendContent(*run);
150 // Move over to the next line.
152 initializeNewLine(layoutContext, line);
156 // This may or may not be the last run on line -but definitely not the first one.
157 line.appendContent(*run);
159 line.close(Line::LastLine::Yes);
162 void InlineFormattingContext::computeWidthAndHeight(LayoutContext& layoutContext, const Box& layoutBox) const
164 if (is<InlineBox>(layoutBox) && downcast<InlineBox>(layoutBox).hasTextContent()) {
165 // 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
166 // split up (or concatenated).
170 if (layoutBox.isInlineBlockBox()) {
171 ASSERT_NOT_IMPLEMENTED_YET();
175 computeBorderAndPadding(layoutContext, layoutBox);
177 WidthAndMargin widthAndMargin;
178 HeightAndMargin heightAndMargin;
180 if (layoutBox.isFloatingPositioned()) {
181 widthAndMargin = Geometry::floatingWidthAndMargin(layoutContext, *this, layoutBox);
182 heightAndMargin = Geometry::floatingHeightAndMargin(layoutContext, layoutBox);
183 } else if (layoutBox.replaced()) {
184 widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutContext, layoutBox);
185 heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutContext, layoutBox);
187 ASSERT_NOT_REACHED();
189 auto& displayBox = layoutContext.displayBoxForLayoutBox(layoutBox);
190 displayBox.setContentBoxWidth(widthAndMargin.width);
191 displayBox.setHorizontalMargin(widthAndMargin.margin);
192 displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
194 displayBox.setContentBoxHeight(heightAndMargin.height);
195 displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
196 displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
200 void InlineFormattingContext::computeFloatPosition(const LayoutContext& layoutContext, const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
202 ASSERT(layoutContext.hasDisplayBox(floatBox));
203 auto& displayBox = layoutContext.displayBoxForLayoutBox(floatBox);
205 // Set static position first.
206 displayBox.setTopLeft({ line.contentLogicalRight(), line.logicalTop() });
208 displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
211 void InlineFormattingContext::computeStaticPosition(const LayoutContext&, const Box&) const
215 void InlineFormattingContext::computeInFlowPositionedPosition(const LayoutContext&, const Box&) const
219 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints(LayoutContext&, const Box&) const