diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
commit | 2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47 (patch) | |
tree | 988e8c5b116dd0466244ae2fe5af8ee9be926d76 /Source/WebCore/rendering/RenderFlexibleBox.cpp | |
parent | dd91e772430dc294e3bf478c119ef8d43c0a3358 (diff) | |
download | qtwebkit-2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47.tar.gz |
Imported WebKit commit 7e538425aa020340619e927792f3d895061fb54b (http://svn.webkit.org/repository/webkit/trunk@116286)
Diffstat (limited to 'Source/WebCore/rendering/RenderFlexibleBox.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderFlexibleBox.cpp | 548 |
1 files changed, 341 insertions, 207 deletions
diff --git a/Source/WebCore/rendering/RenderFlexibleBox.cpp b/Source/WebCore/rendering/RenderFlexibleBox.cpp index e9dd8381d..efa2eb71d 100644 --- a/Source/WebCore/rendering/RenderFlexibleBox.cpp +++ b/Source/WebCore/rendering/RenderFlexibleBox.cpp @@ -102,38 +102,30 @@ private: Vector<int>::const_iterator m_orderValuesIterator; }; -struct RenderFlexibleBox::WrapReverseContext { - explicit WrapReverseContext(EFlexWrap flexWrap) - : isWrapReverse(flexWrap == FlexWrapReverse) +struct RenderFlexibleBox::LineContext { + LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, size_t numberOfChildren, LayoutUnit maxAscent) + : crossAxisOffset(crossAxisOffset) + , crossAxisExtent(crossAxisExtent) + , numberOfChildren(numberOfChildren) + , maxAscent(maxAscent) { } - void addCrossAxisOffset(LayoutUnit offset) - { - if (!isWrapReverse) - return; - crossAxisOffsets.append(offset); - } - - void addNumberOfChildrenOnLine(size_t numberOfChildren) - { - if (!isWrapReverse) - return; - childrenPerLine.append(numberOfChildren); - } + LayoutUnit crossAxisOffset; + LayoutUnit crossAxisExtent; + size_t numberOfChildren; + LayoutUnit maxAscent; +}; - LayoutUnit lineCrossAxisDelta(size_t line, LayoutUnit crossAxisContentExtent) const +struct RenderFlexibleBox::Violation { + Violation(RenderBox* child, LayoutUnit childSize) + : child(child) + , childSize(childSize) { - ASSERT(line + 1 < crossAxisOffsets.size()); - LayoutUnit lineHeight = crossAxisOffsets[line + 1] - crossAxisOffsets[line]; - LayoutUnit originalOffset = crossAxisOffsets[line] - crossAxisOffsets[0]; - LayoutUnit newOffset = crossAxisContentExtent - originalOffset - lineHeight; - return newOffset - originalOffset; } - WTF::Vector<LayoutUnit> crossAxisOffsets; - WTF::Vector<size_t> childrenPerLine; - bool isWrapReverse; + RenderBox* child; + LayoutUnit childSize; }; @@ -188,11 +180,19 @@ void RenderFlexibleBox::computePreferredLogicalWidths() minPreferredLogicalWidth += margin; maxPreferredLogicalWidth += margin; if (!isColumnFlow()) { - m_minPreferredLogicalWidth += minPreferredLogicalWidth; m_maxPreferredLogicalWidth += maxPreferredLogicalWidth; + if (isMultiline()) { + // For multiline, the min preferred width is if you put a break between each item. + m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, minPreferredLogicalWidth); + } else + m_minPreferredLogicalWidth += minPreferredLogicalWidth; } else { m_minPreferredLogicalWidth = std::max(minPreferredLogicalWidth, m_minPreferredLogicalWidth); - m_maxPreferredLogicalWidth = std::max(maxPreferredLogicalWidth, m_maxPreferredLogicalWidth); + if (isMultiline()) { + // For multiline, the max preferred width is if you put a break between each item. + m_maxPreferredLogicalWidth += maxPreferredLogicalWidth; + } else + m_maxPreferredLogicalWidth = std::max(maxPreferredLogicalWidth, m_maxPreferredLogicalWidth); } } @@ -230,7 +230,7 @@ void RenderFlexibleBox::computePreferredLogicalWidths() setPreferredLogicalWidthsDirty(false); } -void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass) +void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) { ASSERT(needsLayout()); @@ -247,12 +247,10 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass) } computeInitialRegionRangeForBlock(); - IntSize previousSize = size(); + LayoutSize previousSize = size(); setLogicalHeight(0); - // We need to call both of these because we grab both crossAxisExtent and mainAxisExtent in layoutFlexItems. computeLogicalWidth(); - computeLogicalHeight(); m_overflow.clear(); @@ -264,10 +262,15 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass) layer()->setHasVerticalScrollbar(true); } - layoutFlexItems(relayoutChildren); + WTF::Vector<LineContext> lineContexts; + FlexOrderHashSet flexOrderValues; + computeMainAxisPreferredSizes(relayoutChildren, flexOrderValues); + FlexOrderIterator flexIterator(this, flexOrderValues); + layoutFlexItems(flexIterator, lineContexts); LayoutUnit oldClientAfterEdge = clientLogicalBottom(); computeLogicalHeight(); + repositionLogicalHeightDependentFlexItems(flexIterator, lineContexts, oldClientAfterEdge); if (size() != previousSize) relayoutChildren = true; @@ -284,13 +287,35 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass) // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. - updateScrollInfoAfterLayout(); + if (hasOverflowClip()) + layer()->updateScrollInfoAfterLayout(); repainter.repaintAfterLayout(); setNeedsLayout(false); } +void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(FlexOrderIterator& iterator, WTF::Vector<LineContext>& lineContexts, LayoutUnit& oldClientAfterEdge) +{ + LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? ZERO_LAYOUT_UNIT : lineContexts[0].crossAxisOffset; + packFlexLines(iterator, lineContexts); + + // If we have a single line flexbox, the line height is all the available space. + // For flex-direction: row, this means we need to use the height, so we do this after calling computeLogicalHeight. + if (!isMultiline() && lineContexts.size() == 1) + lineContexts[0].crossAxisExtent = crossAxisContentExtent(); + alignChildren(iterator, lineContexts); + + if (style()->flexWrap() == FlexWrapReverse) { + if (isHorizontalFlow()) + oldClientAfterEdge = clientLogicalBottom(); + flipForWrapReverse(iterator, lineContexts, crossAxisStartEdge); + } + + // direction:rtl + flex-direction:column means the cross-axis direction is flipped. + flipForRightToLeftColumn(iterator); +} + bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox* child) const { // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow. @@ -321,9 +346,12 @@ bool RenderFlexibleBox::isMultiline() const return style()->flexWrap() != FlexWrapNone; } -Length RenderFlexibleBox::mainAxisLengthForChild(RenderBox* child) const +Length RenderFlexibleBox::preferredLengthForChild(RenderBox* child) const { - return isHorizontalFlow() ? child->style()->width() : child->style()->height(); + Length flexLength = child->style()->flexPreferredSize(); + if (flexLength.isAuto()) + flexLength = isHorizontalFlow() ? child->style()->width() : child->style()->height(); + return flexLength; } Length RenderFlexibleBox::crossAxisLength() const @@ -560,62 +588,58 @@ LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox* child) LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* child) const { - Length mainAxisLength = mainAxisLengthForChild(child); + Length mainAxisLength = preferredLengthForChild(child); if (mainAxisLength.isAuto()) { LayoutUnit mainAxisExtent = hasOrthogonalFlow(child) ? child->logicalHeight() : child->maxPreferredLogicalWidth(); - return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) - mainAxisScrollbarExtentForChild(child); + return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child); } - return mainAxisLength.calcMinValue(mainAxisContentExtent()); + return minimumValueForLength(mainAxisLength, mainAxisContentExtent(), view()); } -void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren) +LayoutUnit RenderFlexibleBox::computeAvailableFreeSpace(LayoutUnit preferredMainAxisExtent) { - FlexOrderHashSet flexOrderValues; - computeMainAxisPreferredSizes(relayoutChildren, flexOrderValues); + LayoutUnit contentExtent = 0; + if (!isColumnFlow()) + contentExtent = mainAxisContentExtent(); + else if (hasOverrideHeight()) + contentExtent = overrideHeight() - (logicalHeight() - contentLogicalHeight()); + else { + LayoutUnit heightResult = computeContentLogicalHeightUsing(style()->logicalHeight()); + if (heightResult == -1) + heightResult = preferredMainAxisExtent; + LayoutUnit minHeight = computeContentLogicalHeightUsing(style()->logicalMinHeight()); // Leave as -1 if unset. + LayoutUnit maxHeight = style()->logicalMaxHeight().isUndefined() ? heightResult : computeContentLogicalHeightUsing(style()->logicalMaxHeight()); + if (maxHeight == -1) + maxHeight = heightResult; + heightResult = std::min(maxHeight, heightResult); + heightResult = std::max(minHeight, heightResult); + contentExtent = heightResult; + } + return contentExtent - preferredMainAxisExtent; +} + +void RenderFlexibleBox::layoutFlexItems(FlexOrderIterator& iterator, WTF::Vector<LineContext>& lineContexts) +{ OrderedFlexItemList orderedChildren; LayoutUnit preferredMainAxisExtent; float totalPositiveFlexibility; float totalNegativeFlexibility; - FlexOrderIterator flexIterator(this, flexOrderValues); - - // For wrap-reverse, we need to layout as wrap, then reverse the lines. The next two arrays - // are some extra information so it's possible to reverse the lines. - WrapReverseContext wrapReverseContext(style()->flexWrap()); + LayoutUnit minMaxAppliedMainAxisExtent; LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore(); - LayoutUnit mainAxisFlexibleSpace = mainAxisContentExtent(); - while (computeNextFlexLine(flexIterator, orderedChildren, preferredMainAxisExtent, totalPositiveFlexibility, totalNegativeFlexibility)) { - LayoutUnit availableFreeSpace = mainAxisFlexibleSpace - preferredMainAxisExtent; + while (computeNextFlexLine(iterator, orderedChildren, preferredMainAxisExtent, totalPositiveFlexibility, totalNegativeFlexibility, minMaxAppliedMainAxisExtent)) { + LayoutUnit availableFreeSpace = computeAvailableFreeSpace(preferredMainAxisExtent); + FlexSign flexSign = (minMaxAppliedMainAxisExtent < preferredMainAxisExtent + availableFreeSpace) ? PositiveFlexibility : NegativeFlexibility; InflexibleFlexItemSize inflexibleItems; WTF::Vector<LayoutUnit> childSizes; - while (!runFreeSpaceAllocationAlgorithm(orderedChildren, availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems, childSizes)) { + while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems, childSizes)) { ASSERT(totalPositiveFlexibility >= 0 && totalNegativeFlexibility >= 0); ASSERT(inflexibleItems.size() > 0); } - wrapReverseContext.addNumberOfChildrenOnLine(orderedChildren.size()); - wrapReverseContext.addCrossAxisOffset(crossAxisOffset); - layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace); - } - - if (wrapReverseContext.isWrapReverse) { - wrapReverseContext.addCrossAxisOffset(crossAxisOffset); - flipForWrapReverse(flexIterator, wrapReverseContext); + layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, lineContexts); } - - // direction:rtl + flex-direction:column means the cross-axis direction is flipped. - flipForRightToLeftColumn(flexIterator); -} - -float RenderFlexibleBox::positiveFlexForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->style()->flexboxWidthPositiveFlex() : child->style()->flexboxHeightPositiveFlex(); -} - -float RenderFlexibleBox::negativeFlexForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->style()->flexboxWidthNegativeFlex() : child->style()->flexboxHeightNegativeFlex(); } LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox* child) @@ -635,6 +659,7 @@ LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox* child) void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, FlexOrderHashSet& flexOrderValues) { LayoutUnit flexboxAvailableContentExtent = mainAxisContentExtent(); + RenderView* renderView = view(); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { flexOrderValues.add(child->style()->flexOrder()); @@ -642,7 +667,7 @@ void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, Fle continue; child->clearOverrideSize(); - if (mainAxisLengthForChild(child).isAuto()) { + if (preferredLengthForChild(child).isAuto()) { if (!relayoutChildren) child->setChildNeedsLayout(true); child->layoutIfNeeded(); @@ -652,52 +677,100 @@ void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, Fle // of 0 and because if we're not auto sizing, we don't do a layout that // computes the start/end margins. if (isHorizontalFlow()) { - child->setMarginLeft(child->style()->marginLeft().calcMinValue(flexboxAvailableContentExtent)); - child->setMarginRight(child->style()->marginRight().calcMinValue(flexboxAvailableContentExtent)); + child->setMarginLeft(minimumValueForLength(child->style()->marginLeft(), flexboxAvailableContentExtent, renderView)); + child->setMarginRight(minimumValueForLength(child->style()->marginRight(), flexboxAvailableContentExtent, renderView)); } else { - child->setMarginTop(child->style()->marginTop().calcMinValue(flexboxAvailableContentExtent)); - child->setMarginBottom(child->style()->marginBottom().calcMinValue(flexboxAvailableContentExtent)); + child->setMarginTop(minimumValueForLength(child->style()->marginTop(), flexboxAvailableContentExtent, renderView)); + child->setMarginBottom(minimumValueForLength(child->style()->marginBottom(), flexboxAvailableContentExtent, renderView)); } } } -bool RenderFlexibleBox::computeNextFlexLine(FlexOrderIterator& iterator, OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, float& totalPositiveFlexibility, float& totalNegativeFlexibility) +LayoutUnit RenderFlexibleBox::lineBreakLength() +{ + if (!isColumnFlow()) + return mainAxisContentExtent(); + + LayoutUnit height = computeContentLogicalHeightUsing(style()->logicalHeight()); + if (height == -1) + height = MAX_LAYOUT_UNIT; + LayoutUnit maxHeight = computeContentLogicalHeightUsing(style()->logicalMaxHeight()); + if (maxHeight != -1) + height = std::min(height, maxHeight); + return height; +} + +LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox* child, LayoutUnit childSize, LayoutUnit flexboxAvailableContentExtent) +{ + Length max = isHorizontalFlow() ? child->style()->maxWidth() : child->style()->maxHeight(); + Length min = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight(); + RenderView* renderView = view(); + // FIXME: valueForLength isn't quite right in quirks mode: percentage heights should check parents until a value is found. + // https://bugs.webkit.org/show_bug.cgi?id=81809 + if (max.isSpecified() && childSize > valueForLength(max, flexboxAvailableContentExtent, renderView)) + childSize = valueForLength(max, flexboxAvailableContentExtent, renderView); + if (min.isSpecified() && childSize < valueForLength(min, flexboxAvailableContentExtent, renderView)) + childSize = valueForLength(min, flexboxAvailableContentExtent, renderView); + return childSize; +} + +bool RenderFlexibleBox::computeNextFlexLine(FlexOrderIterator& iterator, OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, float& totalPositiveFlexibility, float& totalNegativeFlexibility, LayoutUnit& minMaxAppliedMainAxisExtent) { orderedChildren.clear(); preferredMainAxisExtent = 0; totalPositiveFlexibility = totalNegativeFlexibility = 0; + minMaxAppliedMainAxisExtent = 0; if (!iterator.currentChild()) return false; + LayoutUnit flexboxAvailableContentExtent = mainAxisContentExtent(); + LayoutUnit lineBreak = lineBreakLength(); + for (RenderBox* child = iterator.currentChild(); child; child = iterator.next()) { if (child->isPositioned()) { orderedChildren.append(child); continue; } - LayoutUnit childMainAxisExtent = mainAxisBorderAndPaddingExtentForChild(child) + preferredMainAxisContentExtentForChild(child); - if (isHorizontalFlow()) - childMainAxisExtent += child->marginWidth(); - else - childMainAxisExtent += child->marginHeight(); + LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(child); + LayoutUnit childMainAxisMarginBoxExtent = mainAxisBorderAndPaddingExtentForChild(child) + childMainAxisExtent; + childMainAxisMarginBoxExtent += isHorizontalFlow() ? child->marginWidth() : child->marginHeight(); - if (isMultiline() && preferredMainAxisExtent + childMainAxisExtent > mainAxisContentExtent() && orderedChildren.size() > 0) + if (isMultiline() && preferredMainAxisExtent + childMainAxisMarginBoxExtent > lineBreak && orderedChildren.size() > 0) break; orderedChildren.append(child); - preferredMainAxisExtent += childMainAxisExtent; - totalPositiveFlexibility += positiveFlexForChild(child); - totalNegativeFlexibility += negativeFlexForChild(child); + preferredMainAxisExtent += childMainAxisMarginBoxExtent; + totalPositiveFlexibility += child->style()->positiveFlex(); + totalNegativeFlexibility += child->style()->negativeFlex(); + + LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(child, childMainAxisExtent, flexboxAvailableContentExtent); + minMaxAppliedMainAxisExtent += childMinMaxAppliedMainAxisExtent - childMainAxisExtent + childMainAxisMarginBoxExtent; } return true; } +void RenderFlexibleBox::freezeViolations(const WTF::Vector<Violation>& violations, LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems) +{ + for (size_t i = 0; i < violations.size(); ++i) { + RenderBox* child = violations[i].child; + LayoutUnit childSize = violations[i].childSize; + availableFreeSpace -= childSize - preferredMainAxisContentExtentForChild(child); + totalPositiveFlexibility -= child->style()->positiveFlex(); + totalNegativeFlexibility -= child->style()->negativeFlex(); + inflexibleItems.set(child, childSize); + } +} + // Returns true if we successfully ran the algorithm and sized the flex items. -bool RenderFlexibleBox::runFreeSpaceAllocationAlgorithm(const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes) +bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes) { childSizes.clear(); - LayoutUnit flexboxAvailableContentExtent = mainAxisContentExtent(); + LayoutUnit totalViolation = 0; + LayoutUnit usedFreeSpace = 0; + WTF::Vector<Violation> minViolations; + WTF::Vector<Violation> maxViolations; for (size_t i = 0; i < children.size(); ++i) { RenderBox* child = children[i]; if (child->isPositioned()) { @@ -705,59 +778,53 @@ bool RenderFlexibleBox::runFreeSpaceAllocationAlgorithm(const OrderedFlexItemLis continue; } - LayoutUnit childPreferredSize; if (inflexibleItems.contains(child)) - childPreferredSize = inflexibleItems.get(child); + childSizes.append(inflexibleItems.get(child)); else { - childPreferredSize = preferredMainAxisContentExtentForChild(child); - if (availableFreeSpace > 0 && totalPositiveFlexibility > 0) { - childPreferredSize += lroundf(availableFreeSpace * positiveFlexForChild(child) / totalPositiveFlexibility); - - Length childLogicalMaxWidth = isHorizontalFlow() ? child->style()->maxWidth() : child->style()->maxHeight(); - if (!childLogicalMaxWidth.isUndefined() && childLogicalMaxWidth.isSpecified() && childPreferredSize > childLogicalMaxWidth.calcValue(flexboxAvailableContentExtent)) { - childPreferredSize = childLogicalMaxWidth.calcValue(flexboxAvailableContentExtent); - availableFreeSpace -= childPreferredSize - preferredMainAxisContentExtentForChild(child); - totalPositiveFlexibility -= positiveFlexForChild(child); - - inflexibleItems.set(child, childPreferredSize); - return false; - } - } else if (availableFreeSpace < 0 && totalNegativeFlexibility > 0) { - childPreferredSize += lroundf(availableFreeSpace * negativeFlexForChild(child) / totalNegativeFlexibility); - - Length childLogicalMinWidth = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight(); - if (!childLogicalMinWidth.isUndefined() && childLogicalMinWidth.isSpecified() && childPreferredSize < childLogicalMinWidth.calcValue(flexboxAvailableContentExtent)) { - childPreferredSize = childLogicalMinWidth.calcValue(flexboxAvailableContentExtent); - availableFreeSpace += preferredMainAxisContentExtentForChild(child) - childPreferredSize; - totalNegativeFlexibility -= negativeFlexForChild(child); - - inflexibleItems.set(child, childPreferredSize); - return false; - } - } + LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child); + LayoutUnit childSize = preferredChildSize; + if (availableFreeSpace > 0 && totalPositiveFlexibility > 0 && flexSign == PositiveFlexibility) + childSize += lroundf(availableFreeSpace * child->style()->positiveFlex() / totalPositiveFlexibility); + else if (availableFreeSpace < 0 && totalNegativeFlexibility > 0 && flexSign == NegativeFlexibility) + childSize += lroundf(availableFreeSpace * child->style()->negativeFlex() / totalNegativeFlexibility); + + LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize, flexboxAvailableContentExtent); + childSizes.append(adjustedChildSize); + usedFreeSpace += adjustedChildSize - preferredChildSize; + + LayoutUnit violation = adjustedChildSize - childSize; + if (violation > 0) + minViolations.append(Violation(child, adjustedChildSize)); + else if (violation < 0) + maxViolations.append(Violation(child, adjustedChildSize)); + totalViolation += violation; } - childSizes.append(childPreferredSize); } - return true; + + if (totalViolation) + freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems); + else + availableFreeSpace -= usedFreeSpace; + + return !totalViolation; } -static LayoutUnit initialPackingOffset(LayoutUnit availableFreeSpace, EFlexPack flexPack, size_t numberOfChildren) +static LayoutUnit initialPackingOffset(LayoutUnit availableFreeSpace, EFlexPack flexPack, unsigned numberOfChildren) { - if (availableFreeSpace > 0) { - if (flexPack == PackEnd) - return availableFreeSpace; - if (flexPack == PackCenter) - return availableFreeSpace / 2; - if (flexPack == PackDistribute && numberOfChildren) + if (flexPack == PackEnd) + return availableFreeSpace; + if (flexPack == PackCenter) + return availableFreeSpace / 2; + if (flexPack == PackDistribute) { + if (availableFreeSpace > 0 && numberOfChildren) return availableFreeSpace / (2 * numberOfChildren); - } else if (availableFreeSpace < 0) { - if (flexPack == PackCenter || flexPack == PackDistribute) + if (availableFreeSpace < 0) return availableFreeSpace / 2; } return 0; } -static LayoutUnit packingSpaceBetweenChildren(LayoutUnit availableFreeSpace, EFlexPack flexPack, size_t numberOfChildren) +static LayoutUnit packingSpaceBetweenChildren(LayoutUnit availableFreeSpace, EFlexPack flexPack, unsigned numberOfChildren) { if (availableFreeSpace > 0 && numberOfChildren > 1) { if (flexPack == PackJustify) @@ -791,7 +858,7 @@ void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox* child, Layout if (childLayer->staticBlockPosition() != staticBlockPosition) { childLayer->setStaticBlockPosition(staticBlockPosition); if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) - child->setChildNeedsLayout(true, false); + child->setChildNeedsLayout(true, MarkOnlyThis); } } @@ -811,8 +878,9 @@ static EFlexAlign flexAlignForChild(RenderBox* child) return align; } -void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace) +void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, WTF::Vector<LineContext>& lineContexts) { + ASSERT(childSizes.size() == children.size()); LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart(); mainAxisOffset += initialPackingOffset(availableFreeSpace, style()->flexPack(), childSizes.size()); if (style()->flexDirection() == FlowRowReverse) @@ -845,8 +913,8 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons childCrossAxisMarginBoxExtent = maxAscent + maxDescent; } else childCrossAxisMarginBoxExtent = crossAxisExtentForChild(child) + crossAxisMarginExtentForChild(child); - if (crossAxisLength().isAuto()) - setCrossAxisExtent(std::max(crossAxisExtent(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent())); + if (!isColumnFlow() && style()->logicalHeight().isAuto()) + setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent())); maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent); mainAxisOffset += flowAwareMarginStartForChild(child); @@ -872,10 +940,8 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons layoutColumnReverse(children, childSizes, crossAxisOffset, availableFreeSpace); } - LayoutUnit lineCrossAxisExtent = isMultiline() ? maxChildCrossAxisExtent : crossAxisContentExtent(); - alignChildren(children, lineCrossAxisExtent, maxAscent); - - crossAxisOffset += lineCrossAxisExtent; + lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, children.size(), maxAscent)); + crossAxisOffset += maxChildCrossAxisExtent; } void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace) @@ -906,6 +972,55 @@ void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, } } +static LayoutUnit initialLinePackingOffset(LayoutUnit availableFreeSpace, EFlexLinePack linePack, unsigned numberOfLines) +{ + if (linePack == LinePackEnd) + return availableFreeSpace; + if (linePack == LinePackCenter) + return availableFreeSpace / 2; + if (linePack == LinePackDistribute) { + if (availableFreeSpace > 0 && numberOfLines) + return availableFreeSpace / (2 * numberOfLines); + if (availableFreeSpace < 0) + return availableFreeSpace / 2; + } + return 0; +} + +static LayoutUnit linePackingSpaceBetweenChildren(LayoutUnit availableFreeSpace, EFlexLinePack linePack, unsigned numberOfLines) +{ + if (availableFreeSpace > 0 && numberOfLines > 1) { + if (linePack == LinePackJustify) + return availableFreeSpace / (numberOfLines - 1); + if (linePack == LinePackDistribute || linePack == LinePackStretch) + return availableFreeSpace / numberOfLines; + } + return 0; +} + +void RenderFlexibleBox::packFlexLines(FlexOrderIterator& iterator, WTF::Vector<LineContext>& lineContexts) +{ + if (!isMultiline() || style()->flexLinePack() == LinePackStart) + return; + + LayoutUnit availableCrossAxisSpace = crossAxisContentExtent(); + for (size_t i = 0; i < lineContexts.size(); ++i) + availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; + + RenderBox* child = iterator.first(); + LayoutUnit lineOffset = initialLinePackingOffset(availableCrossAxisSpace, style()->flexLinePack(), 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 = iterator.next()) + adjustAlignmentForChild(child, lineOffset); + + if (style()->flexLinePack() == LinePackStretch && availableCrossAxisSpace > 0) + lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast<unsigned>(lineContexts.size()); + + lineOffset += linePackingSpaceBetweenChildren(availableCrossAxisSpace, style()->flexLinePack(), lineContexts.size()); + } +} + void RenderFlexibleBox::adjustAlignmentForChild(RenderBox* child, LayoutUnit delta) { LayoutRect oldRect = child->frameRect(); @@ -919,72 +1034,94 @@ void RenderFlexibleBox::adjustAlignmentForChild(RenderBox* child, LayoutUnit del child->repaintDuringLayoutIfMoved(oldRect); } -void RenderFlexibleBox::alignChildren(const OrderedFlexItemList& children, LayoutUnit lineCrossAxisExtent, LayoutUnit maxAscent) +void RenderFlexibleBox::alignChildren(FlexOrderIterator& iterator, const WTF::Vector<LineContext>& lineContexts) { - LayoutUnit minMarginAfterBaseline = std::numeric_limits<LayoutUnit>::max(); - - for (size_t i = 0; i < children.size(); ++i) { - RenderBox* child = children[i]; - switch (flexAlignForChild(child)) { - case AlignAuto: - ASSERT_NOT_REACHED(); - break; - case AlignStretch: { - if (!isColumnFlow() && child->style()->logicalHeight().isAuto()) { - LayoutUnit logicalHeightBefore = child->logicalHeight(); - LayoutUnit stretchedLogicalHeight = child->logicalHeight() + availableAlignmentSpaceForChild(lineCrossAxisExtent, child); - child->setLogicalHeight(stretchedLogicalHeight); - child->computeLogicalHeight(); - - if (child->logicalHeight() != logicalHeightBefore) { - child->setOverrideHeight(child->logicalHeight()); - child->setLogicalHeight(0); - child->setChildNeedsLayout(true); - child->layoutIfNeeded(); - } - } else if (isColumnFlow() && child->style()->logicalWidth().isAuto() && isMultiline()) { - // FIXME: Handle min-width and max-width. - LayoutUnit childWidth = lineCrossAxisExtent - crossAxisMarginExtentForChild(child); - child->setOverrideWidth(std::max(0, childWidth)); - child->setChildNeedsLayout(true); - child->layoutIfNeeded(); + // Keep track of the space between the baseline edge and the after edge of the box for each line. + WTF::Vector<LayoutUnit> minMarginAfterBaselines; + + RenderBox* child = iterator.first(); + for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { + LayoutUnit minMarginAfterBaseline = MAX_LAYOUT_UNIT; + LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; + LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent; + + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + ASSERT(child); + switch (flexAlignForChild(child)) { + case AlignAuto: + ASSERT_NOT_REACHED(); + break; + case AlignStretch: { + 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; } - // Since wrap-reverse flips cross start and cross end, strech children should be aligned with the cross end. - if (style()->flexWrap() == FlexWrapReverse) + case AlignStart: + break; + case AlignEnd: adjustAlignmentForChild(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child)); - break; - } - case AlignStart: - break; - case AlignEnd: - adjustAlignmentForChild(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child)); - break; - case AlignCenter: - adjustAlignmentForChild(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child) / 2); - break; - case AlignBaseline: { - LayoutUnit ascent = marginBoxAscentForChild(child); - LayoutUnit startOffset = maxAscent - ascent; - adjustAlignmentForChild(child, startOffset); - - if (style()->flexWrap() == FlexWrapReverse) - minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, child) - startOffset); - break; - } + break; + case AlignCenter: + adjustAlignmentForChild(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child) / 2); + break; + case AlignBaseline: { + LayoutUnit ascent = marginBoxAscentForChild(child); + LayoutUnit startOffset = maxAscent - ascent; + adjustAlignmentForChild(child, startOffset); + + if (style()->flexWrap() == FlexWrapReverse) + minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, child) - startOffset); + break; + } + } } + minMarginAfterBaselines.append(minMarginAfterBaseline); } + if (style()->flexWrap() != FlexWrapReverse) + return; + // wrap-reverse flips the cross axis start and end. For baseline alignment, this means we // need to align the after edge of baseline elements with the after edge of the flex line. - if (style()->flexWrap() == FlexWrapReverse && minMarginAfterBaseline) { - for (size_t i = 0; i < children.size(); ++i) { - RenderBox* child = children[i]; - if (flexAlignForChild(child) == AlignBaseline) + child = iterator.first(); + for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { + LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber]; + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + ASSERT(child); + if (flexAlignForChild(child) == AlignBaseline && minMarginAfterBaseline) adjustAlignmentForChild(child, minMarginAfterBaseline); } } } +void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox* child, LayoutUnit lineCrossAxisExtent) +{ + if (!isColumnFlow() && child->style()->logicalHeight().isAuto()) { + LayoutUnit logicalHeightBefore = child->logicalHeight(); + LayoutUnit stretchedLogicalHeight = child->logicalHeight() + availableAlignmentSpaceForChild(lineCrossAxisExtent, child); + if (stretchedLogicalHeight < logicalHeightBefore) + return; + + child->setLogicalHeight(stretchedLogicalHeight); + child->computeLogicalHeight(); + + if (child->logicalHeight() != logicalHeightBefore) { + child->setOverrideHeight(child->logicalHeight()); + child->setLogicalHeight(0); + child->setChildNeedsLayout(true); + child->layoutIfNeeded(); + } + } else if (isColumnFlow() && child->style()->logicalWidth().isAuto() && isMultiline()) { + // FIXME: Handle min-width and max-width. + LayoutUnit childWidth = lineCrossAxisExtent - crossAxisMarginExtentForChild(child); + child->setOverrideWidth(std::max(ZERO_LAYOUT_UNIT, childWidth)); + child->setChildNeedsLayout(true); + child->layoutIfNeeded(); + } +} + void RenderFlexibleBox::flipForRightToLeftColumn(FlexOrderIterator& iterator) { if (style()->isLeftToRightDirection() || !isColumnFlow()) @@ -998,26 +1135,23 @@ void RenderFlexibleBox::flipForRightToLeftColumn(FlexOrderIterator& iterator) } } -void RenderFlexibleBox::flipForWrapReverse(FlexOrderIterator& iterator, const WrapReverseContext& wrapReverseContext) +void RenderFlexibleBox::flipForWrapReverse(FlexOrderIterator& iterator, const WTF::Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge) { - if (!isColumnFlow()) - computeLogicalHeight(); - - size_t currentChild = 0; - size_t lineNumber = 0; LayoutUnit contentExtent = crossAxisContentExtent(); - for (RenderBox* child = iterator.first(); child; child = iterator.next()) { - LayoutPoint location = flowAwareLocationForChild(child); - location.setY(location.y() + wrapReverseContext.lineCrossAxisDelta(lineNumber, contentExtent)); - - LayoutRect oldRect = child->frameRect(); - setFlowAwareLocationForChild(child, location); - if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) - child->repaintDuringLayoutIfMoved(oldRect); - - if (++currentChild == wrapReverseContext.childrenPerLine[lineNumber]) { - ++lineNumber; - currentChild = 0; + RenderBox* child = iterator.first(); + for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + ASSERT(child); + LayoutPoint location = flowAwareLocationForChild(child); + LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; + LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge; + LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent; + location.setY(location.y() + newOffset - originalOffset); + + LayoutRect oldRect = child->frameRect(); + setFlowAwareLocationForChild(child, location); + if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldRect); } } } |