diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderFlexibleBox.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RenderFlexibleBox.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderFlexibleBox.cpp | 395 |
1 files changed, 270 insertions, 125 deletions
diff --git a/Source/WebCore/rendering/RenderFlexibleBox.cpp b/Source/WebCore/rendering/RenderFlexibleBox.cpp index 9fbfbebad..95b26c330 100644 --- a/Source/WebCore/rendering/RenderFlexibleBox.cpp +++ b/Source/WebCore/rendering/RenderFlexibleBox.cpp @@ -34,11 +34,14 @@ #include "LayoutRepainter.h" #include "RenderLayer.h" #include "RenderView.h" +#include "RuntimeEnabledFeatures.h" #include <limits> #include <wtf/MathExtras.h> namespace WebCore { +static constexpr ItemPosition selfAlignmentNormalBehaviorFlexibleBox = ItemPositionStretch; + struct RenderFlexibleBox::LineContext { LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, size_t numberOfChildren, LayoutUnit maxAscent) : crossAxisOffset(crossAxisOffset) @@ -66,16 +69,16 @@ struct RenderFlexibleBox::Violation { }; -RenderFlexibleBox::RenderFlexibleBox(Element& element, PassRef<RenderStyle> style) - : RenderBlock(element, std::move(style), 0) +RenderFlexibleBox::RenderFlexibleBox(Element& element, RenderStyle&& style) + : RenderBlock(element, WTFMove(style), 0) , m_orderIterator(*this) , m_numberOfInFlowChildrenOnFirstLine(-1) { setChildrenInline(false); // All of our children must be block-level. } -RenderFlexibleBox::RenderFlexibleBox(Document& document, PassRef<RenderStyle> style) - : RenderBlock(document, std::move(style), 0) +RenderFlexibleBox::RenderFlexibleBox(Document& document, RenderStyle&& style) + : RenderBlock(document, WTFMove(style), 0) , m_orderIterator(*this) , m_numberOfInFlowChildrenOnFirstLine(-1) { @@ -123,9 +126,12 @@ void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidt } } + // Due to negative margins, it is possible that we calculated a negative intrinsic width. + // Make sure that we never return a negative width. + minLogicalWidth = std::max(LayoutUnit(), minLogicalWidth); maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); - LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth(); + LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidth(); maxLogicalWidth += scrollbarWidth; minLogicalWidth += scrollbarWidth; } @@ -169,24 +175,22 @@ static int synthesizedBaselineFromContentBox(const RenderBox& box, LineDirection int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode) const { - int baseline = firstLineBaseline(); - if (baseline == -1) - baseline = synthesizedBaselineFromContentBox(*this, direction); + int baseline = firstLineBaseline().value_or(synthesizedBaselineFromContentBox(*this, direction)); int marginAscent = direction == HorizontalLine ? marginTop() : marginRight(); return baseline + marginAscent; } -int RenderFlexibleBox::firstLineBaseline() const +std::optional<int> RenderFlexibleBox::firstLineBaseline() const { if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0) - return -1; - RenderBox* baselineChild = 0; + return std::optional<int>(); + RenderBox* baselineChild = nullptr; int childNumber = 0; for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) continue; - if (alignmentForChild(*child) == AlignBaseline && !hasAutoMarginsInCrossAxis(*child)) { + if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child)) { baselineChild = child; break; } @@ -199,52 +203,45 @@ int RenderFlexibleBox::firstLineBaseline() const } if (!baselineChild) - return -1; + return std::optional<int>(); if (!isColumnFlow() && hasOrthogonalFlow(*baselineChild)) - return crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop(); + return std::optional<int>(crossAxisExtentForChild(*baselineChild) + baselineChild->logicalTop()); if (isColumnFlow() && !hasOrthogonalFlow(*baselineChild)) - return mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop(); + return std::optional<int>(mainAxisExtentForChild(*baselineChild) + baselineChild->logicalTop()); - int baseline = baselineChild->firstLineBaseline(); - if (baseline == -1) { + std::optional<int> baseline = baselineChild->firstLineBaseline(); + if (!baseline) { // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root. // This would also fix some cases where the flexbox is orthogonal to its container. LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine; - return synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop(); + return std::optional<int>(synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop()); } - return baseline + baselineChild->logicalTop(); + return std::optional<int>(baseline.value() + baselineChild->logicalTop()); } -int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const +std::optional<int> RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const { - int baseline = firstLineBaseline(); - if (baseline != -1) + if (std::optional<int> baseline = firstLineBaseline()) return baseline; int marginAscent = direction == HorizontalLine ? marginTop() : marginRight(); return synthesizedBaselineFromContentBox(*this, direction) + marginAscent; } -static EAlignItems resolveAlignment(const RenderStyle* parentStyle, const RenderStyle* childStyle) -{ - EAlignItems align = childStyle->alignSelf(); - if (align == AlignAuto) - align = parentStyle->alignItems(); - return align; -} - void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); - if (oldStyle && oldStyle->alignItems() == AlignStretch && diff == StyleDifferenceLayout) { + if (oldStyle && oldStyle->resolvedAlignItems(selfAlignmentNormalBehaviorFlexibleBox).position() == ItemPositionStretch && diff == StyleDifferenceLayout) { // Flex items that were previously stretching need to be relayed out so we can compute new available cross axis space. // This is only necessary for stretching since other alignment values don't change the size of the box. + auto& newStyle = style(); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - EAlignItems previousAlignment = resolveAlignment(oldStyle, &child->style()); - if (previousAlignment == AlignStretch && previousAlignment != resolveAlignment(&style(), &child->style())) + auto& childStyle = child->style(); + auto previousAlignment = childStyle.resolvedAlignSelf(*oldStyle, selfAlignmentNormalBehaviorFlexibleBox).position(); + if (previousAlignment == ItemPositionStretch && previousAlignment != childStyle.resolvedAlignSelf(newStyle, selfAlignmentNormalBehaviorFlexibleBox).position()) child->setChildNeedsLayout(MarkOnlyThis); } } @@ -259,7 +256,7 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - if (updateLogicalWidthAndColumnWidth()) + if (recomputeLogicalWidth()) relayoutChildren = true; LayoutUnit previousHeight = logicalHeight(); @@ -267,34 +264,30 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); - prepareShapesAndPaginationBeforeBlockLayout(relayoutChildren); + preparePaginationBeforeBlockLayout(relayoutChildren); m_numberOfInFlowChildrenOnFirstLine = -1; - RenderBlock::startDelayUpdateScrollInfo(); + beginUpdateScrollInfoAfterLayoutTransaction(); dirtyForLayoutFromPercentageHeightDescendants(); - Vector<LineContext> lineContexts; - OrderIterator::OrderValues orderValues; - computeMainAxisPreferredSizes(orderValues); - m_orderIterator.setOrderValues(std::move(orderValues)); + prepareOrderIteratorAndMargins(); ChildFrameRects oldChildRects; appendChildFrameRects(oldChildRects); + Vector<LineContext> lineContexts; layoutFlexItems(relayoutChildren, lineContexts); updateLogicalHeight(); repositionLogicalHeightDependentFlexItems(lineContexts); - RenderBlock::finishDelayUpdateScrollInfo(); + endAndCommitUpdateScrollInfoAfterLayoutTransaction(); if (logicalHeight() != previousHeight) relayoutChildren = true; - layoutPositionedObjects(relayoutChildren || isRoot()); - - updateShapesAfterBlockLayout(); + layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer()); repaintChildrenDuringLayoutIfMoved(oldChildRects); // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to. @@ -340,7 +333,7 @@ void RenderFlexibleBox::repaintChildrenDuringLayoutIfMoved(const ChildFrameRects void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) { for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { - if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect)) + if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect, PaintAsInlineBlock)) return; } } @@ -375,7 +368,7 @@ LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning() return std::max(clientLogicalBottom(), maxChildLogicalBottom); } -bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox& child) const +bool RenderFlexibleBox::hasOrthogonalFlow(const RenderBox& child) const { // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow. return isHorizontalFlow() != child.isHorizontalWritingMode(); @@ -449,12 +442,11 @@ LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight) { if (isColumnFlow()) { - LogicalExtentComputedValues computedValues; LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(); if (contentLogicalHeight > LayoutUnit::max() - borderPaddingAndScrollbar) contentLogicalHeight -= borderPaddingAndScrollbar; LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar; - computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues); + auto computedValues = computeLogicalHeight(borderBoxLogicalHeight, logicalTop()); if (computedValues.m_extent == LayoutUnit::max()) return computedValues.m_extent; return std::max(LayoutUnit::fromPixel(0), computedValues.m_extent - borderPaddingAndScrollbar); @@ -462,18 +454,19 @@ LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHei return contentLogicalWidth(); } -LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox& child, SizeType sizeType, const Length& size) +std::optional<LayoutUnit> RenderFlexibleBox::computeMainAxisExtentForChild(const RenderBox& child, SizeType sizeType, const Length& size) { // FIXME: This is wrong for orthogonal flows. It should use the flexbox's writing-mode, not the child's in order // to figure out the logical height/width. - // FIXME: This is wrong if the height is set to an intrinsic keyword value. computeContentLogicalHeight will return -1. - // Instead, we need to layout the child an get the appropriate height value. - // https://bugs.webkit.org/show_bug.cgi?id=113610 - if (isColumnFlow()) - return child.computeContentLogicalHeight(size); + if (isColumnFlow()) { + // We don't have to check for "auto" here - computeContentLogicalHeight will just return std::nullopt for that case anyway. + if (size.isIntrinsic()) + const_cast<RenderBox&>(child).layoutIfNeeded(); // FIXME: Should not need to do a layout here. + return child.computeContentLogicalHeight(sizeType, size, child.logicalHeight() - child.borderAndPaddingLogicalHeight()); + } // FIXME: Figure out how this should work for regions and pass in the appropriate values. - RenderRegion* region = 0; - return child.computeLogicalWidthInRegionUsing(sizeType, size, contentLogicalWidth(), this, region) - child.borderAndPaddingLogicalWidth(); + RenderRegion* region = nullptr; + return child.computeLogicalWidthInRegionUsing(sizeType, size, contentLogicalWidth(), *this, region) - child.borderAndPaddingLogicalWidth(); } WritingMode RenderFlexibleBox::transformedWritingMode() const @@ -634,7 +627,7 @@ LayoutUnit RenderFlexibleBox::flowAwareMarginAfterForChild(RenderBox& child) con LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox& child) const { - return isHorizontalFlow() ? child.marginHeight() : child.marginWidth(); + return isHorizontalFlow() ? child.verticalMarginExtent() : child.horizontalMarginExtent(); } LayoutUnit RenderFlexibleBox::crossAxisScrollbarExtent() const @@ -657,7 +650,16 @@ void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox& child, const Lay LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox& child) const { - return isHorizontalFlow() ? child.borderAndPaddingWidth() : child.borderAndPaddingHeight(); + return isHorizontalFlow() ? child.horizontalBorderAndPaddingExtent() : child.verticalBorderAndPaddingExtent(); +} + +bool RenderFlexibleBox::mainAxisLengthIsDefinite(const RenderBox& child, const Length& flexBasis) const +{ + if (flexBasis.isAuto()) + return false; + if (flexBasis.isPercentOrCalculated()) + return isColumnFlow() ? bool(child.computePercentageLogicalHeight(flexBasis)) : hasDefiniteLogicalWidth(); + return true; } LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox& child) const @@ -667,7 +669,7 @@ LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox& child) LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox& child, bool hasInfiniteLineLength) { - bool hasOverrideSize = child.hasOverrideWidth() || child.hasOverrideHeight(); + bool hasOverrideSize = child.hasOverrideLogicalContentWidth() || child.hasOverrideLogicalContentHeight(); if (hasOverrideSize) child.clearOverrideSize(); @@ -682,7 +684,7 @@ LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox& ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0); return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child); } - return std::max(LayoutUnit::fromPixel(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis)); + return computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis).value_or(0); } void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren, Vector<LineContext>& lineContexts) @@ -822,9 +824,7 @@ bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox& child, LayoutUni LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox& child) { - LayoutUnit ascent = child.firstLineBaseline(); - if (ascent == -1) - ascent = crossAxisExtentForChild(child); + LayoutUnit ascent = child.firstLineBaseline().value_or(crossAxisExtentForChild(child)); return ascent + flowAwareMarginBeforeForChild(child); } @@ -836,16 +836,12 @@ LayoutUnit RenderFlexibleBox::computeChildMarginValue(const Length& margin) return minimumValueForLength(margin, availableSize); } -void RenderFlexibleBox::computeMainAxisPreferredSizes(OrderIterator::OrderValues& orderValues) +void RenderFlexibleBox::prepareOrderIteratorAndMargins() { - ASSERT(orderValues.isEmpty()); + OrderIteratorPopulator populator(m_orderIterator); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - // Avoid growing the vector for the common-case default value of 0. This optimizes the most common case which is - // one or a few values with the default order 0 - int order = child->style().order(); - if (orderValues.isEmpty() || orderValues.last() != order) - orderValues.append(order); + populator.collectChild(*child); if (child->isOutOfFlowPositioned()) continue; @@ -862,20 +858,113 @@ void RenderFlexibleBox::computeMainAxisPreferredSizes(OrderIterator::OrderValues } } -LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox& child, LayoutUnit childSize) +bool RenderFlexibleBox::crossAxisLengthIsDefinite(const RenderBox& child, const Length& length) const +{ + if (length.isAuto()) + return false; + if (length.isPercentOrCalculated()) + return hasOrthogonalFlow(child) ? hasDefiniteLogicalWidth() : bool(child.computePercentageLogicalHeight(length)); + return length.isFixed(); +} + + +std::optional<LayoutUnit> RenderFlexibleBox::computeMainSizeFromAspectRatioUsing(const RenderBox& child, Length crossSizeLength) const +{ + ASSERT(child.hasAspectRatio()); + ASSERT(child.intrinsicSize().height() > 0); + + std::optional<LayoutUnit> crossSize; + if (crossSizeLength.isFixed()) + crossSize = LayoutUnit(crossSizeLength.value()); + else { + ASSERT(crossSizeLength.isPercentOrCalculated()); + crossSize = hasOrthogonalFlow(child) ? + adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(crossSizeLength, contentWidth())) : + child.computePercentageLogicalHeight(crossSizeLength); + } + + if (!crossSize) + return crossSize; + + const LayoutSize& childIntrinsicSize = child.intrinsicSize(); + double ratio = childIntrinsicSize.width().toFloat() / childIntrinsicSize.height().toFloat(); + if (isHorizontalFlow()) + return LayoutUnit(crossSize.value() * ratio); + return LayoutUnit(crossSize.value() / ratio); +} + +LayoutUnit RenderFlexibleBox::adjustChildSizeForAspectRatioCrossAxisMinAndMax(const RenderBox& child, LayoutUnit childSize) +{ + Length crossMin = isHorizontalFlow() ? child.style().minHeight() : child.style().minWidth(); + Length crossMax = isHorizontalFlow() ? child.style().maxHeight() : child.style().maxWidth(); + + if (crossAxisLengthIsDefinite(child, crossMax)) { + std::optional<LayoutUnit> maxValue = computeMainSizeFromAspectRatioUsing(child, crossMax); + if (maxValue) + childSize = std::min(maxValue.value(), childSize); + } + + if (crossAxisLengthIsDefinite(child, crossMin)) { + std::optional<LayoutUnit> minValue = computeMainSizeFromAspectRatioUsing(child, crossMin); + if (minValue) + childSize = std::max(minValue.value(), childSize); + } + + return childSize; +} + +bool RenderFlexibleBox::useChildAspectRatio(const RenderBox& child) const +{ + if (!child.hasAspectRatio()) + return false; + if (!child.intrinsicSize().height()) + return false; + Length crossSize = isHorizontalFlow() ? child.style().height() : child.style().width(); + return crossAxisLengthIsDefinite(child, crossSize); +} + +LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(const RenderBox& child, LayoutUnit childSize) { Length max = isHorizontalFlow() ? child.style().maxWidth() : child.style().maxHeight(); + std::optional<LayoutUnit> maxExtent = std::nullopt; if (max.isSpecifiedOrIntrinsic()) { - LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); - if (maxExtent != -1 && childSize > maxExtent) - childSize = maxExtent; + maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); + childSize = std::min(childSize, maxExtent.value_or(childSize)); } - + Length min = isHorizontalFlow() ? child.style().minWidth() : child.style().minHeight(); - LayoutUnit minExtent = 0; if (min.isSpecifiedOrIntrinsic()) - minExtent = computeMainAxisExtentForChild(child, MinSize, min); - return std::max(childSize, minExtent); + return std::max(childSize, computeMainAxisExtentForChild(child, MinSize, min).value_or(childSize)); + + if (!isFlexibleBoxImpl() && min.isAuto() && mainAxisOverflowForChild(child) == OVISIBLE && !(isColumnFlow() && is<RenderFlexibleBox>(child))) { + // This is the implementation of CSS flexbox section 4.5 which defines the minimum size of "pure" flex + // items. For any other item the value should be 0, this also includes RenderFlexibleBox's derived clases + // (RenderButton, RenderFullScreen...) because that's just an implementation detail. + // FIXME: For now we don't handle nested column flexboxes. Need to implement better intrinsic + // size handling from the flex box spec first (4.5). + LayoutUnit contentSize = computeMainAxisExtentForChild(child, MinSize, Length(MinContent)).value(); + ASSERT(contentSize >= 0); + if (child.hasAspectRatio() && child.intrinsicSize().height() > 0) + contentSize = adjustChildSizeForAspectRatioCrossAxisMinAndMax(child, contentSize); + contentSize = std::min(contentSize, maxExtent.value_or(contentSize)); + + Length mainSize = isHorizontalFlow() ? child.style().width() : child.style().height(); + if (mainAxisLengthIsDefinite(child, mainSize)) { + LayoutUnit resolvedMainSize = computeMainAxisExtentForChild(child, MainOrPreferredSize, mainSize).value(); + ASSERT(resolvedMainSize >= 0); + LayoutUnit specifiedSize = std::min(resolvedMainSize, maxExtent.value_or(resolvedMainSize)); + return std::max(childSize, std::min(specifiedSize, contentSize)); + } else if (useChildAspectRatio(child)) { + Length crossSizeLength = isHorizontalFlow() ? child.style().height() : child.style().width(); + std::optional<LayoutUnit> transferredSize = computeMainSizeFromAspectRatioUsing(child, crossSizeLength); + if (transferredSize) { + transferredSize = adjustChildSizeForAspectRatioCrossAxisMinAndMax(child, transferredSize.value()); + return std::max(childSize, std::min(transferredSize.value(), contentSize)); + } + } + return std::max(childSize, contentSize); + } + return childSize; } bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent, bool& hasInfiniteLineLength) @@ -893,6 +982,7 @@ bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren bool lineHasInFlowItem = false; + LayoutUnit preferredMainAxisExtentWithMinWidthConstraint; for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) { orderedChildren.append(child); @@ -900,19 +990,26 @@ bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren } LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(*child, hasInfiniteLineLength); - LayoutUnit childMainAxisMarginBoxExtent = mainAxisBorderAndPaddingExtentForChild(*child) + childMainAxisExtent; - childMainAxisMarginBoxExtent += isHorizontalFlow() ? child->marginWidth() : child->marginHeight(); + LayoutUnit borderMarginAndPaddingSpace = mainAxisBorderAndPaddingExtentForChild(*child) + (isHorizontalFlow() ? child->horizontalMarginExtent() : child->verticalMarginExtent()); - if (isMultiline() && preferredMainAxisExtent + childMainAxisMarginBoxExtent > lineBreakLength && lineHasInFlowItem) + LayoutUnit childMainAxisExtentWithMinWidthConstraint = childMainAxisExtent; + if (child->style().logicalMinWidth().isSpecifiedOrIntrinsic()) { + if (auto minWidthForChild = computeMainAxisExtentForChild(*child, MinSize, child->style().logicalMinWidth())) + childMainAxisExtentWithMinWidthConstraint = std::max(childMainAxisExtent, minWidthForChild.value()); + } + preferredMainAxisExtentWithMinWidthConstraint += childMainAxisExtentWithMinWidthConstraint + borderMarginAndPaddingSpace; + + if (isMultiline() && preferredMainAxisExtentWithMinWidthConstraint > lineBreakLength && lineHasInFlowItem) break; orderedChildren.append(child); lineHasInFlowItem = true; - preferredMainAxisExtent += childMainAxisMarginBoxExtent; + + preferredMainAxisExtent += childMainAxisExtent + borderMarginAndPaddingSpace; totalFlexGrow += child->style().flexGrow(); totalWeightedFlexShrink += child->style().flexShrink() * childMainAxisExtent; LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(*child, childMainAxisExtent); - minMaxAppliedMainAxisExtent += childMinMaxAppliedMainAxisExtent - childMainAxisExtent + childMainAxisMarginBoxExtent; + minMaxAppliedMainAxisExtent += childMinMaxAppliedMainAxisExtent + borderMarginAndPaddingSpace; } return true; } @@ -956,7 +1053,7 @@ bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedF else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink)) extraSpace = availableFreeSpace * child.style().flexShrink() * preferredChildSize / totalWeightedFlexShrink; if (std::isfinite(extraSpace)) - childSize += roundedLayoutUnit(extraSpace); + childSize += LayoutUnit::fromFloatRound(extraSpace); LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize); childSizes.append(adjustedChildSize); @@ -979,13 +1076,13 @@ bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedF return !totalViolation; } -static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren) +static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, ContentPosition justifyContent, ContentDistributionType justifyContentDistribution, unsigned numberOfChildren) { - if (justifyContent == JustifyFlexEnd) + if (justifyContent == ContentPositionFlexEnd) return availableFreeSpace; - if (justifyContent == JustifyCenter) + if (justifyContent == ContentPositionCenter) return availableFreeSpace / 2; - if (justifyContent == JustifySpaceAround) { + if (justifyContentDistribution == ContentDistributionSpaceAround) { if (availableFreeSpace > 0 && numberOfChildren) return availableFreeSpace / (2 * numberOfChildren); else @@ -994,12 +1091,12 @@ static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, EJu return 0; } -static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren) +static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, ContentDistributionType justifyContentDistribution, unsigned numberOfChildren) { if (availableFreeSpace > 0 && numberOfChildren > 1) { - if (justifyContent == JustifySpaceBetween) + if (justifyContentDistribution == ContentDistributionSpaceBetween) return availableFreeSpace / (numberOfChildren - 1); - if (justifyContent == JustifySpaceAround) + if (justifyContentDistribution == ContentDistributionSpaceAround) return availableFreeSpace / numberOfChildren; } return 0; @@ -1031,18 +1128,18 @@ void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox& child, Layout } } -EAlignItems RenderFlexibleBox::alignmentForChild(RenderBox& child) const +ItemPosition RenderFlexibleBox::alignmentForChild(RenderBox& child) const { - EAlignItems align = resolveAlignment(&style(), &child.style()); + ItemPosition align = child.style().resolvedAlignSelf(style(), selfAlignmentNormalBehaviorFlexibleBox).position(); - if (align == AlignBaseline && hasOrthogonalFlow(child)) - align = AlignFlexStart; + if (align == ItemPositionBaseline && hasOrthogonalFlow(child)) + align = ItemPositionFlexStart; if (style().flexWrap() == FlexWrapReverse) { - if (align == AlignFlexStart) - align = AlignFlexEnd; - else if (align == AlignFlexEnd) - align = AlignFlexStart; + if (align == ItemPositionFlexStart) + align = ItemPositionFlexEnd; + else if (align == ItemPositionFlexEnd) + align = ItemPositionFlexStart; } return align; @@ -1061,7 +1158,7 @@ size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItem bool RenderFlexibleBox::needToStretchChild(RenderBox& child) { - if (alignmentForChild(child) != AlignStretch) + if (alignmentForChild(child) != ItemPositionStretch) return false; Length crossAxisLength = isHorizontalFlow() ? child.style().height() : child.style().width(); @@ -1074,14 +1171,34 @@ void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox& chil child.updateLogicalHeight(); } +EOverflow RenderFlexibleBox::mainAxisOverflowForChild(const RenderBox& child) const +{ + if (isHorizontalFlow()) + return child.style().overflowX(); + return child.style().overflowY(); +} + +static const StyleContentAlignmentData& contentAlignmentNormalBehaviorFlexibleBox() +{ + // The justify-content property applies along the main axis, but since flexing + // in the main axis is controlled by flex, stretch behaves as flex-start (ignoring + // the specified fallback alignment, if any). + // https://drafts.csswg.org/css-align/#distribution-flex + static const StyleContentAlignmentData normalBehavior = {ContentPositionNormal, ContentDistributionStretch}; + return normalBehavior; +} + void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts) { ASSERT(childSizes.size() == children.size()); + auto position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehaviorFlexibleBox()); + auto distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehaviorFlexibleBox()); + size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children); LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace); LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart(); - mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent); + mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, position, distribution, numberOfChildrenForJustifyContent); if (style().flexDirection() == FlowRowReverse) mainAxisOffset += isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight(); @@ -1112,7 +1229,7 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons updateAutoMarginsInMainAxis(child, autoMarginOffset); LayoutUnit childCrossAxisMarginBoxExtent; - if (alignmentForChild(child) == AlignBaseline && !hasAutoMarginsInCrossAxis(child)) { + if (alignmentForChild(child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(child)) { LayoutUnit ascent = marginBoxAscentForChild(child); LayoutUnit descent = (crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child)) - ascent; @@ -1138,7 +1255,7 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons ++seenInFlowPositionedChildren; if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) - mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent); + mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, distribution, numberOfChildrenForJustifyContent); } if (isColumnFlow()) @@ -1159,12 +1276,15 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace) { + auto position = style().resolvedJustifyContentPosition(contentAlignmentNormalBehaviorFlexibleBox()); + auto distribution = style().resolvedJustifyContentDistribution(contentAlignmentNormalBehaviorFlexibleBox()); + // This is similar to the logic in layoutAndPlaceChildren, except we place the children // starting from the end of the flexbox. We also don't need to layout anything since we're // just moving the children to a new position. size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children); LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd(); - mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent); + mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, position, distribution, numberOfChildrenForJustifyContent); mainAxisOffset -= isHorizontalFlow() ? verticalScrollbarWidth() : horizontalScrollbarHeight(); size_t seenInFlowPositionedChildren = 0; @@ -1182,17 +1302,17 @@ void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, ++seenInFlowPositionedChildren; if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) - mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, style().justifyContent(), numberOfChildrenForJustifyContent); + mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, distribution, numberOfChildrenForJustifyContent); } } -static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines) +static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, ContentPosition alignContent, ContentDistributionType alignContentDistribution, unsigned numberOfLines) { - if (alignContent == AlignContentFlexEnd) + if (alignContent == ContentPositionFlexEnd) return availableFreeSpace; - if (alignContent == AlignContentCenter) + if (alignContent == ContentPositionCenter) return availableFreeSpace / 2; - if (alignContent == AlignContentSpaceAround) { + if (alignContentDistribution == ContentDistributionSpaceAround) { if (availableFreeSpace > 0 && numberOfLines) return availableFreeSpace / (2 * numberOfLines); if (availableFreeSpace < 0) @@ -1201,12 +1321,12 @@ static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, EAlig return 0; } -static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines) +static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, ContentDistributionType alignContentDistribution, unsigned numberOfLines) { if (availableFreeSpace > 0 && numberOfLines > 1) { - if (alignContent == AlignContentSpaceBetween) + if (alignContentDistribution == ContentDistributionSpaceBetween) return availableFreeSpace / (numberOfLines - 1); - if (alignContent == AlignContentSpaceAround || alignContent == AlignContentStretch) + if (alignContentDistribution == ContentDistributionSpaceAround || alignContentDistribution == ContentDistributionStretch) return availableFreeSpace / numberOfLines; } return 0; @@ -1214,7 +1334,10 @@ static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts) { - if (!isMultiline() || style().alignContent() == AlignContentFlexStart) + ContentPosition position = style().resolvedAlignContentPosition(contentAlignmentNormalBehaviorFlexibleBox()); + ContentDistributionType distribution = style().resolvedAlignContentDistribution(contentAlignmentNormalBehaviorFlexibleBox()); + + if (!isMultiline() || position == ContentPositionFlexStart) return; LayoutUnit availableCrossAxisSpace = crossAxisContentExtent(); @@ -1222,16 +1345,16 @@ void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts) availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; RenderBox* child = m_orderIterator.first(); - LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style().alignContent(), lineContexts.size()); + LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, position, distribution, lineContexts.size()); for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { lineContexts[lineNumber].crossAxisOffset += lineOffset; for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) adjustAlignmentForChild(*child, lineOffset); - if (style().alignContent() == AlignContentStretch && availableCrossAxisSpace > 0) + if (distribution == ContentDistributionStretch && availableCrossAxisSpace > 0) lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size()); - lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, style().alignContent(), lineContexts.size()); + lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, distribution, lineContexts.size()); } } @@ -1273,25 +1396,33 @@ void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) continue; switch (alignmentForChild(*child)) { - case AlignAuto: + case ItemPositionAuto: + case ItemPositionNormal: ASSERT_NOT_REACHED(); break; - case AlignStretch: { + case ItemPositionStart: + // FIXME: https://webkit.org/b/135460 - The extended grammar is not supported + // yet for FlexibleBox. + // Defaulting to Stretch for now, as it what most of FlexBox based renders + // expect as default. + ASSERT(RuntimeEnabledFeatures::sharedFeatures().isCSSGridLayoutEnabled()); + FALLTHROUGH; + case ItemPositionStretch: { applyStretchAlignmentToChild(*child, lineCrossAxisExtent); // Since wrap-reverse flips cross start and cross end, strech children should be aligned with the cross end. if (style().flexWrap() == FlexWrapReverse) adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child)); break; } - case AlignFlexStart: + case ItemPositionFlexStart: break; - case AlignFlexEnd: + case ItemPositionFlexEnd: adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child)); break; - case AlignCenter: + case ItemPositionCenter: adjustAlignmentForChild(*child, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) / 2); break; - case AlignBaseline: { + case ItemPositionBaseline: { // FIXME: If we get here in columns, we want the use the descent, except we currently can't get the ascent/descent of orthogonal children. // https://bugs.webkit.org/show_bug.cgi?id=98076 LayoutUnit ascent = marginBoxAscentForChild(*child); @@ -1302,6 +1433,19 @@ void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, *child) - startOffset); break; } + case ItemPositionLastBaseline: + case ItemPositionSelfStart: + case ItemPositionSelfEnd: + case ItemPositionEnd: + case ItemPositionLeft: + case ItemPositionRight: + // FIXME: https://webkit.org/b/135460 - The extended grammar is not supported + // yet for FlexibleBox. + ASSERT(RuntimeEnabledFeatures::sharedFeatures().isCSSGridLayoutEnabled()); + break; + default: + ASSERT_NOT_REACHED(); + break; } } minMarginAfterBaselines.append(minMarginAfterBaseline); @@ -1317,7 +1461,7 @@ void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber]; for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { ASSERT(child); - if (alignmentForChild(*child) == AlignBaseline && !hasAutoMarginsInCrossAxis(*child) && minMarginAfterBaseline) + if (alignmentForChild(*child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(*child) && minMarginAfterBaseline) adjustAlignmentForChild(*child, minMarginAfterBaseline); } } @@ -1329,7 +1473,8 @@ void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUni // FIXME: If the child has orthogonal flow, then it already has an override height set, so use it. if (!hasOrthogonalFlow(child)) { LayoutUnit stretchedLogicalHeight = child.logicalHeight() + availableAlignmentSpaceForChild(lineCrossAxisExtent, child); - LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight); + ASSERT(!child.needsLayout()); + LayoutUnit desiredLogicalHeight = child.constrainLogicalHeightByMinMax(stretchedLogicalHeight, child.logicalHeight() - child.borderAndPaddingLogicalHeight()); // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905. if (desiredLogicalHeight != child.logicalHeight()) { @@ -1343,7 +1488,7 @@ void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox& child, LayoutUni // FIXME: If the child doesn't have orthogonal flow, then it already has an override width set, so use it. if (hasOrthogonalFlow(child)) { LayoutUnit childWidth = std::max<LayoutUnit>(0, lineCrossAxisExtent - crossAxisMarginExtentForChild(child)); - childWidth = child.constrainLogicalWidthInRegionByMinMax(childWidth, childWidth, this); + childWidth = child.constrainLogicalWidthInRegionByMinMax(childWidth, childWidth, *this); if (childWidth != child.logicalWidth()) { child.setOverrideLogicalContentWidth(childWidth - child.borderAndPaddingLogicalWidth()); |