blob: 20250f52bb9b0ae49d49f065940e0198b812e60d [file] [log] [blame]
zalan@apple.com4c4bae22018-04-23 23:08:47 +00001/*
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"
zalan@apple.comce6567c2018-04-25 22:54:01 +000028
29#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31#include "InlineFormattingState.h"
zalan@apple.comae37bbb2018-10-19 16:41:58 +000032#include "InlineLineBreaker.h"
zalan@apple.com2bf37862019-05-25 20:14:09 +000033#include "InlineTextItem.h"
zalan@apple.comd56d5502018-04-27 15:40:16 +000034#include "LayoutBox.h"
zalan@apple.com5ac57092018-07-20 17:05:16 +000035#include "LayoutContainer.h"
zalan@apple.comac194b12019-09-20 13:36:00 +000036#include "LayoutContext.h"
antti@apple.com878f8862018-12-08 00:26:34 +000037#include "LayoutState.h"
zalan@apple.com5ac57092018-07-20 17:05:16 +000038#include "Logging.h"
zalan@apple.com5901b782018-11-20 15:56:57 +000039#include "Textutil.h"
zalan@apple.comce6567c2018-04-25 22:54:01 +000040#include <wtf/IsoMallocInlines.h>
zalan@apple.com5ac57092018-07-20 17:05:16 +000041#include <wtf/text/TextStream.h>
zalan@apple.comce6567c2018-04-25 22:54:01 +000042
43namespace WebCore {
44namespace Layout {
45
46WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingContext);
47
zalan@apple.com97eeb342019-09-13 19:15:48 +000048InlineFormattingContext::InlineFormattingContext(const Container& formattingContextRoot, InlineFormattingState& formattingState)
zalan@apple.com9892ac12018-10-31 13:50:05 +000049 : FormattingContext(formattingContextRoot, formattingState)
zalan@apple.comce6567c2018-04-25 22:54:01 +000050{
51}
52
zalan@apple.coma99fb222019-08-04 10:16:06 +000053static inline const Box* nextInPreOrder(const Box& layoutBox, const Container& stayWithin)
zalan@apple.com6bf87cf2019-02-05 16:01:30 +000054{
55 const Box* nextInPreOrder = nullptr;
56 if (!layoutBox.establishesFormattingContext() && is<Container>(layoutBox) && downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
57 return downcast<Container>(layoutBox).firstInFlowOrFloatingChild();
58
zalan@apple.coma99fb222019-08-04 10:16:06 +000059 for (nextInPreOrder = &layoutBox; nextInPreOrder && nextInPreOrder != &stayWithin; nextInPreOrder = nextInPreOrder->parent()) {
zalan@apple.com6bf87cf2019-02-05 16:01:30 +000060 if (auto* nextSibling = nextInPreOrder->nextInFlowOrFloatingSibling())
61 return nextSibling;
62 }
63 return nullptr;
64}
65
zalan@apple.com362e3382019-09-13 16:40:40 +000066void InlineFormattingContext::layoutInFlowContent()
zalan@apple.comce6567c2018-04-25 22:54:01 +000067{
zalan@apple.com97eeb342019-09-13 19:15:48 +000068 if (!root().hasInFlowOrFloatingChild())
zalan@apple.com5ac57092018-07-20 17:05:16 +000069 return;
70
zalan@apple.com97eeb342019-09-13 19:15:48 +000071 LOG_WITH_STREAM(FormattingContextLayout, stream << "[Start] -> inline formatting context -> formatting root(" << &root() << ")");
zalan@apple.comf6eb9b02019-09-23 16:14:21 +000072 auto& rootGeometry = geometryForBox(root());
73 auto usedHorizontalValues = UsedHorizontalValues { UsedHorizontalValues::Constraints { rootGeometry } };
74 auto usedVerticalValues = UsedVerticalValues { UsedVerticalValues::Constraints { rootGeometry } };
zalan@apple.com97eeb342019-09-13 19:15:48 +000075 auto* layoutBox = root().firstInFlowOrFloatingChild();
zalan@apple.comf6eb9b02019-09-23 16:14:21 +000076 // 1. Visit each inline box and partially compute their geometry (margins, paddings and borders).
77 // 2. Collect the inline items (flatten the the layout tree) and place them on lines in bidirectional order.
zalan@apple.com6bf87cf2019-02-05 16:01:30 +000078 while (layoutBox) {
79 if (layoutBox->establishesFormattingContext())
zalan@apple.comf6eb9b02019-09-23 16:14:21 +000080 layoutFormattingContextRoot(*layoutBox, usedHorizontalValues, usedVerticalValues);
zalan@apple.comfa3d8f92019-09-23 17:21:22 +000081 else
82 computeHorizontalAndVerticalGeometry(*layoutBox, usedHorizontalValues, usedVerticalValues);
zalan@apple.com97eeb342019-09-13 19:15:48 +000083 layoutBox = nextInPreOrder(*layoutBox, root());
zalan@apple.com6bf87cf2019-02-05 16:01:30 +000084 }
zalan@apple.com5ac57092018-07-20 17:05:16 +000085
zalan@apple.com2bf37862019-05-25 20:14:09 +000086 // FIXME: This is such a waste when intrinsic width computation already collected the inline items.
87 formattingState().inlineItems().clear();
88 formattingState().inlineRuns().clear();
89
90 collectInlineContent();
zalan@apple.comf0517792019-09-29 15:08:41 +000091 lineLayout(usedHorizontalValues);
zalan@apple.com97eeb342019-09-13 19:15:48 +000092 LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
zalan@apple.comce6567c2018-04-25 22:54:01 +000093}
94
zalan@apple.comf0517792019-09-29 15:08:41 +000095void InlineFormattingContext::lineLayout(UsedHorizontalValues usedHorizontalValues)
96{
97 auto& inlineItems = formattingState().inlineItems();
98 auto lineLogicalTop = geometryForBox(root()).contentBoxTop();
99 LineLayout::IndexAndRange currentInlineItem;
100 while (currentInlineItem.index < inlineItems.size()) {
101 auto lineConstraints = initialConstraintsForLine(usedHorizontalValues, lineLogicalTop);
102 auto lineInput = LineLayout::LineInput { lineConstraints, root().style().textAlign(), currentInlineItem, inlineItems };
103 auto lineLayout = LineLayout { *this, lineInput };
104
105 auto lineContent = lineLayout.layout();
106 setDisplayBoxesForLine(lineContent, usedHorizontalValues);
107
108 if (lineContent.lastCommitted) {
109 currentInlineItem = { lineContent.lastCommitted->index + 1, WTF::nullopt };
110 lineLogicalTop = lineContent.lineBox.logicalBottom();
111 } else {
112 // Floats prevented us placing any content on the line.
113 ASSERT(lineInput.initialConstraints.lineIsConstrainedByFloat);
114 // Move the next line below the intrusive float.
115 auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
116 auto floatConstraints = floatingContext.constraints({ lineLogicalTop });
117 ASSERT(floatConstraints.left || floatConstraints.right);
118 static auto inifitePoint = PointInContextRoot::max();
119 // In case of left and right constraints, we need to pick the one that's closer to the current line.
120 lineLogicalTop = std::min(floatConstraints.left.valueOr(inifitePoint).y, floatConstraints.right.valueOr(inifitePoint).y);
121 ASSERT(lineLogicalTop < inifitePoint.y);
122 }
123 }
124}
125
zalan@apple.comfa3d8f92019-09-23 17:21:22 +0000126void InlineFormattingContext::layoutFormattingContextRoot(const Box& formattingContextRoot, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues)
127{
128 ASSERT(formattingContextRoot.isFloatingPositioned() || formattingContextRoot.isInlineBlockBox());
129
130 computeBorderAndPadding(formattingContextRoot, usedHorizontalValues);
131 computeWidthAndMargin(formattingContextRoot, usedHorizontalValues);
132 // Swich over to the new formatting context (the one that the root creates).
133 if (is<Container>(formattingContextRoot)) {
134 auto& rootContainer = downcast<Container>(formattingContextRoot);
135 auto formattingContext = LayoutContext::createFormattingContext(rootContainer, layoutState());
136 formattingContext->layoutInFlowContent();
137 // Come back and finalize the root's height and margin.
138 computeHeightAndMargin(rootContainer, usedHorizontalValues, usedVerticalValues);
139 // Now that we computed the root's height, we can go back and layout the out-of-flow content.
140 formattingContext->layoutOutOfFlowContent();
141 } else
142 computeHeightAndMargin(formattingContextRoot, usedHorizontalValues, usedVerticalValues);
143}
144
145void InlineFormattingContext::computeHorizontalAndVerticalGeometry(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues)
146{
147 if (is<Container>(layoutBox)) {
148 // Inline containers (<span>) can't get sized/positioned yet. At this point we can only compute their margins, borders and paddings.
149 computeHorizontalMargin(layoutBox, usedHorizontalValues);
150 computeBorderAndPadding(layoutBox, usedHorizontalValues);
151 // Inline containers have 0 computed vertical margins.
152 formattingState().displayBox(layoutBox).setVerticalMargin({ { }, { } });
153 return;
154 }
155
156 if (layoutBox.isReplaced()) {
157 // Replaced elements (img, video) can be sized but not yet positioned.
158 computeBorderAndPadding(layoutBox, usedHorizontalValues);
159 computeWidthAndMargin(layoutBox, usedHorizontalValues);
160 computeHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues);
161 return;
162 }
163
164 // These are actual text boxes. No margins, borders or paddings.
165 ASSERT(layoutBox.isAnonymous() || layoutBox.isLineBreakBox());
166 auto& displayBox = formattingState().displayBox(layoutBox);
167
168 displayBox.setVerticalMargin({ { }, { } });
169 displayBox.setHorizontalMargin({ });
170 displayBox.setBorder({ { }, { } });
171 displayBox.setPadding({ });
172}
173
zalan@apple.com7aeac502019-08-30 17:02:31 +0000174FormattingContext::IntrinsicWidthConstraints InlineFormattingContext::computedIntrinsicWidthConstraints()
zalan@apple.comdee95862019-02-09 20:37:03 +0000175{
zalan@apple.comdee95862019-02-09 20:37:03 +0000176 auto& layoutState = this->layoutState();
zalan@apple.coma99fb222019-08-04 10:16:06 +0000177 ASSERT(!formattingState().intrinsicWidthConstraints());
zalan@apple.com09509252019-08-03 16:09:47 +0000178
zalan@apple.com97eeb342019-09-13 19:15:48 +0000179 if (!root().hasInFlowOrFloatingChild()) {
zalan@apple.com7aeac502019-08-30 17:02:31 +0000180 auto constraints = geometry().constrainByMinMaxWidth(root(), { });
zalan@apple.coma99fb222019-08-04 10:16:06 +0000181 formattingState().setIntrinsicWidthConstraints(constraints);
182 return constraints;
zalan@apple.com09509252019-08-03 16:09:47 +0000183 }
zalan@apple.comdee95862019-02-09 20:37:03 +0000184
zalan@apple.com25b5c772019-02-10 18:03:52 +0000185 Vector<const Box*> formattingContextRootList;
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000186 auto usedHorizontalValues = UsedHorizontalValues { UsedHorizontalValues::Constraints { { }, { } } };
zalan@apple.com97eeb342019-09-13 19:15:48 +0000187 auto* layoutBox = root().firstInFlowOrFloatingChild();
zalan@apple.comdee95862019-02-09 20:37:03 +0000188 while (layoutBox) {
zalan@apple.com694422c2019-02-12 15:34:36 +0000189 if (layoutBox->establishesFormattingContext()) {
zalan@apple.com25b5c772019-02-10 18:03:52 +0000190 formattingContextRootList.append(layoutBox);
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000191 computeIntrinsicWidthForFormattingRoot(*layoutBox, usedHorizontalValues);
zalan@apple.com25b5c772019-02-10 18:03:52 +0000192 } else if (layoutBox->isReplaced() || is<Container>(*layoutBox)) {
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000193 computeBorderAndPadding(*layoutBox, usedHorizontalValues);
zalan@apple.com25b5c772019-02-10 18:03:52 +0000194 // inline-block and replaced.
zalan@apple.coma99fb222019-08-04 10:16:06 +0000195 auto needsWidthComputation = layoutBox->isReplaced();
zalan@apple.com25b5c772019-02-10 18:03:52 +0000196 if (needsWidthComputation)
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000197 computeWidthAndMargin(*layoutBox, usedHorizontalValues);
zalan@apple.com27ca52c2019-02-10 16:53:41 +0000198 else {
199 // Simple inline container with no intrinsic width <span>.
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000200 computeHorizontalMargin(*layoutBox, usedHorizontalValues);
zalan@apple.com27ca52c2019-02-10 16:53:41 +0000201 }
202 }
zalan@apple.com97eeb342019-09-13 19:15:48 +0000203 layoutBox = nextInPreOrder(*layoutBox, root());
zalan@apple.comdee95862019-02-09 20:37:03 +0000204 }
205
zalan@apple.com2bf37862019-05-25 20:14:09 +0000206 collectInlineContent();
zalan@apple.comdee95862019-02-09 20:37:03 +0000207
208 auto maximumLineWidth = [&](auto availableWidth) {
zalan@apple.com25b5c772019-02-10 18:03:52 +0000209 // Switch to the min/max formatting root width values before formatting the lines.
210 for (auto* formattingRoot : formattingContextRootList) {
zalan@apple.coma99fb222019-08-04 10:16:06 +0000211 auto intrinsicWidths = layoutState.formattingStateForBox(*formattingRoot).intrinsicWidthConstraintsForBox(*formattingRoot);
zalan@apple.com3b39c362019-09-13 15:42:28 +0000212 auto& displayBox = formattingState().displayBox(*formattingRoot);
zalan@apple.coma99fb222019-08-04 10:16:06 +0000213 auto contentWidth = (availableWidth ? intrinsicWidths->maximum : intrinsicWidths->minimum) - displayBox.horizontalMarginBorderAndPadding();
214 displayBox.setContentBoxWidth(contentWidth);
zalan@apple.com25b5c772019-02-10 18:03:52 +0000215 }
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000216 auto usedHorizontalValues = UsedHorizontalValues { UsedHorizontalValues::Constraints { { }, availableWidth } };
zalan@apple.comf0517792019-09-29 15:08:41 +0000217 return computedIntrinsicWidthForConstraint(usedHorizontalValues);
zalan@apple.comdee95862019-02-09 20:37:03 +0000218 };
219
zalan@apple.com97eeb342019-09-13 19:15:48 +0000220 auto constraints = geometry().constrainByMinMaxWidth(root(), { maximumLineWidth(0), maximumLineWidth(LayoutUnit::max()) });
zalan@apple.coma99fb222019-08-04 10:16:06 +0000221 formattingState().setIntrinsicWidthConstraints(constraints);
222 return constraints;
zalan@apple.comdee95862019-02-09 20:37:03 +0000223}
224
zalan@apple.comf0517792019-09-29 15:08:41 +0000225LayoutUnit InlineFormattingContext::computedIntrinsicWidthForConstraint(UsedHorizontalValues usedHorizontalValues) const
226{
227 auto& inlineItems = formattingState().inlineItems();
228 LayoutUnit maximumLineWidth;
229 LineLayout::IndexAndRange currentInlineItem;
230 while (currentInlineItem.index < inlineItems.size()) {
231 // Only the horiztonal available width is constrained when computing intrinsic width.
232 auto initialLineConstraints = Line::InitialConstraints { { }, usedHorizontalValues.constraints.width, false, { } };
233 auto lineInput = LineLayout::LineInput { initialLineConstraints, currentInlineItem, inlineItems };
234
235 auto lineContent = LineLayout(*this, lineInput).layout();
236
237 currentInlineItem = { lineContent.lastCommitted->index + 1, { } };
238 LayoutUnit floatsWidth;
239 for (auto& floatItem : lineContent.floats)
240 floatsWidth += geometryForBox(floatItem->layoutBox()).marginBoxWidth();
241 maximumLineWidth = std::max(maximumLineWidth, floatsWidth + lineContent.lineBox.logicalWidth());
242 }
243 return maximumLineWidth;
244}
245
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000246void InlineFormattingContext::computeIntrinsicWidthForFormattingRoot(const Box& formattingRoot, UsedHorizontalValues usedHorizontalValues)
zalan@apple.com25b5c772019-02-10 18:03:52 +0000247{
zalan@apple.coma99fb222019-08-04 10:16:06 +0000248 ASSERT(formattingRoot.establishesFormattingContext());
zalan@apple.com694422c2019-02-12 15:34:36 +0000249
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000250 computeBorderAndPadding(formattingRoot, usedHorizontalValues);
251 computeHorizontalMargin(formattingRoot, usedHorizontalValues);
zalan@apple.com694422c2019-02-12 15:34:36 +0000252
zalan@apple.com97eeb342019-09-13 19:15:48 +0000253 auto constraints = IntrinsicWidthConstraints { };
zalan@apple.com7aeac502019-08-30 17:02:31 +0000254 if (auto fixedWidth = geometry().fixedValue(formattingRoot.style().logicalWidth()))
zalan@apple.coma99fb222019-08-04 10:16:06 +0000255 constraints = { *fixedWidth, *fixedWidth };
zalan@apple.com97eeb342019-09-13 19:15:48 +0000256 else if (is<Container>(formattingRoot))
zalan@apple.comac194b12019-09-20 13:36:00 +0000257 constraints = LayoutContext::createFormattingContext(downcast<Container>(formattingRoot), layoutState())->computedIntrinsicWidthConstraints();
zalan@apple.com7aeac502019-08-30 17:02:31 +0000258 constraints = geometry().constrainByMinMaxWidth(formattingRoot, constraints);
zalan@apple.com3b39c362019-09-13 15:42:28 +0000259 constraints.expand(geometryForBox(formattingRoot).horizontalMarginBorderAndPadding());
zalan@apple.coma99fb222019-08-04 10:16:06 +0000260 formattingState().setIntrinsicWidthConstraintsForBox(formattingRoot, constraints);
zalan@apple.com25b5c772019-02-10 18:03:52 +0000261}
262
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000263void InlineFormattingContext::computeHorizontalMargin(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues)
zalan@apple.com6bf87cf2019-02-05 16:01:30 +0000264{
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000265 auto computedHorizontalMargin = geometry().computedHorizontalMargin(layoutBox, usedHorizontalValues);
zalan@apple.com3b39c362019-09-13 15:42:28 +0000266 auto& displayBox = formattingState().displayBox(layoutBox);
zalan@apple.com6bf87cf2019-02-05 16:01:30 +0000267 displayBox.setHorizontalComputedMargin(computedHorizontalMargin);
268 displayBox.setHorizontalMargin({ computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) });
269}
270
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000271void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues)
zalan@apple.comae37bbb2018-10-19 16:41:58 +0000272{
zalan@apple.com4add9052018-11-02 14:23:54 +0000273 WidthAndMargin widthAndMargin;
274 if (layoutBox.isFloatingPositioned())
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000275 widthAndMargin = geometry().floatingWidthAndMargin(layoutBox, usedHorizontalValues);
zalan@apple.com4add9052018-11-02 14:23:54 +0000276 else if (layoutBox.isInlineBlockBox())
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000277 widthAndMargin = geometry().inlineBlockWidthAndMargin(layoutBox, usedHorizontalValues);
zalan@apple.com4add9052018-11-02 14:23:54 +0000278 else if (layoutBox.replaced())
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000279 widthAndMargin = geometry().inlineReplacedWidthAndMargin(layoutBox, usedHorizontalValues);
zalan@apple.com4add9052018-11-02 14:23:54 +0000280 else
281 ASSERT_NOT_REACHED();
zalan@apple.comae37bbb2018-10-19 16:41:58 +0000282
zalan@apple.com3b39c362019-09-13 15:42:28 +0000283 auto& displayBox = formattingState().displayBox(layoutBox);
zalan@apple.com9a4acd02018-10-26 14:36:30 +0000284 displayBox.setContentBoxWidth(widthAndMargin.width);
zalan@apple.com97077062019-01-04 15:21:45 +0000285 displayBox.setHorizontalMargin(widthAndMargin.usedMargin);
286 displayBox.setHorizontalComputedMargin(widthAndMargin.computedMargin);
zalan@apple.com4add9052018-11-02 14:23:54 +0000287}
zalan@apple.comae37bbb2018-10-19 16:41:58 +0000288
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000289void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues)
zalan@apple.com4add9052018-11-02 14:23:54 +0000290{
zalan@apple.com4add9052018-11-02 14:23:54 +0000291 HeightAndMargin heightAndMargin;
292 if (layoutBox.isFloatingPositioned())
zalan@apple.com8237af02019-09-18 21:48:03 +0000293 heightAndMargin = geometry().floatingHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues);
zalan@apple.com4add9052018-11-02 14:23:54 +0000294 else if (layoutBox.isInlineBlockBox())
zalan@apple.com8237af02019-09-18 21:48:03 +0000295 heightAndMargin = geometry().inlineBlockHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues);
zalan@apple.com4add9052018-11-02 14:23:54 +0000296 else if (layoutBox.replaced())
zalan@apple.com8237af02019-09-18 21:48:03 +0000297 heightAndMargin = geometry().inlineReplacedHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues);
zalan@apple.com4add9052018-11-02 14:23:54 +0000298 else
299 ASSERT_NOT_REACHED();
300
zalan@apple.com3b39c362019-09-13 15:42:28 +0000301 auto& displayBox = formattingState().displayBox(layoutBox);
zalan@apple.com9a4acd02018-10-26 14:36:30 +0000302 displayBox.setContentBoxHeight(heightAndMargin.height);
zalan@apple.coma33797c2019-01-07 16:18:52 +0000303 displayBox.setVerticalMargin({ heightAndMargin.nonCollapsedMargin, { } });
zalan@apple.comae37bbb2018-10-19 16:41:58 +0000304}
305
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000306void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Box& layoutBox, UsedHorizontalValues usedHorizontalValues, UsedVerticalValues usedVerticalValues)
zalan@apple.com4add9052018-11-02 14:23:54 +0000307{
308 ASSERT(!layoutBox.isContainer());
309 ASSERT(!layoutBox.establishesFormattingContext());
310 ASSERT(layoutBox.replaced());
311
zalan@apple.comdfa33722019-09-16 16:56:35 +0000312 computeBorderAndPadding(layoutBox, usedHorizontalValues);
313 computeWidthAndMargin(layoutBox, usedHorizontalValues);
zalan@apple.comf6eb9b02019-09-23 16:14:21 +0000314 computeHeightAndMargin(layoutBox, usedHorizontalValues, usedVerticalValues);
zalan@apple.com4add9052018-11-02 14:23:54 +0000315}
316
zalan@apple.com9ed00c02019-09-18 23:33:08 +0000317void InlineFormattingContext::collectInlineContent()
zalan@apple.com3ec85f32018-11-14 15:31:17 +0000318{
zalan@apple.com2bf37862019-05-25 20:14:09 +0000319 // Traverse the tree and create inline items out of containers and leaf nodes. This essentially turns the tree inline structure into a flat one.
320 // <span>text<span></span><img></span> -> [ContainerStart][InlineBox][ContainerStart][ContainerEnd][InlineBox][ContainerEnd]
321 auto& formattingState = this->formattingState();
zalan@apple.com57261ba2019-02-04 15:49:24 +0000322 LayoutQueue layoutQueue;
zalan@apple.com97eeb342019-09-13 19:15:48 +0000323 if (root().hasInFlowOrFloatingChild())
324 layoutQueue.append(root().firstInFlowOrFloatingChild());
zalan@apple.com57261ba2019-02-04 15:49:24 +0000325 while (!layoutQueue.isEmpty()) {
zalan@apple.com2bf37862019-05-25 20:14:09 +0000326 auto treatAsInlineContainer = [](auto& layoutBox) {
327 return is<Container>(layoutBox) && !layoutBox.establishesFormattingContext();
328 };
zalan@apple.com57261ba2019-02-04 15:49:24 +0000329 while (true) {
330 auto& layoutBox = *layoutQueue.last();
zalan@apple.com2bf37862019-05-25 20:14:09 +0000331 if (!treatAsInlineContainer(layoutBox))
zalan@apple.com57261ba2019-02-04 15:49:24 +0000332 break;
zalan@apple.com2bf37862019-05-25 20:14:09 +0000333 // This is the start of an inline container (e.g. <span>).
ysuzuki@apple.com1d8e24d2019-08-19 06:59:40 +0000334 formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::ContainerStart));
zalan@apple.com57261ba2019-02-04 15:49:24 +0000335 auto& container = downcast<Container>(layoutBox);
zalan@apple.com57261ba2019-02-04 15:49:24 +0000336 if (!container.hasInFlowOrFloatingChild())
337 break;
338 layoutQueue.append(container.firstInFlowOrFloatingChild());
339 }
340
341 while (!layoutQueue.isEmpty()) {
342 auto& layoutBox = *layoutQueue.takeLast();
zalan@apple.com2bf37862019-05-25 20:14:09 +0000343 // This is the end of an inline container (e.g. </span>).
344 if (treatAsInlineContainer(layoutBox))
ysuzuki@apple.com1d8e24d2019-08-19 06:59:40 +0000345 formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::ContainerEnd));
zalan@apple.com09509252019-08-03 16:09:47 +0000346 else if (layoutBox.isLineBreakBox())
ysuzuki@apple.com1d8e24d2019-08-19 06:59:40 +0000347 formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::HardLineBreak));
zalan@apple.com2bf37862019-05-25 20:14:09 +0000348 else if (layoutBox.isFloatingPositioned())
ysuzuki@apple.com1d8e24d2019-08-19 06:59:40 +0000349 formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::Float));
zalan@apple.com2bf37862019-05-25 20:14:09 +0000350 else {
zalan@apple.com09509252019-08-03 16:09:47 +0000351 ASSERT(layoutBox.isInlineLevelBox());
352 if (layoutBox.hasTextContent())
353 InlineTextItem::createAndAppendTextItems(formattingState.inlineItems(), layoutBox);
zalan@apple.com2bf37862019-05-25 20:14:09 +0000354 else
ysuzuki@apple.com1d8e24d2019-08-19 06:59:40 +0000355 formattingState.addInlineItem(makeUnique<InlineItem>(layoutBox, InlineItem::Type::Box));
zalan@apple.com57261ba2019-02-04 15:49:24 +0000356 }
357
358 if (auto* nextSibling = layoutBox.nextInFlowOrFloatingSibling()) {
359 layoutQueue.append(nextSibling);
360 break;
361 }
362 }
363 }
zalan@apple.com4add9052018-11-02 14:23:54 +0000364}
365
zalan@apple.comf0517792019-09-29 15:08:41 +0000366Line::InitialConstraints InlineFormattingContext::initialConstraintsForLine(UsedHorizontalValues usedHorizontalValues, const LayoutUnit lineLogicalTop)
367{
368 auto lineLogicalLeft = geometryForBox(root()).contentBoxLeft();
369 auto availableWidth = usedHorizontalValues.constraints.width;
370 auto lineIsConstrainedByFloat = false;
371
372 auto floatingContext = FloatingContext { root(), *this, formattingState().floatingState() };
373 // Check for intruding floats and adjust logical left/available width for this line accordingly.
374 if (!floatingContext.isEmpty()) {
375 auto floatConstraints = floatingContext.constraints({ lineLogicalTop });
376 // Check if these constraints actually put limitation on the line.
377 if (floatConstraints.left && floatConstraints.left->x <= lineLogicalLeft)
378 floatConstraints.left = { };
379
380 auto lineLogicalRight = geometryForBox(root()).contentBoxRight();
381 if (floatConstraints.right && floatConstraints.right->x >= lineLogicalRight)
382 floatConstraints.right = { };
383
384 lineIsConstrainedByFloat = floatConstraints.left || floatConstraints.right;
385
386 if (floatConstraints.left && floatConstraints.right) {
387 ASSERT(floatConstraints.left->x <= floatConstraints.right->x);
388 availableWidth = floatConstraints.right->x - floatConstraints.left->x;
389 lineLogicalLeft = floatConstraints.left->x;
390 } else if (floatConstraints.left) {
391 ASSERT(floatConstraints.left->x >= lineLogicalLeft);
392 availableWidth -= (floatConstraints.left->x - lineLogicalLeft);
393 lineLogicalLeft = floatConstraints.left->x;
394 } else if (floatConstraints.right) {
395 ASSERT(floatConstraints.right->x >= lineLogicalLeft);
396 availableWidth = floatConstraints.right->x - lineLogicalLeft;
397 }
398 }
399 return Line::InitialConstraints { { lineLogicalLeft, lineLogicalTop }, availableWidth, lineIsConstrainedByFloat, quirks().lineHeightConstraints(root()) };
400}
401
402void InlineFormattingContext::setDisplayBoxesForLine(const LineLayout::LineContent& lineContent, UsedHorizontalValues usedHorizontalValues)
403{
404 auto& formattingState = this->formattingState();
405
406 if (!lineContent.floats.isEmpty()) {
407 auto floatingContext = FloatingContext { root(), *this, formattingState.floatingState() };
408 // Move floats to their final position.
409 for (const auto& floatItem : lineContent.floats) {
410 auto& floatBox = floatItem->layoutBox();
411 auto& displayBox = formattingState.displayBox(floatBox);
412 // Set static position first.
413 auto& lineBox = lineContent.lineBox;
414 displayBox.setTopLeft({ lineBox.logicalLeft(), lineBox.logicalTop() });
415 // Float it.
416 displayBox.setTopLeft(floatingContext.positionForFloat(floatBox));
417 floatingContext.append(floatBox);
418 }
419 }
420
421 // Add final display runs to state.
422 for (auto& lineRun : lineContent.runList) {
423 // Inline level containers (<span>) don't generate inline runs.
424 if (lineRun->isContainerStart() || lineRun->isContainerEnd())
425 continue;
426 // Collapsed line runs don't generate display runs.
427 if (lineRun->isVisuallyEmpty())
428 continue;
429 formattingState.addInlineRun(lineRun->displayRun());
430 }
431
432 // Compute box final geometry.
433 auto& lineRuns = lineContent.runList;
434 for (unsigned index = 0; index < lineRuns.size(); ++index) {
435 auto& lineRun = lineRuns.at(index);
436 auto& logicalRect = lineRun->logicalRect();
437 auto& layoutBox = lineRun->layoutBox();
438 auto& displayBox = formattingState.displayBox(layoutBox);
439
440 if (lineRun->isLineBreak()) {
441 displayBox.setTopLeft(logicalRect.topLeft());
442 displayBox.setContentBoxWidth(logicalRect.width());
443 displayBox.setContentBoxHeight(logicalRect.height());
444 continue;
445 }
446
447 // Inline level box (replaced or inline-block)
448 if (lineRun->isBox()) {
449 auto topLeft = logicalRect.topLeft();
450 if (layoutBox.isInFlowPositioned())
451 topLeft += geometry().inFlowPositionedPositionOffset(layoutBox, usedHorizontalValues);
452 displayBox.setTopLeft(topLeft);
453 continue;
454 }
455
456 // Inline level container start (<span>)
457 if (lineRun->isContainerStart()) {
458 displayBox.setTopLeft(logicalRect.topLeft());
459 continue;
460 }
461
462 // Inline level container end (</span>)
463 if (lineRun->isContainerEnd()) {
464 if (layoutBox.isInFlowPositioned()) {
465 auto inflowOffset = geometry().inFlowPositionedPositionOffset(layoutBox, usedHorizontalValues);
466 displayBox.moveHorizontally(inflowOffset.width());
467 displayBox.moveVertically(inflowOffset.height());
468 }
469 auto marginBoxWidth = logicalRect.left() - displayBox.left();
470 auto contentBoxWidth = marginBoxWidth - (displayBox.marginStart() + displayBox.borderLeft() + displayBox.paddingLeft().valueOr(0));
471 // FIXME fix it for multiline.
472 displayBox.setContentBoxWidth(contentBoxWidth);
473 displayBox.setContentBoxHeight(logicalRect.height());
474 continue;
475 }
476
477 if (lineRun->isText()) {
478 const Line::Run* previousLineRun = !index ? nullptr : lineRuns[index - 1].get();
479 // FIXME take content breaking into account when part of the layout box is on the previous line.
480 auto firstInlineRunForLayoutBox = !previousLineRun || &previousLineRun->layoutBox() != &layoutBox;
481 auto logicalWidth = lineRun->isVisuallyEmpty() ? LayoutUnit() : logicalRect.width();
482 if (firstInlineRunForLayoutBox) {
483 // Setup display box for the associated layout box.
484 displayBox.setTopLeft(logicalRect.topLeft());
485 displayBox.setContentBoxWidth(logicalWidth);
486 displayBox.setContentBoxHeight(logicalRect.height());
487 } else {
488 // FIXME fix it for multirun/multiline.
489 displayBox.setContentBoxWidth(displayBox.contentBoxWidth() + logicalWidth);
490 }
491 continue;
492 }
493 ASSERT_NOT_REACHED();
494 }
495 formattingState.addLineBox(lineContent.lineBox);
496}
497
zalan@apple.comce6567c2018-04-25 22:54:01 +0000498}
499}
500
501#endif