1 | /* |
2 | * Copyright (C) 2011 Google 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 are |
6 | * met: |
7 | * |
8 | * * Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * * Redistributions in binary form must reproduce the above |
11 | * copyright notice, this list of conditions and the following disclaimer |
12 | * in the documentation and/or other materials provided with the |
13 | * distribution. |
14 | * * Neither the name of Google Inc. nor the names of its |
15 | * contributors may be used to endorse or promote products derived from |
16 | * this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | |
31 | #include "config.h" |
32 | #include "RenderFlexibleBox.h" |
33 | |
34 | #include "FlexibleBoxAlgorithm.h" |
35 | #include "LayoutRepainter.h" |
36 | #include "RenderChildIterator.h" |
37 | #include "RenderLayer.h" |
38 | #include "RenderLayoutState.h" |
39 | #include "RenderView.h" |
40 | #include "RuntimeEnabledFeatures.h" |
41 | #include <limits> |
42 | #include <wtf/IsoMallocInlines.h> |
43 | #include <wtf/MathExtras.h> |
44 | |
45 | namespace WebCore { |
46 | |
47 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderFlexibleBox); |
48 | |
49 | struct RenderFlexibleBox::LineContext { |
50 | LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, LayoutUnit maxAscent, Vector<FlexItem>&& flexItems) |
51 | : crossAxisOffset(crossAxisOffset) |
52 | , crossAxisExtent(crossAxisExtent) |
53 | , maxAscent(maxAscent) |
54 | , flexItems(flexItems) |
55 | { |
56 | } |
57 | |
58 | LayoutUnit crossAxisOffset; |
59 | LayoutUnit crossAxisExtent; |
60 | LayoutUnit maxAscent; |
61 | Vector<FlexItem> flexItems; |
62 | }; |
63 | |
64 | RenderFlexibleBox::RenderFlexibleBox(Element& element, RenderStyle&& style) |
65 | : RenderBlock(element, WTFMove(style), 0) |
66 | { |
67 | setChildrenInline(false); // All of our children must be block-level. |
68 | } |
69 | |
70 | RenderFlexibleBox::RenderFlexibleBox(Document& document, RenderStyle&& style) |
71 | : RenderBlock(document, WTFMove(style), 0) |
72 | { |
73 | setChildrenInline(false); // All of our children must be block-level. |
74 | } |
75 | |
76 | RenderFlexibleBox::~RenderFlexibleBox() = default; |
77 | |
78 | const char* RenderFlexibleBox::renderName() const |
79 | { |
80 | return "RenderFlexibleBox" ; |
81 | } |
82 | |
83 | void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const |
84 | { |
85 | LayoutUnit childMinWidth; |
86 | LayoutUnit childMaxWidth; |
87 | bool hadExcludedChildren = computePreferredWidthsForExcludedChildren(childMinWidth, childMaxWidth); |
88 | |
89 | // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start |
90 | // honoring it though until the flex shorthand stops setting it to 0. See |
91 | // https://bugs.webkit.org/show_bug.cgi?id=116117 and |
92 | // https://crbug.com/240765. |
93 | for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { |
94 | if (child->isOutOfFlowPositioned() || child->isExcludedFromNormalLayout()) |
95 | continue; |
96 | |
97 | LayoutUnit margin = marginIntrinsicLogicalWidthForChild(*child); |
98 | |
99 | LayoutUnit minPreferredLogicalWidth; |
100 | LayoutUnit maxPreferredLogicalWidth; |
101 | computeChildPreferredLogicalWidths(*child, minPreferredLogicalWidth, maxPreferredLogicalWidth); |
102 | |
103 | minPreferredLogicalWidth += margin; |
104 | maxPreferredLogicalWidth += margin; |
105 | |
106 | if (!isColumnFlow()) { |
107 | maxLogicalWidth += maxPreferredLogicalWidth; |
108 | if (isMultiline()) { |
109 | // For multiline, the min preferred width is if you put a break between |
110 | // each item. |
111 | minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth); |
112 | } else |
113 | minLogicalWidth += minPreferredLogicalWidth; |
114 | } else { |
115 | minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth); |
116 | maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth); |
117 | } |
118 | } |
119 | |
120 | maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); |
121 | |
122 | // Due to negative margins, it is possible that we calculated a negative |
123 | // intrinsic width. Make sure that we never return a negative width. |
124 | minLogicalWidth = std::max(0_lu, minLogicalWidth); |
125 | maxLogicalWidth = std::max(0_lu, maxLogicalWidth); |
126 | |
127 | if (hadExcludedChildren) { |
128 | minLogicalWidth = std::max(minLogicalWidth, childMinWidth); |
129 | maxLogicalWidth = std::max(maxLogicalWidth, childMaxWidth); |
130 | } |
131 | |
132 | LayoutUnit scrollbarWidth(scrollbarLogicalWidth()); |
133 | maxLogicalWidth += scrollbarWidth; |
134 | minLogicalWidth += scrollbarWidth; |
135 | } |
136 | |
137 | void RenderFlexibleBox::computePreferredLogicalWidths() |
138 | { |
139 | ASSERT(preferredLogicalWidthsDirty()); |
140 | |
141 | m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; |
142 | |
143 | const RenderStyle& styleToUse = style(); |
144 | // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for width. |
145 | if (styleToUse.logicalWidth().isFixed() && styleToUse.logicalWidth().value() > 0) |
146 | m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalWidth().value()); |
147 | else |
148 | computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); |
149 | |
150 | // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width. |
151 | if (styleToUse.logicalMinWidth().isFixed() && styleToUse.logicalMinWidth().value() > 0) { |
152 | m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value())); |
153 | m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value())); |
154 | } |
155 | |
156 | // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth. |
157 | if (styleToUse.logicalMaxWidth().isFixed()) { |
158 | m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); |
159 | m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); |
160 | } |
161 | |
162 | LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); |
163 | m_minPreferredLogicalWidth += borderAndPadding; |
164 | m_maxPreferredLogicalWidth += borderAndPadding; |
165 | |
166 | setPreferredLogicalWidthsDirty(false); |
167 | } |
168 | |
169 | static int synthesizedBaselineFromBorderBox(const RenderBox& box, LineDirectionMode direction) |
170 | { |
171 | return (direction == HorizontalLine ? box.size().height() : box.size().width()).toInt(); |
172 | } |
173 | |
174 | int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode) const |
175 | { |
176 | auto baseline = firstLineBaseline(); |
177 | if (!baseline) |
178 | return synthesizedBaselineFromBorderBox(*this, direction) + marginLogicalHeight(); |
179 | |
180 | return baseline.value() + (direction == HorizontalLine ? marginTop() : marginRight()).toInt(); |
181 | } |
182 | |
183 | Optional<int> RenderFlexibleBox::firstLineBaseline() const |
184 | { |
185 | if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0) |
186 | return Optional<int>(); |
187 | RenderBox* baselineChild = nullptr; |
188 | int childNumber = 0; |
189 | for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { |
190 | if (m_orderIterator.shouldSkipChild(*child)) |
191 | continue; |
192 | if (alignmentForChild(*child) == ItemPosition::Baseline && !hasAutoMarginsInCrossAxis(*child)) { |
193 | baselineChild = child; |
194 | break; |
195 | } |
196 | if (!baselineChild) |
197 | baselineChild = child; |
198 | |
199 | ++childNumber; |
200 | if (childNumber == m_numberOfInFlowChildrenOnFirstLine) |
201 | break; |
202 | } |
203 | |
204 | if (!baselineChild) |
205 | return Optional<int>(); |
206 | |
207 | if (!isColumnFlow() && hasOrthogonalFlow(*baselineChild)) |
208 | return Optional<int>(crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop()); |
209 | if (isColumnFlow() && !hasOrthogonalFlow(*baselineChild)) |
210 | return Optional<int>(mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop()); |
211 | |
212 | Optional<int> baseline = baselineChild->firstLineBaseline(); |
213 | if (!baseline) { |
214 | // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root. |
215 | // This would also fix some cases where the flexbox is orthogonal to its container. |
216 | LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine; |
217 | return Optional<int>(synthesizedBaselineFromBorderBox(*baselineChild, direction) + baselineChild->logicalTop()); |
218 | } |
219 | |
220 | return Optional<int>(baseline.value() + baselineChild->logicalTop()); |
221 | } |
222 | |
223 | Optional<int> RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode) const |
224 | { |
225 | return firstLineBaseline(); |
226 | } |
227 | |
228 | static const StyleContentAlignmentData& contentAlignmentNormalBehavior() |
229 | { |
230 | // The justify-content property applies along the main axis, but since |
231 | // flexing in the main axis is controlled by flex, stretch behaves as |
232 | // flex-start (ignoring the specified fallback alignment, if any). |
233 | // https://drafts.csswg.org/css-align/#distribution-flex |
234 | static const StyleContentAlignmentData normalBehavior = { ContentPosition::Normal, ContentDistribution::Stretch}; |
235 | return normalBehavior; |
236 | } |
237 | |
238 | void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
239 | { |
240 | RenderBlock::styleDidChange(diff, oldStyle); |
241 | if (!oldStyle || diff != StyleDifference::Layout) |
242 | return; |
243 | |
244 | if (oldStyle->resolvedAlignItems(selfAlignmentNormalBehavior()).position() == ItemPosition::Stretch) { |
245 | // Flex items that were previously stretching need to be relayed out so we |
246 | // can compute new available cross axis space. This is only necessary for |
247 | // stretching since other alignment values don't change the size of the |
248 | // box. |
249 | for (auto& child : childrenOfType<RenderBox>(*this)) { |
250 | ItemPosition previousAlignment = child.style().resolvedAlignSelf(oldStyle, selfAlignmentNormalBehavior()).position(); |
251 | if (previousAlignment == ItemPosition::Stretch && previousAlignment != child.style().resolvedAlignSelf(&style(), selfAlignmentNormalBehavior()).position()) |
252 | child.setChildNeedsLayout(MarkOnlyThis); |
253 | } |
254 | } |
255 | } |
256 | |
257 | void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) |
258 | { |
259 | ASSERT(needsLayout()); |
260 | |
261 | if (!relayoutChildren && simplifiedLayout()) |
262 | return; |
263 | |
264 | LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); |
265 | |
266 | m_relaidOutChildren.clear(); |
267 | |
268 | bool oldInLayout = m_inLayout; |
269 | m_inLayout = true; |
270 | |
271 | if (recomputeLogicalWidth()) |
272 | relayoutChildren = true; |
273 | |
274 | LayoutUnit previousHeight = logicalHeight(); |
275 | setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight()); |
276 | { |
277 | LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); |
278 | |
279 | preparePaginationBeforeBlockLayout(relayoutChildren); |
280 | |
281 | m_numberOfInFlowChildrenOnFirstLine = -1; |
282 | |
283 | beginUpdateScrollInfoAfterLayoutTransaction(); |
284 | |
285 | prepareOrderIteratorAndMargins(); |
286 | |
287 | // Fieldsets need to find their legend and position it inside the border of the object. |
288 | // The legend then gets skipped during normal layout. The same is true for ruby text. |
289 | // It doesn't get included in the normal layout process but is instead skipped. |
290 | layoutExcludedChildren(relayoutChildren); |
291 | |
292 | ChildFrameRects oldChildRects; |
293 | appendChildFrameRects(oldChildRects); |
294 | |
295 | layoutFlexItems(relayoutChildren); |
296 | |
297 | endAndCommitUpdateScrollInfoAfterLayoutTransaction(); |
298 | |
299 | if (logicalHeight() != previousHeight) |
300 | relayoutChildren = true; |
301 | |
302 | layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer()); |
303 | |
304 | repaintChildrenDuringLayoutIfMoved(oldChildRects); |
305 | // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to. |
306 | computeOverflow(clientLogicalBottomAfterRepositioning()); |
307 | } |
308 | updateLayerTransform(); |
309 | |
310 | // We have to reset this, because changes to our ancestors' style can affect |
311 | // this value. Also, this needs to be before we call updateAfterLayout, as |
312 | // that function may re-enter this one. |
313 | m_hasDefiniteHeight = SizeDefiniteness::Unknown; |
314 | |
315 | // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if we overflow or not. |
316 | updateScrollInfoAfterLayout(); |
317 | |
318 | repainter.repaintAfterLayout(); |
319 | |
320 | clearNeedsLayout(); |
321 | |
322 | m_inLayout = oldInLayout; |
323 | } |
324 | |
325 | void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects) |
326 | { |
327 | for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { |
328 | if (!child->isOutOfFlowPositioned()) |
329 | childFrameRects.append(child->frameRect()); |
330 | } |
331 | } |
332 | |
333 | void RenderFlexibleBox::repaintChildrenDuringLayoutIfMoved(const ChildFrameRects& oldChildRects) |
334 | { |
335 | size_t childIndex = 0; |
336 | for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { |
337 | if (child->isOutOfFlowPositioned()) |
338 | continue; |
339 | |
340 | // If the child moved, we have to repaint it as well as any floating/positioned |
341 | // descendants. An exception is if we need a layout. In this case, we know we're going to |
342 | // repaint ourselves (and the child) anyway. |
343 | if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) |
344 | child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]); |
345 | ++childIndex; |
346 | } |
347 | ASSERT(childIndex == oldChildRects.size()); |
348 | } |
349 | |
350 | void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) |
351 | { |
352 | for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { |
353 | if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect, PaintAsInlineBlock)) |
354 | return; |
355 | } |
356 | } |
357 | |
358 | void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts) |
359 | { |
360 | LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? 0_lu : lineContexts[0].crossAxisOffset; |
361 | alignFlexLines(lineContexts); |
362 | |
363 | alignChildren(lineContexts); |
364 | |
365 | if (style().flexWrap() == FlexWrap::Reverse) |
366 | flipForWrapReverse(lineContexts, crossAxisStartEdge); |
367 | |
368 | // direction:rtl + flex-direction:column means the cross-axis direction is |
369 | // flipped. |
370 | flipForRightToLeftColumn(lineContexts); |
371 | } |
372 | |
373 | LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning() |
374 | { |
375 | LayoutUnit maxChildLogicalBottom; |
376 | for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { |
377 | if (child->isOutOfFlowPositioned()) |
378 | continue; |
379 | LayoutUnit childLogicalBottom = logicalTopForChild(*child) + logicalHeightForChild(*child) + marginAfterForChild(*child); |
380 | maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom); |
381 | } |
382 | return std::max(clientLogicalBottom(), maxChildLogicalBottom + paddingAfter()); |
383 | } |
384 | |
385 | bool RenderFlexibleBox::hasOrthogonalFlow(const RenderBox& child) const |
386 | { |
387 | return isHorizontalFlow() != child.isHorizontalWritingMode(); |
388 | } |
389 | |
390 | bool RenderFlexibleBox::isColumnFlow() const |
391 | { |
392 | return style().isColumnFlexDirection(); |
393 | } |
394 | |
395 | bool RenderFlexibleBox::isHorizontalFlow() const |
396 | { |
397 | if (isHorizontalWritingMode()) |
398 | return !isColumnFlow(); |
399 | return isColumnFlow(); |
400 | } |
401 | |
402 | bool RenderFlexibleBox::isLeftToRightFlow() const |
403 | { |
404 | if (isColumnFlow()) |
405 | return style().writingMode() == TopToBottomWritingMode || style().writingMode() == LeftToRightWritingMode; |
406 | return style().isLeftToRightDirection() ^ (style().flexDirection() == FlexDirection::RowReverse); |
407 | } |
408 | |
409 | bool RenderFlexibleBox::isMultiline() const |
410 | { |
411 | return style().flexWrap() != FlexWrap::NoWrap; |
412 | } |
413 | |
414 | Length RenderFlexibleBox::flexBasisForChild(const RenderBox& child) const |
415 | { |
416 | Length flexLength = child.style().flexBasis(); |
417 | if (flexLength.isAuto()) |
418 | flexLength = isHorizontalFlow() ? child.style().width() : child.style().height(); |
419 | return flexLength; |
420 | } |
421 | |
422 | LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(const RenderBox& child) const |
423 | { |
424 | return isHorizontalFlow() ? child.height() : child.width(); |
425 | } |
426 | |
427 | LayoutUnit RenderFlexibleBox::cachedChildIntrinsicContentLogicalHeight(const RenderBox& child) const |
428 | { |
429 | if (child.isRenderReplaced()) |
430 | return downcast<RenderReplaced>(child).intrinsicLogicalHeight(); |
431 | |
432 | if (m_intrinsicContentLogicalHeights.contains(&child)) |
433 | return m_intrinsicContentLogicalHeights.get(&child); |
434 | |
435 | return child.contentLogicalHeight(); |
436 | } |
437 | |
438 | void RenderFlexibleBox::setCachedChildIntrinsicContentLogicalHeight(const RenderBox& child, LayoutUnit height) |
439 | { |
440 | if (child.isRenderReplaced()) |
441 | return; // Replaced elements know their intrinsic height already, so save space by not caching. |
442 | m_intrinsicContentLogicalHeights.set(&child, height); |
443 | } |
444 | |
445 | void RenderFlexibleBox::clearCachedChildIntrinsicContentLogicalHeight(const RenderBox& child) |
446 | { |
447 | if (child.isRenderReplaced()) |
448 | return; // Replaced elements know their intrinsic height already, so nothing to do. |
449 | m_intrinsicContentLogicalHeights.remove(&child); |
450 | } |
451 | |
452 | LayoutUnit RenderFlexibleBox::childIntrinsicLogicalHeight(const RenderBox& child) const |
453 | { |
454 | // This should only be called if the logical height is the cross size |
455 | ASSERT(!hasOrthogonalFlow(child)); |
456 | if (needToStretchChildLogicalHeight(child)) { |
457 | LayoutUnit childContentHeight = cachedChildIntrinsicContentLogicalHeight(child); |
458 | LayoutUnit childLogicalHeight = childContentHeight + child.scrollbarLogicalHeight() + child.borderAndPaddingLogicalHeight(); |
459 | return child.constrainLogicalHeightByMinMax(childLogicalHeight, childContentHeight); |
460 | } |
461 | return child.logicalHeight(); |
462 | } |
463 | |
464 | LayoutUnit RenderFlexibleBox::childIntrinsicLogicalWidth(const RenderBox& child) const |
465 | { |
466 | // This should only be called if the logical width is the cross size |
467 | ASSERT(hasOrthogonalFlow(child)); |
468 | // If our height is auto, make sure that our returned height is unaffected by |
469 | // earlier layouts by returning the max preferred logical width |
470 | if (!crossAxisLengthIsDefinite(child, child.style().logicalWidth())) |
471 | return child.maxPreferredLogicalWidth(); |
472 | return child.logicalWidth(); |
473 | } |
474 | |
475 | LayoutUnit RenderFlexibleBox::crossAxisIntrinsicExtentForChild(const RenderBox& child) const |
476 | { |
477 | return hasOrthogonalFlow(child) ? childIntrinsicLogicalWidth(child) : childIntrinsicLogicalHeight(child); |
478 | } |
479 | |
480 | LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(const RenderBox& child) const |
481 | { |
482 | return isHorizontalFlow() ? child.size().width() : child.size().height(); |
483 | } |
484 | |
485 | LayoutUnit RenderFlexibleBox::mainAxisContentExtentForChildIncludingScrollbar(const RenderBox& child) const |
486 | { |
487 | return isHorizontalFlow() ? child.contentWidth() + child.verticalScrollbarWidth() : child.contentHeight() + child.horizontalScrollbarHeight(); |
488 | } |
489 | |
490 | LayoutUnit RenderFlexibleBox::crossAxisExtent() const |
491 | { |
492 | return isHorizontalFlow() ? size().height() : size().width(); |
493 | } |
494 | |
495 | LayoutUnit RenderFlexibleBox::mainAxisExtent() const |
496 | { |
497 | return isHorizontalFlow() ? size().width() : size().height(); |
498 | } |
499 | |
500 | LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const |
501 | { |
502 | return isHorizontalFlow() ? contentHeight() : contentWidth(); |
503 | } |
504 | |
505 | LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight) |
506 | { |
507 | if (isColumnFlow()) { |
508 | LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(); |
509 | LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar; |
510 | auto computedValues = computeLogicalHeight(borderBoxLogicalHeight, logicalTop()); |
511 | if (computedValues.m_extent == LayoutUnit::max()) |
512 | return computedValues.m_extent; |
513 | return std::max(0_lu, computedValues.m_extent - borderPaddingAndScrollbar); |
514 | } |
515 | return contentLogicalWidth(); |
516 | } |
517 | |
518 | Optional<LayoutUnit> RenderFlexibleBox::computeMainAxisExtentForChild(const RenderBox& child, SizeType sizeType, const Length& size) |
519 | { |
520 | // If we have a horizontal flow, that means the main size is the width. |
521 | // That's the logical width for horizontal writing modes, and the logical |
522 | // height in vertical writing modes. For a vertical flow, main size is the |
523 | // height, so it's the inverse. So we need the logical width if we have a |
524 | // horizontal flow and horizontal writing mode, or vertical flow and vertical |
525 | // writing mode. Otherwise we need the logical height. |
526 | if (isHorizontalFlow() != child.style().isHorizontalWritingMode()) { |
527 | // We don't have to check for "auto" here - computeContentLogicalHeight |
528 | // will just return a null Optional for that case anyway. It's safe to access |
529 | // scrollbarLogicalHeight here because ComputeNextFlexLine will have |
530 | // already forced layout on the child. We previously did a layout out the child |
531 | // if necessary (see ComputeNextFlexLine and the call to |
532 | // childHasIntrinsicMainAxisSize) so we can be sure that the two height |
533 | // calls here will return up-to-date data. |
534 | Optional<LayoutUnit> height = child.computeContentLogicalHeight(sizeType, size, cachedChildIntrinsicContentLogicalHeight(child)); |
535 | if (!height) |
536 | return height; |
537 | return height.value() + child.scrollbarLogicalHeight(); |
538 | } |
539 | |
540 | // computeLogicalWidth always re-computes the intrinsic widths. However, when |
541 | // our logical width is auto, we can just use our cached value. So let's do |
542 | // that here. (Compare code in LayoutBlock::computePreferredLogicalWidths) |
543 | LayoutUnit borderAndPadding = child.borderAndPaddingLogicalWidth(); |
544 | if (child.style().logicalWidth().isAuto() && !child.hasAspectRatio()) { |
545 | if (size.type() == MinContent) |
546 | return child.minPreferredLogicalWidth() - borderAndPadding; |
547 | if (size.type() == MaxContent) |
548 | return child.maxPreferredLogicalWidth() - borderAndPadding; |
549 | } |
550 | |
551 | // FIXME: Figure out how this should work for regions and pass in the appropriate values. |
552 | RenderFragmentContainer* fragment = nullptr; |
553 | return child.computeLogicalWidthInFragmentUsing(sizeType, size, contentLogicalWidth(), *this, fragment) - borderAndPadding; |
554 | } |
555 | |
556 | |
557 | WritingMode RenderFlexibleBox::transformedWritingMode() const |
558 | { |
559 | WritingMode mode = style().writingMode(); |
560 | if (!isColumnFlow()) |
561 | return mode; |
562 | |
563 | switch (mode) { |
564 | case TopToBottomWritingMode: |
565 | case BottomToTopWritingMode: |
566 | return style().isLeftToRightDirection() ? LeftToRightWritingMode : RightToLeftWritingMode; |
567 | case LeftToRightWritingMode: |
568 | case RightToLeftWritingMode: |
569 | return style().isLeftToRightDirection() ? TopToBottomWritingMode : BottomToTopWritingMode; |
570 | } |
571 | ASSERT_NOT_REACHED(); |
572 | return TopToBottomWritingMode; |
573 | } |
574 | |
575 | LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const |
576 | { |
577 | if (isHorizontalFlow()) |
578 | return isLeftToRightFlow() ? borderLeft() : borderRight(); |
579 | return isLeftToRightFlow() ? borderTop() : borderBottom(); |
580 | } |
581 | |
582 | LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const |
583 | { |
584 | if (isHorizontalFlow()) |
585 | return isLeftToRightFlow() ? borderRight() : borderLeft(); |
586 | return isLeftToRightFlow() ? borderBottom() : borderTop(); |
587 | } |
588 | |
589 | LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const |
590 | { |
591 | switch (transformedWritingMode()) { |
592 | case TopToBottomWritingMode: |
593 | return borderTop(); |
594 | case BottomToTopWritingMode: |
595 | return borderBottom(); |
596 | case LeftToRightWritingMode: |
597 | return borderLeft(); |
598 | case RightToLeftWritingMode: |
599 | return borderRight(); |
600 | } |
601 | ASSERT_NOT_REACHED(); |
602 | return borderTop(); |
603 | } |
604 | |
605 | LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const |
606 | { |
607 | switch (transformedWritingMode()) { |
608 | case TopToBottomWritingMode: |
609 | return borderBottom(); |
610 | case BottomToTopWritingMode: |
611 | return borderTop(); |
612 | case LeftToRightWritingMode: |
613 | return borderRight(); |
614 | case RightToLeftWritingMode: |
615 | return borderLeft(); |
616 | } |
617 | ASSERT_NOT_REACHED(); |
618 | return borderTop(); |
619 | } |
620 | |
621 | LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const |
622 | { |
623 | if (isHorizontalFlow()) |
624 | return isLeftToRightFlow() ? paddingLeft() : paddingRight(); |
625 | return isLeftToRightFlow() ? paddingTop() : paddingBottom(); |
626 | } |
627 | |
628 | LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const |
629 | { |
630 | if (isHorizontalFlow()) |
631 | return isLeftToRightFlow() ? paddingRight() : paddingLeft(); |
632 | return isLeftToRightFlow() ? paddingBottom() : paddingTop(); |
633 | } |
634 | |
635 | LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const |
636 | { |
637 | switch (transformedWritingMode()) { |
638 | case TopToBottomWritingMode: |
639 | return paddingTop(); |
640 | case BottomToTopWritingMode: |
641 | return paddingBottom(); |
642 | case LeftToRightWritingMode: |
643 | return paddingLeft(); |
644 | case RightToLeftWritingMode: |
645 | return paddingRight(); |
646 | } |
647 | ASSERT_NOT_REACHED(); |
648 | return paddingTop(); |
649 | } |
650 | |
651 | LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const |
652 | { |
653 | switch (transformedWritingMode()) { |
654 | case TopToBottomWritingMode: |
655 | return paddingBottom(); |
656 | case BottomToTopWritingMode: |
657 | return paddingTop(); |
658 | case LeftToRightWritingMode: |
659 | return paddingRight(); |
660 | case RightToLeftWritingMode: |
661 | return paddingLeft(); |
662 | } |
663 | ASSERT_NOT_REACHED(); |
664 | return paddingTop(); |
665 | } |
666 | |
667 | LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(const RenderBox& child) const |
668 | { |
669 | if (isHorizontalFlow()) |
670 | return isLeftToRightFlow() ? child.marginLeft() : child.marginRight(); |
671 | return isLeftToRightFlow() ? child.marginTop() : child.marginBottom(); |
672 | } |
673 | |
674 | LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(const RenderBox& child) const |
675 | { |
676 | if (isHorizontalFlow()) |
677 | return isLeftToRightFlow() ? child.marginRight() : child.marginLeft(); |
678 | return isLeftToRightFlow() ? child.marginBottom() : child.marginTop(); |
679 | } |
680 | |
681 | LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(const RenderBox& child) const |
682 | { |
683 | switch (transformedWritingMode()) { |
684 | case TopToBottomWritingMode: |
685 | return child.marginTop(); |
686 | case BottomToTopWritingMode: |
687 | return child.marginBottom(); |
688 | case LeftToRightWritingMode: |
689 | return child.marginLeft(); |
690 | case RightToLeftWritingMode: |
691 | return child.marginRight(); |
692 | } |
693 | ASSERT_NOT_REACHED(); |
694 | return marginTop(); |
695 | } |
696 | |
697 | LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(const RenderBox& child) const |
698 | { |
699 | return isHorizontalFlow() ? child.verticalMarginExtent() : child.horizontalMarginExtent(); |
700 | } |
701 | |
702 | LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const |
703 | { |
704 | return isHorizontalFlow() ? horizontalScrollbarHeight() : verticalScrollbarWidth(); |
705 | } |
706 | |
707 | LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(const RenderBox& child) const |
708 | { |
709 | return isHorizontalFlow() ? child.location() : child.location().transposedPoint(); |
710 | } |
711 | |
712 | bool RenderFlexibleBox::useChildAspectRatio(const RenderBox& child) const |
713 | { |
714 | if (!child.hasAspectRatio()) |
715 | return false; |
716 | if (!child.intrinsicSize().height()) { |
717 | // We can't compute a ratio in this case. |
718 | return false; |
719 | } |
720 | Length crossSize; |
721 | if (isHorizontalFlow()) |
722 | crossSize = child.style().height(); |
723 | else |
724 | crossSize = child.style().width(); |
725 | return crossAxisLengthIsDefinite(child, crossSize); |
726 | } |
727 | |
728 | |
729 | LayoutUnit RenderFlexibleBox::computeMainSizeFromAspectRatioUsing(const RenderBox& child, Length crossSizeLength) const |
730 | { |
731 | ASSERT(child.hasAspectRatio()); |
732 | ASSERT(child.intrinsicSize().height()); |
733 | |
734 | Optional<LayoutUnit> crossSize; |
735 | if (crossSizeLength.isFixed()) |
736 | crossSize = LayoutUnit(crossSizeLength.value()); |
737 | else { |
738 | ASSERT(crossSizeLength.isPercentOrCalculated()); |
739 | crossSize = hasOrthogonalFlow(child) ? adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(crossSizeLength, contentWidth())) : child.computePercentageLogicalHeight(crossSizeLength); |
740 | if (!crossSize) |
741 | return 0_lu; |
742 | } |
743 | |
744 | const LayoutSize& childIntrinsicSize = child.intrinsicSize(); |
745 | double ratio = childIntrinsicSize.width().toFloat() / |
746 | childIntrinsicSize.height().toFloat(); |
747 | if (isHorizontalFlow()) |
748 | return LayoutUnit(crossSize.value() * ratio); |
749 | return LayoutUnit(crossSize.value() / ratio); |
750 | } |
751 | |
752 | void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const LayoutPoint& location) |
753 | { |
754 | if (isHorizontalFlow()) |
755 | child.setLocation(location); |
756 | else |
757 | child.setLocation(location.transposedPoint()); |
758 | } |
759 | |
760 | bool RenderFlexibleBox::mainAxisLengthIsDefinite(const RenderBox& child, const Length& flexBasis) const |
761 | { |
762 | if (flexBasis.isAuto()) |
763 | return false; |
764 | if (flexBasis.isPercentOrCalculated()) { |
765 | if (!isColumnFlow() || m_hasDefiniteHeight == SizeDefiniteness::Definite) |
766 | return true; |
767 | if (m_hasDefiniteHeight == SizeDefiniteness::Indefinite) |
768 | return false; |
769 | bool definite = child.computePercentageLogicalHeight(flexBasis) != WTF::nullopt; |
770 | if (m_inLayout) { |
771 | // We can reach this code even while we're not laying ourselves out, such |
772 | // as from mainSizeForPercentageResolution. |
773 | m_hasDefiniteHeight = definite ? SizeDefiniteness::Definite : SizeDefiniteness::Indefinite; |
774 | } |
775 | return definite; |
776 | } |
777 | return true; |
778 | } |
779 | |
780 | bool RenderFlexibleBox::crossAxisLengthIsDefinite(const RenderBox& child, const Length& length) const |
781 | { |
782 | if (length.isAuto()) |
783 | return false; |
784 | if (length.isPercentOrCalculated()) { |
785 | if (hasOrthogonalFlow(child) || m_hasDefiniteHeight == SizeDefiniteness::Definite) |
786 | return true; |
787 | if (m_hasDefiniteHeight == SizeDefiniteness::Indefinite) |
788 | return false; |
789 | bool definite = bool(child.computePercentageLogicalHeight(length)); |
790 | m_hasDefiniteHeight = definite ? SizeDefiniteness::Definite : SizeDefiniteness::Indefinite; |
791 | return definite; |
792 | } |
793 | // FIXME: Eventually we should support other types of sizes here. |
794 | // Requires updating computeMainSizeFromAspectRatioUsing. |
795 | return length.isFixed(); |
796 | } |
797 | |
798 | void RenderFlexibleBox::cacheChildMainSize(const RenderBox& child) |
799 | { |
800 | ASSERT(!child.needsLayout()); |
801 | LayoutUnit mainSize; |
802 | if (hasOrthogonalFlow(child)) |
803 | mainSize = child.logicalHeight(); |
804 | else |
805 | mainSize = child.maxPreferredLogicalWidth(); |
806 | m_intrinsicSizeAlongMainAxis.set(&child, mainSize); |
807 | m_relaidOutChildren.add(&child); |
808 | } |
809 | |
810 | void RenderFlexibleBox::clearCachedMainSizeForChild(const RenderBox& child) |
811 | { |
812 | m_intrinsicSizeAlongMainAxis.remove(&child); |
813 | } |
814 | |
815 | |
816 | LayoutUnit RenderFlexibleBox::computeInnerFlexBaseSizeForChild(RenderBox& child, LayoutUnit mainAxisBorderAndPadding, bool relayoutChildren) |
817 | { |
818 | child.clearOverrideContentSize(); |
819 | |
820 | Length flexBasis = flexBasisForChild(child); |
821 | if (mainAxisLengthIsDefinite(child, flexBasis)) |
822 | return std::max(0_lu, computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis).value()); |
823 | |
824 | // The flex basis is indefinite (=auto), so we need to compute the actual |
825 | // width of the child. For the logical width axis we just use the preferred |
826 | // width; for the height we need to lay out the child. |
827 | LayoutUnit mainAxisExtent; |
828 | if (hasOrthogonalFlow(child)) { |
829 | updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child); |
830 | if (child.needsLayout() || relayoutChildren || !m_intrinsicSizeAlongMainAxis.contains(&child)) { |
831 | if (!child.needsLayout()) |
832 | child.setChildNeedsLayout(MarkOnlyThis); |
833 | child.layoutIfNeeded(); |
834 | cacheChildMainSize(child); |
835 | } |
836 | mainAxisExtent = m_intrinsicSizeAlongMainAxis.get(&child); |
837 | } else { |
838 | // We don't need to add scrollbarLogicalWidth here because the preferred |
839 | // width includes the scrollbar, even for overflow: auto. |
840 | mainAxisExtent = child.maxPreferredLogicalWidth(); |
841 | } |
842 | return mainAxisExtent - mainAxisBorderAndPadding; |
843 | } |
844 | |
845 | void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren) |
846 | { |
847 | Vector<LineContext> lineContexts; |
848 | LayoutUnit sumFlexBaseSize; |
849 | double totalFlexGrow; |
850 | double totalFlexShrink; |
851 | double totalWeightedFlexShrink; |
852 | LayoutUnit sumHypotheticalMainSize; |
853 | |
854 | // Set up our master list of flex items. All of the rest of the algorithm |
855 | // should work off this list of a subset. |
856 | // TODO(cbiesinger): That second part is not yet true. |
857 | Vector<FlexItem> allItems; |
858 | m_orderIterator.first(); |
859 | for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) { |
860 | if (m_orderIterator.shouldSkipChild(*child)) { |
861 | // Out-of-flow children are not flex items, so we skip them here. |
862 | if (child->isOutOfFlowPositioned()) |
863 | prepareChildForPositionedLayout(*child); |
864 | continue; |
865 | } |
866 | allItems.append(constructFlexItem(*child, relayoutChildren)); |
867 | } |
868 | |
869 | const LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max()); |
870 | FlexLayoutAlgorithm flexAlgorithm(style(), lineBreakLength, allItems); |
871 | LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore(); |
872 | Vector<FlexItem> lineItems; |
873 | size_t nextIndex = 0; |
874 | while (flexAlgorithm.computeNextFlexLine(nextIndex, lineItems, sumFlexBaseSize, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink, sumHypotheticalMainSize)) { |
875 | LayoutUnit containerMainInnerSize = mainAxisContentExtent(sumHypotheticalMainSize); |
876 | // availableFreeSpace is the initial amount of free space in this flexbox. |
877 | // remainingFreeSpace starts out at the same value but as we place and lay |
878 | // out flex items we subtract from it. Note that both values can be |
879 | // negative. |
880 | LayoutUnit remainingFreeSpace = containerMainInnerSize - sumFlexBaseSize; |
881 | FlexSign flexSign = (sumHypotheticalMainSize < containerMainInnerSize) ? PositiveFlexibility : NegativeFlexibility; |
882 | freezeInflexibleItems(flexSign, lineItems, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink); |
883 | // The initial free space gets calculated after freezing inflexible items. |
884 | // https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths step 3 |
885 | const LayoutUnit initialFreeSpace = remainingFreeSpace; |
886 | while (!resolveFlexibleLengths(flexSign, lineItems, initialFreeSpace, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink)) { |
887 | ASSERT(totalFlexGrow >= 0); |
888 | ASSERT(totalWeightedFlexShrink >= 0); |
889 | } |
890 | |
891 | // Recalculate the remaining free space. The adjustment for flex factors |
892 | // between 0..1 means we can't just use remainingFreeSpace here. |
893 | remainingFreeSpace = containerMainInnerSize; |
894 | for (size_t i = 0; i < lineItems.size(); ++i) { |
895 | FlexItem& flexItem = lineItems[i]; |
896 | ASSERT(!flexItem.box.isOutOfFlowPositioned()); |
897 | remainingFreeSpace -= flexItem.flexedMarginBoxSize(); |
898 | } |
899 | // This will std::move lineItems into a newly-created LineContext. |
900 | layoutAndPlaceChildren(crossAxisOffset, lineItems, remainingFreeSpace, relayoutChildren, lineContexts); |
901 | } |
902 | |
903 | if (hasLineIfEmpty()) { |
904 | // Even if computeNextFlexLine returns true, the flexbox might not have |
905 | // a line because all our children might be out of flow positioned. |
906 | // Instead of just checking if we have a line, make sure the flexbox |
907 | // has at least a line's worth of height to cover this case. |
908 | LayoutUnit minHeight = borderAndPaddingLogicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) + scrollbarLogicalHeight(); |
909 | if (size().height() < minHeight) |
910 | setLogicalHeight(minHeight); |
911 | } |
912 | |
913 | updateLogicalHeight(); |
914 | repositionLogicalHeightDependentFlexItems(lineContexts); |
915 | } |
916 | |
917 | LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const Vector<FlexItem>& children, LayoutUnit& availableFreeSpace) |
918 | { |
919 | if (availableFreeSpace <= 0_lu) |
920 | return 0_lu; |
921 | |
922 | int numberOfAutoMargins = 0; |
923 | bool isHorizontal = isHorizontalFlow(); |
924 | for (size_t i = 0; i < children.size(); ++i) { |
925 | const auto& child = children[i].box; |
926 | ASSERT(!child.isOutOfFlowPositioned()); |
927 | if (isHorizontal) { |
928 | if (child.style().marginLeft().isAuto()) |
929 | ++numberOfAutoMargins; |
930 | if (child.style().marginRight().isAuto()) |
931 | ++numberOfAutoMargins; |
932 | } else { |
933 | if (child.style().marginTop().isAuto()) |
934 | ++numberOfAutoMargins; |
935 | if (child.style().marginBottom().isAuto()) |
936 | ++numberOfAutoMargins; |
937 | } |
938 | } |
939 | if (!numberOfAutoMargins) |
940 | return 0_lu; |
941 | |
942 | LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins; |
943 | availableFreeSpace = 0_lu; |
944 | return sizeOfAutoMargin; |
945 | } |
946 | |
947 | void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox& child, LayoutUnit autoMarginOffset) |
948 | { |
949 | ASSERT(autoMarginOffset >= 0_lu); |
950 | |
951 | if (isHorizontalFlow()) { |
952 | if (child.style().marginLeft().isAuto()) |
953 | child.setMarginLeft(autoMarginOffset); |
954 | if (child.style().marginRight().isAuto()) |
955 | child.setMarginRight(autoMarginOffset); |
956 | } else { |
957 | if (child.style().marginTop().isAuto()) |
958 | child.setMarginTop(autoMarginOffset); |
959 | if (child.style().marginBottom().isAuto()) |
960 | child.setMarginBottom(autoMarginOffset); |
961 | } |
962 | } |
963 | |
964 | bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(const RenderBox& child) const |
965 | { |
966 | if (isHorizontalFlow()) |
967 | return child.style().marginTop().isAuto() || child.style().marginBottom().isAuto(); |
968 | return child.style().marginLeft().isAuto() || child.style().marginRight().isAuto(); |
969 | } |
970 | |
971 | LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, const RenderBox& child) |
972 | { |
973 | ASSERT(!child.isOutOfFlowPositioned()); |
974 | LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child); |
975 | return lineCrossAxisExtent - childCrossExtent; |
976 | } |
977 | |
978 | bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox& child, LayoutUnit availableAlignmentSpace) |
979 | { |
980 | ASSERT(!child.isOutOfFlowPositioned()); |
981 | ASSERT(availableAlignmentSpace >= 0_lu); |
982 | |
983 | bool isHorizontal = isHorizontalFlow(); |
984 | Length topOrLeft = isHorizontal ? child.style().marginTop() : child.style().marginLeft(); |
985 | Length bottomOrRight = isHorizontal ? child.style().marginBottom() : child.style().marginRight(); |
986 | if (topOrLeft.isAuto() && bottomOrRight.isAuto()) { |
987 | adjustAlignmentForChild(child, availableAlignmentSpace / 2); |
988 | if (isHorizontal) { |
989 | child.setMarginTop(availableAlignmentSpace / 2); |
990 | child.setMarginBottom(availableAlignmentSpace / 2); |
991 | } else { |
992 | child.setMarginLeft(availableAlignmentSpace / 2); |
993 | child.setMarginRight(availableAlignmentSpace / 2); |
994 | } |
995 | return true; |
996 | } |
997 | bool shouldAdjustTopOrLeft = true; |
998 | if (isColumnFlow() && !child.style().isLeftToRightDirection()) { |
999 | // For column flows, only make this adjustment if topOrLeft corresponds to |
1000 | // the "before" margin, so that flipForRightToLeftColumn will do the right |
1001 | // thing. |
1002 | shouldAdjustTopOrLeft = false; |
1003 | } |
1004 | if (!isColumnFlow() && child.style().isFlippedBlocksWritingMode()) { |
1005 | // If we are a flipped writing mode, we need to adjust the opposite side. |
1006 | // This is only needed for row flows because this only affects the |
1007 | // block-direction axis. |
1008 | shouldAdjustTopOrLeft = false; |
1009 | } |
1010 | |
1011 | if (topOrLeft.isAuto()) { |
1012 | if (shouldAdjustTopOrLeft) |
1013 | adjustAlignmentForChild(child, availableAlignmentSpace); |
1014 | |
1015 | if (isHorizontal) |
1016 | child.setMarginTop(availableAlignmentSpace); |
1017 | else |
1018 | child.setMarginLeft(availableAlignmentSpace); |
1019 | return true; |
1020 | } |
1021 | |
1022 | if (bottomOrRight.isAuto()) { |
1023 | if (!shouldAdjustTopOrLeft) |
1024 | adjustAlignmentForChild(child, availableAlignmentSpace); |
1025 | |
1026 | if (isHorizontal) |
1027 | child.setMarginBottom(availableAlignmentSpace); |
1028 | else |
1029 | child.setMarginRight(availableAlignmentSpace); |
1030 | return true; |
1031 | } |
1032 | return false; |
1033 | } |
1034 | |
1035 | LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(const RenderBox& child) |
1036 | { |
1037 | LayoutUnit ascent = child.firstLineBaseline().valueOr(crossAxisExtentForChild(child)); |
1038 | return ascent + flowAwareMarginBeforeForChild(child); |
1039 | } |
1040 | |
1041 | LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin) |
1042 | { |
1043 | // When resolving the margins, we use the content size for resolving percent and calc (for percents in calc expressions) margins. |
1044 | // Fortunately, percent margins are always computed with respect to the block's width, even for margin-top and margin-bottom. |
1045 | LayoutUnit availableSize = contentLogicalWidth(); |
1046 | return minimumValueForLength(margin, availableSize); |
1047 | } |
1048 | |
1049 | void RenderFlexibleBox::prepareOrderIteratorAndMargins() |
1050 | { |
1051 | OrderIteratorPopulator populator(m_orderIterator); |
1052 | |
1053 | for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { |
1054 | if (!populator.collectChild(*child)) |
1055 | continue; |
1056 | |
1057 | // Before running the flex algorithm, 'auto' has a margin of 0. |
1058 | // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins. |
1059 | if (isHorizontalFlow()) { |
1060 | child->setMarginLeft(computeChildMarginValue(child->style().marginLeft())); |
1061 | child->setMarginRight(computeChildMarginValue(child->style().marginRight())); |
1062 | } else { |
1063 | child->setMarginTop(computeChildMarginValue(child->style().marginTop())); |
1064 | child->setMarginBottom(computeChildMarginValue(child->style().marginBottom())); |
1065 | } |
1066 | } |
1067 | } |
1068 | |
1069 | LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(const RenderBox& child, LayoutUnit childSize) |
1070 | { |
1071 | Length max = isHorizontalFlow() ? child.style().maxWidth() : child.style().maxHeight(); |
1072 | Optional<LayoutUnit> maxExtent = WTF::nullopt; |
1073 | if (max.isSpecifiedOrIntrinsic()) { |
1074 | maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); |
1075 | childSize = std::min(childSize, maxExtent.valueOr(childSize)); |
1076 | } |
1077 | |
1078 | Length min = isHorizontalFlow() ? child.style().minWidth() : child.style().minHeight(); |
1079 | if (min.isSpecifiedOrIntrinsic()) |
1080 | return std::max(childSize, std::max(0_lu, computeMainAxisExtentForChild(child, MinSize, min).valueOr(childSize))); |
1081 | |
1082 | if (!isFlexibleBoxImpl() && min.isAuto() && mainAxisOverflowForChild(child) == Overflow::Visible && !(isColumnFlow() && is<RenderFlexibleBox>(child))) { |
1083 | // FIXME: For now, we do not handle min-height: auto for nested |
1084 | // column flexboxes. We need to implement |
1085 | // https://drafts.csswg.org/css-flexbox/#intrinsic-sizes before that |
1086 | // produces reasonable results. Tracking bug: https://crbug.com/581553 |
1087 | // css-flexbox section 4.5 |
1088 | // FIXME: If the min value is expected to be valid here, we need to come up with a non optional version of computeMainAxisExtentForChild and |
1089 | // ensure it's valid through the virtual calls of computeIntrinsicLogicalContentHeightUsing. |
1090 | LayoutUnit contentSize = computeMainAxisExtentForChild(child, MinSize, Length(MinContent)).valueOr(0); |
1091 | ASSERT(contentSize >= 0); |
1092 | if (child.hasAspectRatio() && child.intrinsicSize().height() > 0) |
1093 | contentSize = adjustChildSizeForAspectRatioCrossAxisMinAndMax(child, contentSize); |
1094 | contentSize = std::min(contentSize, maxExtent.valueOr(contentSize)); |
1095 | |
1096 | Length mainSize = isHorizontalFlow() ? child.style().width() : child.style().height(); |
1097 | if (mainAxisLengthIsDefinite(child, mainSize)) { |
1098 | LayoutUnit resolvedMainSize = computeMainAxisExtentForChild(child, MainOrPreferredSize, mainSize).valueOr(0); |
1099 | ASSERT(resolvedMainSize >= 0); |
1100 | LayoutUnit specifiedSize = std::min(resolvedMainSize, maxExtent.valueOr(resolvedMainSize)); |
1101 | return std::max(childSize, std::min(specifiedSize, contentSize)); |
1102 | } |
1103 | |
1104 | if (useChildAspectRatio(child)) { |
1105 | Length crossSizeLength = isHorizontalFlow() ? child.style().height() : child.style().width(); |
1106 | Optional<LayoutUnit> transferredSize = computeMainSizeFromAspectRatioUsing(child, crossSizeLength); |
1107 | if (transferredSize) { |
1108 | transferredSize = adjustChildSizeForAspectRatioCrossAxisMinAndMax(child, transferredSize.value()); |
1109 | return std::max(childSize, std::min(transferredSize.value(), contentSize)); |
1110 | } |
1111 | } |
1112 | |
1113 | return std::max(childSize, contentSize); |
1114 | } |
1115 | |
1116 | return std::max(0_lu, childSize); |
1117 | } |
1118 | |
1119 | Optional<LayoutUnit> RenderFlexibleBox::crossSizeForPercentageResolution(const RenderBox& child) |
1120 | { |
1121 | if (alignmentForChild(child) != ItemPosition::Stretch) |
1122 | return WTF::nullopt; |
1123 | |
1124 | // Here we implement https://drafts.csswg.org/css-flexbox/#algo-stretch |
1125 | if (hasOrthogonalFlow(child) && child.hasOverrideContentLogicalWidth()) |
1126 | return child.overrideContentLogicalWidth(); |
1127 | if (!hasOrthogonalFlow(child) && child.hasOverrideContentLogicalHeight()) |
1128 | return child.overrideContentLogicalHeight(); |
1129 | |
1130 | // We don't currently implement the optimization from |
1131 | // https://drafts.csswg.org/css-flexbox/#definite-sizes case 1. While that |
1132 | // could speed up a specialized case, it requires determining if we have a |
1133 | // definite size, which itself is not cheap. We can consider implementing it |
1134 | // at a later time. (The correctness is ensured by redoing layout in |
1135 | // applyStretchAlignmentToChild) |
1136 | return WTF::nullopt; |
1137 | } |
1138 | |
1139 | Optional<LayoutUnit> RenderFlexibleBox::mainSizeForPercentageResolution(const RenderBox& child) |
1140 | { |
1141 | // This function implements section 9.8. Definite and Indefinite Sizes, case |
1142 | // 2) of the flexbox spec. |
1143 | // We need to check for the flexbox to have a definite main size, and for the |
1144 | // flex item to have a definite flex basis. |
1145 | const Length& flexBasis = flexBasisForChild(child); |
1146 | if (!mainAxisLengthIsDefinite(child, flexBasis)) |
1147 | return WTF::nullopt; |
1148 | if (!flexBasis.isPercentOrCalculated()) { |
1149 | // If flex basis had a percentage, our size is guaranteed to be definite or |
1150 | // the flex item's size could not be definite. Otherwise, we make up a |
1151 | // percentage to check whether we have a definite size. |
1152 | if (!mainAxisLengthIsDefinite(child, Length(0, Percent))) |
1153 | return WTF::nullopt; |
1154 | } |
1155 | |
1156 | if (hasOrthogonalFlow(child)) |
1157 | return child.hasOverrideContentLogicalHeight() ? Optional<LayoutUnit>(child.overrideContentLogicalHeight()) : WTF::nullopt; |
1158 | return child.hasOverrideContentLogicalWidth() ? Optional<LayoutUnit>(child.overrideContentLogicalWidth()) : WTF::nullopt; |
1159 | } |
1160 | |
1161 | Optional<LayoutUnit> RenderFlexibleBox::childLogicalHeightForPercentageResolution(const RenderBox& child) |
1162 | { |
1163 | if (!hasOrthogonalFlow(child)) |
1164 | return crossSizeForPercentageResolution(child); |
1165 | return mainSizeForPercentageResolution(child); |
1166 | } |
1167 | |
1168 | LayoutUnit RenderFlexibleBox::adjustChildSizeForAspectRatioCrossAxisMinAndMax(const RenderBox& child, LayoutUnit childSize) |
1169 | { |
1170 | Length crossMin = isHorizontalFlow() ? child.style().minHeight() : child.style().minWidth(); |
1171 | Length crossMax = isHorizontalFlow() ? child.style().maxHeight() : child.style().maxWidth(); |
1172 | |
1173 | if (crossAxisLengthIsDefinite(child, crossMax)) { |
1174 | LayoutUnit maxValue = computeMainSizeFromAspectRatioUsing(child, crossMax); |
1175 | childSize = std::min(maxValue, childSize); |
1176 | } |
1177 | |
1178 | if (crossAxisLengthIsDefinite(child, crossMin)) { |
1179 | LayoutUnit minValue = computeMainSizeFromAspectRatioUsing(child, crossMin); |
1180 | childSize = std::max(minValue, childSize); |
1181 | } |
1182 | |
1183 | return childSize; |
1184 | } |
1185 | |
1186 | FlexItem RenderFlexibleBox::constructFlexItem(RenderBox& child, bool relayoutChildren) |
1187 | { |
1188 | // If this condition is true, then computeMainAxisExtentForChild will call |
1189 | // child.intrinsicContentLogicalHeight() and |
1190 | // child.scrollbarLogicalHeight(), so if the child has intrinsic |
1191 | // min/max/preferred size, run layout on it now to make sure its logical |
1192 | // height and scroll bars are up to date. |
1193 | if (childHasIntrinsicMainAxisSize(child) && child.needsLayout()) { |
1194 | child.clearOverrideContentSize(); |
1195 | child.setChildNeedsLayout(MarkOnlyThis); |
1196 | child.layoutIfNeeded(); |
1197 | cacheChildMainSize(child); |
1198 | relayoutChildren = false; |
1199 | } |
1200 | |
1201 | LayoutUnit borderAndPadding = isHorizontalFlow() ? child.horizontalBorderAndPaddingExtent() : child.verticalBorderAndPaddingExtent(); |
1202 | LayoutUnit childInnerFlexBaseSize = computeInnerFlexBaseSizeForChild(child, borderAndPadding, relayoutChildren); |
1203 | LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(child, childInnerFlexBaseSize); |
1204 | LayoutUnit margin = isHorizontalFlow() ? child.horizontalMarginExtent() : child.verticalMarginExtent(); |
1205 | return FlexItem(child, childInnerFlexBaseSize, childMinMaxAppliedMainAxisExtent, borderAndPadding, margin); |
1206 | } |
1207 | |
1208 | void RenderFlexibleBox::freezeViolations(Vector<FlexItem*>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalFlexShrink, double& totalWeightedFlexShrink) |
1209 | { |
1210 | for (size_t i = 0; i < violations.size(); ++i) { |
1211 | ASSERT(!violations[i]->frozen); |
1212 | const auto& child = violations[i]->box; |
1213 | LayoutUnit childSize = violations[i]->flexedContentSize; |
1214 | availableFreeSpace -= childSize - violations[i]->flexBaseContentSize; |
1215 | totalFlexGrow -= child.style().flexGrow(); |
1216 | totalFlexShrink -= child.style().flexShrink(); |
1217 | totalWeightedFlexShrink -= child.style().flexShrink() * violations[i]->flexBaseContentSize; |
1218 | // totalWeightedFlexShrink can be negative when we exceed the precision of |
1219 | // a double when we initially calcuate totalWeightedFlexShrink. We then |
1220 | // subtract each child's weighted flex shrink with full precision, now |
1221 | // leading to a negative result. See |
1222 | // css3/flexbox/large-flex-shrink-assert.html |
1223 | totalWeightedFlexShrink = std::max(totalWeightedFlexShrink, 0.0); |
1224 | violations[i]->frozen = true; |
1225 | } |
1226 | } |
1227 | |
1228 | void RenderFlexibleBox::freezeInflexibleItems(FlexSign flexSign, Vector<FlexItem>& children, LayoutUnit& remainingFreeSpace, double& totalFlexGrow, double& totalFlexShrink, double& totalWeightedFlexShrink) |
1229 | { |
1230 | // Per https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths step 2, |
1231 | // we freeze all items with a flex factor of 0 as well as those with a min/max |
1232 | // size violation. |
1233 | Vector<FlexItem*> newInflexibleItems; |
1234 | for (size_t i = 0; i < children.size(); ++i) { |
1235 | FlexItem& flexItem = children[i]; |
1236 | const auto& child = flexItem.box; |
1237 | ASSERT(!flexItem.box.isOutOfFlowPositioned()); |
1238 | ASSERT(!flexItem.frozen); |
1239 | float flexFactor = (flexSign == PositiveFlexibility) ? child.style().flexGrow() : child.style().flexShrink(); |
1240 | if (!flexFactor || (flexSign == PositiveFlexibility && flexItem.flexBaseContentSize > flexItem.hypotheticalMainContentSize) || (flexSign == NegativeFlexibility && flexItem.flexBaseContentSize < flexItem.hypotheticalMainContentSize)) { |
1241 | flexItem.flexedContentSize = flexItem.hypotheticalMainContentSize; |
1242 | newInflexibleItems.append(&flexItem); |
1243 | } |
1244 | } |
1245 | freezeViolations(newInflexibleItems, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink); |
1246 | } |
1247 | |
1248 | // Returns true if we successfully ran the algorithm and sized the flex items. |
1249 | bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, Vector<FlexItem>& children, LayoutUnit initialFreeSpace, LayoutUnit& remainingFreeSpace, double& totalFlexGrow, double& totalFlexShrink, double& totalWeightedFlexShrink) |
1250 | { |
1251 | LayoutUnit totalViolation; |
1252 | LayoutUnit usedFreeSpace; |
1253 | Vector<FlexItem*> minViolations; |
1254 | Vector<FlexItem*> maxViolations; |
1255 | |
1256 | double sumFlexFactors = (flexSign == PositiveFlexibility) ? totalFlexGrow : totalFlexShrink; |
1257 | if (sumFlexFactors > 0 && sumFlexFactors < 1) { |
1258 | LayoutUnit fractional(initialFreeSpace * sumFlexFactors); |
1259 | if (fractional.abs() < remainingFreeSpace.abs()) |
1260 | remainingFreeSpace = fractional; |
1261 | } |
1262 | |
1263 | for (size_t i = 0; i < children.size(); ++i) { |
1264 | FlexItem& flexItem = children[i]; |
1265 | const auto& child = flexItem.box; |
1266 | |
1267 | // This check also covers out-of-flow children. |
1268 | if (flexItem.frozen) |
1269 | continue; |
1270 | |
1271 | LayoutUnit childSize = flexItem.flexBaseContentSize; |
1272 | double = 0; |
1273 | if (remainingFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow)) |
1274 | extraSpace = remainingFreeSpace * child.style().flexGrow() / totalFlexGrow; |
1275 | else if (remainingFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink) && child.style().flexShrink()) |
1276 | extraSpace = remainingFreeSpace * child.style().flexShrink() * flexItem.flexBaseContentSize / totalWeightedFlexShrink; |
1277 | if (std::isfinite(extraSpace)) |
1278 | childSize += LayoutUnit::fromFloatRound(extraSpace); |
1279 | |
1280 | LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize); |
1281 | ASSERT(adjustedChildSize >= 0); |
1282 | flexItem.flexedContentSize = adjustedChildSize; |
1283 | usedFreeSpace += adjustedChildSize - flexItem.flexBaseContentSize; |
1284 | |
1285 | LayoutUnit violation = adjustedChildSize - childSize; |
1286 | if (violation > 0) |
1287 | minViolations.append(&flexItem); |
1288 | else if (violation < 0) |
1289 | maxViolations.append(&flexItem); |
1290 | totalViolation += violation; |
1291 | } |
1292 | |
1293 | if (totalViolation) |
1294 | freezeViolations(totalViolation < 0 ? maxViolations : minViolations, remainingFreeSpace, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink); |
1295 | else |
1296 | remainingFreeSpace -= usedFreeSpace; |
1297 | |
1298 | return !totalViolation; |
1299 | } |
1300 | |
1301 | static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, ContentPosition justifyContent, ContentDistribution justifyContentDistribution, unsigned numberOfChildren) |
1302 | { |
1303 | if (justifyContent == ContentPosition::FlexEnd) |
1304 | return availableFreeSpace; |
1305 | if (justifyContent == ContentPosition::Center) |
1306 | return availableFreeSpace / 2; |
1307 | if (justifyContentDistribution == ContentDistribution::SpaceAround) { |
1308 | if (availableFreeSpace > 0 && numberOfChildren) |
1309 | return availableFreeSpace / (2 * numberOfChildren); |
1310 | else |
1311 | return availableFreeSpace / 2; |
1312 | } |
1313 | if (justifyContentDistribution == ContentDistribution::SpaceEvenly) { |
1314 | if (availableFreeSpace > 0 && numberOfChildren) |
1315 | return availableFreeSpace / (numberOfChildren + 1); |
1316 | // Fallback to 'center' |
1317 | return availableFreeSpace / 2; |
1318 | } |
1319 | return 0; |
1320 | } |
1321 | |
1322 | static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, ContentDistribution justifyContentDistribution, unsigned numberOfChildren) |
1323 | { |
1324 | if (availableFreeSpace > 0 && numberOfChildren > 1) { |
1325 | if (justifyContentDistribution == ContentDistribution::SpaceBetween) |
1326 | return availableFreeSpace / (numberOfChildren - 1); |
1327 | if (justifyContentDistribution == ContentDistribution::SpaceAround) |
1328 | return availableFreeSpace / numberOfChildren; |
1329 | if (justifyContentDistribution == ContentDistribution::SpaceEvenly) |
1330 | return availableFreeSpace / (numberOfChildren + 1); |
1331 | } |
1332 | return 0; |
1333 | } |
1334 | |
1335 | |
1336 | static LayoutUnit alignmentOffset(LayoutUnit availableFreeSpace, ItemPosition position, LayoutUnit ascent, LayoutUnit maxAscent, bool isWrapReverse) |
1337 | { |
1338 | switch (position) { |
1339 | case ItemPosition::Legacy: |
1340 | case ItemPosition::Auto: |
1341 | case ItemPosition::Normal: |
1342 | ASSERT_NOT_REACHED(); |
1343 | break; |
1344 | case ItemPosition::Stretch: |
1345 | // Actual stretching must be handled by the caller. Since wrap-reverse |
1346 | // flips cross start and cross end, stretch children should be aligned |
1347 | // with the cross end. This matters because applyStretchAlignment |
1348 | // doesn't always stretch or stretch fully (explicit cross size given, or |
1349 | // stretching constrained by max-height/max-width). For flex-start and |
1350 | // flex-end this is handled by alignmentForChild(). |
1351 | if (isWrapReverse) |
1352 | return availableFreeSpace; |
1353 | break; |
1354 | case ItemPosition::FlexStart: |
1355 | break; |
1356 | case ItemPosition::FlexEnd: |
1357 | return availableFreeSpace; |
1358 | case ItemPosition::Center: |
1359 | return availableFreeSpace / 2; |
1360 | case ItemPosition::Baseline: |
1361 | // FIXME: If we get here in columns, we want the use the descent, except |
1362 | // we currently can't get the ascent/descent of orthogonal children. |
1363 | // https://bugs.webkit.org/show_bug.cgi?id=98076 |
1364 | return maxAscent - ascent; |
1365 | case ItemPosition::LastBaseline: |
1366 | case ItemPosition::SelfStart: |
1367 | case ItemPosition::SelfEnd: |
1368 | case ItemPosition::Start: |
1369 | case ItemPosition::End: |
1370 | case ItemPosition::Left: |
1371 | case ItemPosition::Right: |
1372 | // FIXME: Implement the extended grammar, enabled when the Grid Layout |
1373 | // feature was enabled by default. |
1374 | break; |
1375 | } |
1376 | return 0; |
1377 | } |
1378 | |
1379 | void RenderFlexibleBox::setOverrideMainAxisContentSizeForChild(RenderBox& child, LayoutUnit childPreferredSize) |
1380 | { |
1381 | if (hasOrthogonalFlow(child)) |
1382 | child.setOverrideContentLogicalHeight(childPreferredSize); |
1383 | else |
1384 | child.setOverrideContentLogicalWidth(childPreferredSize); |
1385 | } |
1386 | |
1387 | LayoutUnit RenderFlexibleBox::staticMainAxisPositionForPositionedChild(const RenderBox& child) |
1388 | { |
1389 | const LayoutUnit availableSpace = mainAxisContentExtent(contentLogicalHeight()) - mainAxisExtentForChild(child); |
1390 | |
1391 | ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehavior()); |
1392 | ContentDistribution distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehavior()); |
1393 | LayoutUnit offset = initialJustifyContentOffset(availableSpace, position, distribution, 1); |
1394 | if (style().flexDirection() == FlexDirection::RowReverse || style().flexDirection() == FlexDirection::ColumnReverse) |
1395 | offset = availableSpace - offset; |
1396 | return offset; |
1397 | } |
1398 | |
1399 | LayoutUnit RenderFlexibleBox::staticCrossAxisPositionForPositionedChild(const RenderBox& child) |
1400 | { |
1401 | LayoutUnit availableSpace = crossAxisContentExtent() - crossAxisExtentForChild(child); |
1402 | return alignmentOffset(availableSpace, alignmentForChild(child), 0_lu, 0_lu, style().flexWrap() == FlexWrap::Reverse); |
1403 | } |
1404 | |
1405 | LayoutUnit RenderFlexibleBox::staticInlinePositionForPositionedChild(const RenderBox& child) |
1406 | { |
1407 | return startOffsetForContent() + (isColumnFlow() ? staticCrossAxisPositionForPositionedChild(child) : staticMainAxisPositionForPositionedChild(child)); |
1408 | } |
1409 | |
1410 | LayoutUnit RenderFlexibleBox::staticBlockPositionForPositionedChild(const RenderBox& child) |
1411 | { |
1412 | return borderAndPaddingBefore() + (isColumnFlow() ? staticMainAxisPositionForPositionedChild(child) : staticCrossAxisPositionForPositionedChild(child)); |
1413 | } |
1414 | |
1415 | bool RenderFlexibleBox::setStaticPositionForPositionedLayout(const RenderBox& child) |
1416 | { |
1417 | bool positionChanged = false; |
1418 | auto* childLayer = child.layer(); |
1419 | if (child.style().hasStaticInlinePosition(style().isHorizontalWritingMode())) { |
1420 | LayoutUnit inlinePosition = staticInlinePositionForPositionedChild(child); |
1421 | if (childLayer->staticInlinePosition() != inlinePosition) { |
1422 | childLayer->setStaticInlinePosition(inlinePosition); |
1423 | positionChanged = true; |
1424 | } |
1425 | } |
1426 | if (child.style().hasStaticBlockPosition(style().isHorizontalWritingMode())) { |
1427 | LayoutUnit blockPosition = staticBlockPositionForPositionedChild(child); |
1428 | if (childLayer->staticBlockPosition() != blockPosition) { |
1429 | childLayer->setStaticBlockPosition(blockPosition); |
1430 | positionChanged = true; |
1431 | } |
1432 | } |
1433 | return positionChanged; |
1434 | } |
1435 | |
1436 | void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox& child) |
1437 | { |
1438 | ASSERT(child.isOutOfFlowPositioned()); |
1439 | child.containingBlock()->insertPositionedObject(child); |
1440 | auto* childLayer = child.layer(); |
1441 | LayoutUnit staticInlinePosition = flowAwareBorderStart() + flowAwarePaddingStart(); |
1442 | if (childLayer->staticInlinePosition() != staticInlinePosition) { |
1443 | childLayer->setStaticInlinePosition(staticInlinePosition); |
1444 | if (child.style().hasStaticInlinePosition(style().isHorizontalWritingMode())) |
1445 | child.setChildNeedsLayout(MarkOnlyThis); |
1446 | } |
1447 | |
1448 | LayoutUnit staticBlockPosition = flowAwareBorderBefore() + flowAwarePaddingBefore(); |
1449 | if (childLayer->staticBlockPosition() != staticBlockPosition) { |
1450 | childLayer->setStaticBlockPosition(staticBlockPosition); |
1451 | if (child.style().hasStaticBlockPosition(style().isHorizontalWritingMode())) |
1452 | child.setChildNeedsLayout(MarkOnlyThis); |
1453 | } |
1454 | } |
1455 | |
1456 | ItemPosition RenderFlexibleBox::alignmentForChild(const RenderBox& child) const |
1457 | { |
1458 | ItemPosition align = child.style().resolvedAlignSelf(&style(), selfAlignmentNormalBehavior()).position(); |
1459 | ASSERT(align != ItemPosition::Auto && align != ItemPosition::Normal); |
1460 | |
1461 | if (align == ItemPosition::Baseline && hasOrthogonalFlow(child)) |
1462 | align = ItemPosition::FlexStart; |
1463 | |
1464 | if (style().flexWrap() == FlexWrap::Reverse) { |
1465 | if (align == ItemPosition::FlexStart) |
1466 | align = ItemPosition::FlexEnd; |
1467 | else if (align == ItemPosition::FlexEnd) |
1468 | align = ItemPosition::FlexStart; |
1469 | } |
1470 | |
1471 | return align; |
1472 | } |
1473 | |
1474 | void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox& child) |
1475 | { |
1476 | if (hasAutoMarginsInCrossAxis(child)) { |
1477 | child.updateLogicalHeight(); |
1478 | if (isHorizontalFlow()) { |
1479 | if (child.style().marginTop().isAuto()) |
1480 | child.setMarginTop(0_lu); |
1481 | if (child.style().marginBottom().isAuto()) |
1482 | child.setMarginBottom(0_lu); |
1483 | } else { |
1484 | if (child.style().marginLeft().isAuto()) |
1485 | child.setMarginLeft(0_lu); |
1486 | if (child.style().marginRight().isAuto()) |
1487 | child.setMarginRight(0_lu); |
1488 | } |
1489 | } |
1490 | } |
1491 | |
1492 | bool RenderFlexibleBox::needToStretchChildLogicalHeight(const RenderBox& child) const |
1493 | { |
1494 | // This function is a little bit magical. It relies on the fact that blocks |
1495 | // intrinsically "stretch" themselves in their inline axis, i.e. a <div> has |
1496 | // an implicit width: 100%. So the child will automatically stretch if our |
1497 | // cross axis is the child's inline axis. That's the case if: |
1498 | // - We are horizontal and the child is in vertical writing mode |
1499 | // - We are vertical and the child is in horizontal writing mode |
1500 | // Otherwise, we need to stretch if the cross axis size is auto. |
1501 | if (alignmentForChild(child) != ItemPosition::Stretch) |
1502 | return false; |
1503 | |
1504 | if (isHorizontalFlow() != child.style().isHorizontalWritingMode()) |
1505 | return false; |
1506 | |
1507 | return child.style().logicalHeight().isAuto(); |
1508 | } |
1509 | |
1510 | bool RenderFlexibleBox::childHasIntrinsicMainAxisSize(const RenderBox& child) const |
1511 | { |
1512 | bool result = false; |
1513 | if (isHorizontalFlow() != child.style().isHorizontalWritingMode()) { |
1514 | Length childFlexBasis = flexBasisForChild(child); |
1515 | Length childMinSize = isHorizontalFlow() ? child.style().minWidth() : child.style().minHeight(); |
1516 | Length childMaxSize = isHorizontalFlow() ? child.style().maxWidth() : child.style().maxHeight(); |
1517 | if (childFlexBasis.isIntrinsic() || childMinSize.isIntrinsicOrAuto() || childMaxSize.isIntrinsic()) |
1518 | result = true; |
1519 | } |
1520 | return result; |
1521 | } |
1522 | |
1523 | Overflow RenderFlexibleBox::mainAxisOverflowForChild(const RenderBox& child) const |
1524 | { |
1525 | if (isHorizontalFlow()) |
1526 | return child.style().overflowX(); |
1527 | return child.style().overflowY(); |
1528 | } |
1529 | |
1530 | Overflow RenderFlexibleBox::crossAxisOverflowForChild(const RenderBox& child) const |
1531 | { |
1532 | if (isHorizontalFlow()) |
1533 | return child.style().overflowY(); |
1534 | return child.style().overflowX(); |
1535 | } |
1536 | |
1537 | void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, Vector<FlexItem>& children, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts) |
1538 | { |
1539 | ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehavior()); |
1540 | ContentDistribution distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehavior()); |
1541 | |
1542 | LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace); |
1543 | LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart(); |
1544 | mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, position, distribution, children.size()); |
1545 | if (style().flexDirection() == FlexDirection::RowReverse) |
1546 | mainAxisOffset += isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight(); |
1547 | |
1548 | LayoutUnit totalMainExtent = mainAxisExtent(); |
1549 | LayoutUnit maxAscent, maxDescent; // Used when align-items: baseline. |
1550 | LayoutUnit maxChildCrossAxisExtent; |
1551 | bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow(); |
1552 | for (size_t i = 0; i < children.size(); ++i) { |
1553 | const auto& flexItem = children[i]; |
1554 | auto& child = flexItem.box; |
1555 | bool childHadLayout = child.everHadLayout(); |
1556 | |
1557 | ASSERT(!flexItem.box.isOutOfFlowPositioned()); |
1558 | |
1559 | setOverrideMainAxisContentSizeForChild(child, flexItem.flexedContentSize); |
1560 | // The flexed content size and the override size include the scrollbar |
1561 | // width, so we need to compare to the size including the scrollbar. |
1562 | // TODO(cbiesinger): Should it include the scrollbar? |
1563 | if (flexItem.flexedContentSize != mainAxisContentExtentForChildIncludingScrollbar(child)) |
1564 | child.setChildNeedsLayout(MarkOnlyThis); |
1565 | else { |
1566 | // To avoid double applying margin changes in |
1567 | // updateAutoMarginsInCrossAxis, we reset the margins here. |
1568 | resetAutoMarginsAndLogicalTopInCrossAxis(child); |
1569 | } |
1570 | // We may have already forced relayout for orthogonal flowing children in |
1571 | // computeInnerFlexBaseSizeForChild. |
1572 | bool forceChildRelayout = relayoutChildren && !m_relaidOutChildren.contains(&child); |
1573 | if (child.isRenderBlock() && downcast<RenderBlock>(child).hasPercentHeightDescendants()) { |
1574 | // Have to force another relayout even though the child is sized |
1575 | // correctly, because its descendants are not sized correctly yet. Our |
1576 | // previous layout of the child was done without an override height set. |
1577 | // So, redo it here. |
1578 | forceChildRelayout = true; |
1579 | } |
1580 | updateBlockChildDirtyBitsBeforeLayout(forceChildRelayout, child); |
1581 | if (!child.needsLayout()) |
1582 | child.markForPaginationRelayoutIfNeeded(); |
1583 | if (child.needsLayout()) |
1584 | m_relaidOutChildren.add(&child); |
1585 | child.layoutIfNeeded(); |
1586 | if (!childHadLayout && child.checkForRepaintDuringLayout()) { |
1587 | child.repaint(); |
1588 | child.repaintOverhangingFloats(true); |
1589 | } |
1590 | |
1591 | updateAutoMarginsInMainAxis(child, autoMarginOffset); |
1592 | |
1593 | LayoutUnit childCrossAxisMarginBoxExtent; |
1594 | if (alignmentForChild(child) == ItemPosition::Baseline && !hasAutoMarginsInCrossAxis(child)) { |
1595 | LayoutUnit ascent = marginBoxAscentForChild(child); |
1596 | LayoutUnit descent = (crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child)) - ascent; |
1597 | |
1598 | maxAscent = std::max(maxAscent, ascent); |
1599 | maxDescent = std::max(maxDescent, descent); |
1600 | |
1601 | // FIXME: Take scrollbar into account |
1602 | childCrossAxisMarginBoxExtent = maxAscent + maxDescent; |
1603 | } else |
1604 | childCrossAxisMarginBoxExtent = crossAxisIntrinsicExtentForChild(child) + crossAxisMarginExtentForChild(child); |
1605 | |
1606 | if (!isColumnFlow()) |
1607 | setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent())); |
1608 | maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent); |
1609 | |
1610 | mainAxisOffset += flowAwareMarginStartForChild(child); |
1611 | |
1612 | LayoutUnit childMainExtent = mainAxisExtentForChild(child); |
1613 | // In an RTL column situation, this will apply the margin-right/margin-end |
1614 | // on the left. This will be fixed later in flipForRightToLeftColumn. |
1615 | LayoutPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child)); |
1616 | setFlowAwareLocationForChild(child, childLocation); |
1617 | mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(child); |
1618 | |
1619 | if (i != children.size() - 1) { |
1620 | // The last item does not get extra space added. |
1621 | mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, distribution, children.size()); |
1622 | } |
1623 | |
1624 | // FIXME: Deal with pagination. |
1625 | } |
1626 | |
1627 | if (isColumnFlow()) |
1628 | setLogicalHeight(std::max(logicalHeight(), mainAxisOffset + flowAwareBorderEnd() + flowAwarePaddingEnd() + scrollbarLogicalHeight())); |
1629 | |
1630 | if (style().flexDirection() == FlexDirection::ColumnReverse) { |
1631 | // We have to do an extra pass for column-reverse to reposition the flex |
1632 | // items since the start depends on the height of the flexbox, which we |
1633 | // only know after we've positioned all the flex items. |
1634 | updateLogicalHeight(); |
1635 | layoutColumnReverse(children, crossAxisOffset, availableFreeSpace); |
1636 | } |
1637 | |
1638 | if (m_numberOfInFlowChildrenOnFirstLine == -1) |
1639 | m_numberOfInFlowChildrenOnFirstLine = children.size(); |
1640 | lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, maxAscent, WTFMove(children))); |
1641 | crossAxisOffset += maxChildCrossAxisExtent; |
1642 | } |
1643 | |
1644 | void RenderFlexibleBox::layoutColumnReverse(const Vector<FlexItem>& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace) |
1645 | { |
1646 | ContentPosition position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehavior()); |
1647 | ContentDistribution distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehavior()); |
1648 | |
1649 | // This is similar to the logic in layoutAndPlaceChildren, except we place |
1650 | // the children starting from the end of the flexbox. We also don't need to |
1651 | // layout anything since we're just moving the children to a new position. |
1652 | LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd(); |
1653 | mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, position, distribution, children.size()); |
1654 | mainAxisOffset -= isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight(); |
1655 | |
1656 | for (size_t i = 0; i < children.size(); ++i) { |
1657 | auto& child = children[i].box; |
1658 | ASSERT(!child.isOutOfFlowPositioned()); |
1659 | mainAxisOffset -= mainAxisExtentForChild(child) + flowAwareMarginEndForChild(child); |
1660 | setFlowAwareLocationForChild(child, LayoutPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child))); |
1661 | mainAxisOffset -= flowAwareMarginStartForChild(child); |
1662 | mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, distribution, children.size()); |
1663 | } |
1664 | } |
1665 | |
1666 | static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, ContentPosition alignContent, ContentDistribution alignContentDistribution, unsigned numberOfLines) |
1667 | { |
1668 | if (numberOfLines <= 1) |
1669 | return 0_lu; |
1670 | if (alignContent == ContentPosition::FlexEnd) |
1671 | return availableFreeSpace; |
1672 | if (alignContent == ContentPosition::Center) |
1673 | return availableFreeSpace / 2; |
1674 | if (alignContentDistribution == ContentDistribution::SpaceAround) { |
1675 | if (availableFreeSpace > 0 && numberOfLines) |
1676 | return availableFreeSpace / (2 * numberOfLines); |
1677 | if (availableFreeSpace < 0) |
1678 | return availableFreeSpace / 2; |
1679 | } |
1680 | if (alignContentDistribution == ContentDistribution::SpaceEvenly) { |
1681 | if (availableFreeSpace > 0) |
1682 | return availableFreeSpace / (numberOfLines + 1); |
1683 | // Fallback to 'center' |
1684 | return availableFreeSpace / 2; |
1685 | } |
1686 | return 0_lu; |
1687 | } |
1688 | |
1689 | static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, ContentDistribution alignContentDistribution, unsigned numberOfLines) |
1690 | { |
1691 | if (availableFreeSpace > 0 && numberOfLines > 1) { |
1692 | if (alignContentDistribution == ContentDistribution::SpaceBetween) |
1693 | return availableFreeSpace / (numberOfLines - 1); |
1694 | if (alignContentDistribution == ContentDistribution::SpaceAround || alignContentDistribution == ContentDistribution::Stretch) |
1695 | return availableFreeSpace / numberOfLines; |
1696 | if (alignContentDistribution == ContentDistribution::SpaceEvenly) |
1697 | return availableFreeSpace / (numberOfLines + 1); |
1698 | } |
1699 | return 0_lu; |
1700 | } |
1701 | |
1702 | void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts) |
1703 | { |
1704 | ContentPosition position = style().resolvedAlignContentPosition(contentAlignmentNormalBehavior()); |
1705 | ContentDistribution distribution = style().resolvedAlignContentDistribution(contentAlignmentNormalBehavior()); |
1706 | |
1707 | // If we have a single line flexbox or a multiline line flexbox with only one |
1708 | // flex line, the line height is all the available space. For |
1709 | // flex-direction: row, this means we need to use the height, so we do this |
1710 | // after calling updateLogicalHeight. |
1711 | if (lineContexts.size() == 1) { |
1712 | lineContexts[0].crossAxisExtent = crossAxisContentExtent(); |
1713 | return; |
1714 | } |
1715 | |
1716 | if (position == ContentPosition::FlexStart) |
1717 | return; |
1718 | |
1719 | LayoutUnit availableCrossAxisSpace = crossAxisContentExtent(); |
1720 | for (size_t i = 0; i < lineContexts.size(); ++i) |
1721 | availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; |
1722 | |
1723 | LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, position, distribution, lineContexts.size()); |
1724 | for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { |
1725 | LineContext& lineContext = lineContexts[lineNumber]; |
1726 | lineContext.crossAxisOffset += lineOffset; |
1727 | for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) { |
1728 | FlexItem& flexItem = lineContext.flexItems[childNumber]; |
1729 | adjustAlignmentForChild(flexItem.box, lineOffset); |
1730 | } |
1731 | |
1732 | if (distribution == ContentDistribution::Stretch && availableCrossAxisSpace > 0) |
1733 | lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size()); |
1734 | |
1735 | lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, distribution, lineContexts.size()); |
1736 | } |
1737 | } |
1738 | |
1739 | void RenderFlexibleBox::adjustAlignmentForChild(RenderBox& child, LayoutUnit delta) |
1740 | { |
1741 | ASSERT(!child.isOutOfFlowPositioned()); |
1742 | setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0_lu, delta)); |
1743 | } |
1744 | |
1745 | void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) |
1746 | { |
1747 | // Keep track of the space between the baseline edge and the after edge of |
1748 | // the box for each line. |
1749 | Vector<LayoutUnit> minMarginAfterBaselines; |
1750 | |
1751 | for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { |
1752 | const LineContext& lineContext = lineContexts[lineNumber]; |
1753 | |
1754 | LayoutUnit minMarginAfterBaseline = LayoutUnit::max(); |
1755 | LayoutUnit lineCrossAxisExtent = lineContext.crossAxisExtent; |
1756 | LayoutUnit maxAscent = lineContext.maxAscent; |
1757 | |
1758 | for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) { |
1759 | const auto& flexItem = lineContext.flexItems[childNumber]; |
1760 | ASSERT(!flexItem.box.isOutOfFlowPositioned()); |
1761 | |
1762 | if (updateAutoMarginsInCrossAxis(flexItem.box, std::max(0_lu, availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem.box)))) |
1763 | continue; |
1764 | |
1765 | ItemPosition position = alignmentForChild(flexItem.box); |
1766 | if (position == ItemPosition::Stretch) |
1767 | applyStretchAlignmentToChild(flexItem.box, lineCrossAxisExtent); |
1768 | LayoutUnit availableSpace = |
1769 | availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem.box); |
1770 | LayoutUnit offset = alignmentOffset(availableSpace, position, marginBoxAscentForChild(flexItem.box), maxAscent, style().flexWrap() == FlexWrap::Reverse); |
1771 | adjustAlignmentForChild(flexItem.box, offset); |
1772 | if (position == ItemPosition::Baseline && style().flexWrap() == FlexWrap::Reverse) |
1773 | minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem.box) - offset); |
1774 | } |
1775 | |
1776 | minMarginAfterBaselines.append(minMarginAfterBaseline); |
1777 | } |
1778 | |
1779 | if (style().flexWrap() != FlexWrap::Reverse) |
1780 | return; |
1781 | |
1782 | // wrap-reverse flips the cross axis start and end. For baseline alignment, |
1783 | // this means we need to align the after edge of baseline elements with the |
1784 | // after edge of the flex line. |
1785 | for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { |
1786 | const LineContext& lineContext = lineContexts[lineNumber]; |
1787 | LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber]; |
1788 | for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) { |
1789 | const auto& flexItem = lineContext.flexItems[childNumber]; |
1790 | if (alignmentForChild(flexItem.box) == ItemPosition::Baseline && !hasAutoMarginsInCrossAxis(flexItem.box) && minMarginAfterBaseline) |
1791 | adjustAlignmentForChild(flexItem.box, minMarginAfterBaseline); |
1792 | } |
1793 | } |
1794 | } |
1795 | |
1796 | void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUnit lineCrossAxisExtent) |
1797 | { |
1798 | if (!hasOrthogonalFlow(child) && child.style().logicalHeight().isAuto()) { |
1799 | LayoutUnit stretchedLogicalHeight = std::max(child.borderAndPaddingLogicalHeight(), |
1800 | lineCrossAxisExtent - crossAxisMarginExtentForChild(child)); |
1801 | ASSERT(!child.needsLayout()); |
1802 | LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, cachedChildIntrinsicContentLogicalHeight(child)); |
1803 | |
1804 | // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905. |
1805 | bool childNeedsRelayout = desiredLogicalHeight != child.logicalHeight(); |
1806 | if (child.isRenderBlock() && downcast<RenderBlock>(child).hasPercentHeightDescendants() && m_relaidOutChildren.contains(&child)) { |
1807 | // Have to force another relayout even though the child is sized |
1808 | // correctly, because its descendants are not sized correctly yet. Our |
1809 | // previous layout of the child was done without an override height set. |
1810 | // So, redo it here. |
1811 | childNeedsRelayout = true; |
1812 | } |
1813 | if (childNeedsRelayout || !child.hasOverrideContentLogicalHeight()) |
1814 | child.setOverrideContentLogicalHeight(desiredLogicalHeight - child.borderAndPaddingLogicalHeight()); |
1815 | if (childNeedsRelayout) { |
1816 | child.setLogicalHeight(0_lu); |
1817 | // We cache the child's intrinsic content logical height to avoid it being |
1818 | // reset to the stretched height. |
1819 | // FIXME: This is fragile. RendertBoxes should be smart enough to |
1820 | // determine their intrinsic content logical height correctly even when |
1821 | // there's an overrideHeight. |
1822 | LayoutUnit childIntrinsicContentLogicalHeight = cachedChildIntrinsicContentLogicalHeight(child); |
1823 | child.setChildNeedsLayout(MarkOnlyThis); |
1824 | |
1825 | // Don't use layoutChildIfNeeded to avoid setting cross axis cached size twice. |
1826 | child.layoutIfNeeded(); |
1827 | |
1828 | setCachedChildIntrinsicContentLogicalHeight(child, childIntrinsicContentLogicalHeight); |
1829 | } |
1830 | } else if (hasOrthogonalFlow(child) && child.style().logicalWidth().isAuto()) { |
1831 | LayoutUnit childWidth = std::max(0_lu, lineCrossAxisExtent - crossAxisMarginExtentForChild(child)); |
1832 | childWidth = child.constrainLogicalWidthInFragmentByMinMax(childWidth, crossAxisContentExtent(), *this, nullptr); |
1833 | |
1834 | if (childWidth != child.logicalWidth()) { |
1835 | child.setOverrideContentLogicalWidth(childWidth - child.borderAndPaddingLogicalWidth()); |
1836 | child.setChildNeedsLayout(MarkOnlyThis); |
1837 | child.layoutIfNeeded(); |
1838 | } |
1839 | } |
1840 | } |
1841 | |
1842 | void RenderFlexibleBox::flipForRightToLeftColumn(const Vector<LineContext>& lineContexts) |
1843 | { |
1844 | if (style().isLeftToRightDirection() || !isColumnFlow()) |
1845 | return; |
1846 | |
1847 | LayoutUnit crossExtent = crossAxisExtent(); |
1848 | for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { |
1849 | const LineContext& lineContext = lineContexts[lineNumber]; |
1850 | for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) { |
1851 | const auto& flexItem = lineContext.flexItems[childNumber]; |
1852 | ASSERT(!flexItem.box.isOutOfFlowPositioned()); |
1853 | |
1854 | LayoutPoint location = flowAwareLocationForChild(flexItem.box); |
1855 | // For vertical flows, setFlowAwareLocationForChild will transpose x and |
1856 | // y, so using the y axis for a column cross axis extent is correct. |
1857 | location.setY(crossExtent - crossAxisExtentForChild(flexItem.box) - location.y()); |
1858 | if (!isHorizontalWritingMode()) |
1859 | location.move(LayoutSize(0, -horizontalScrollbarHeight())); |
1860 | setFlowAwareLocationForChild(flexItem.box, location); |
1861 | } |
1862 | } |
1863 | } |
1864 | |
1865 | void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge) |
1866 | { |
1867 | LayoutUnit contentExtent = crossAxisContentExtent(); |
1868 | for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { |
1869 | const LineContext& lineContext = lineContexts[lineNumber]; |
1870 | for (size_t childNumber = 0; childNumber < lineContext.flexItems.size(); ++childNumber) { |
1871 | const auto& flexItem = lineContext.flexItems[childNumber]; |
1872 | LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; |
1873 | LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge; |
1874 | LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent; |
1875 | adjustAlignmentForChild(flexItem.box, newOffset - originalOffset); |
1876 | } |
1877 | } |
1878 | } |
1879 | |
1880 | bool RenderFlexibleBox::isTopLayoutOverflowAllowed() const |
1881 | { |
1882 | bool hasTopOverflow = RenderBlock::isTopLayoutOverflowAllowed(); |
1883 | if (hasTopOverflow || !style().isReverseFlexDirection()) |
1884 | return hasTopOverflow; |
1885 | |
1886 | return !isHorizontalFlow(); |
1887 | } |
1888 | |
1889 | bool RenderFlexibleBox::isLeftLayoutOverflowAllowed() const |
1890 | { |
1891 | bool hasLeftOverflow = RenderBlock::isLeftLayoutOverflowAllowed(); |
1892 | if (hasLeftOverflow || !style().isReverseFlexDirection()) |
1893 | return hasLeftOverflow; |
1894 | |
1895 | return isHorizontalFlow(); |
1896 | } |
1897 | |
1898 | } |
1899 | |