diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderFlexibleBox.cpp')
| -rw-r--r-- | Source/WebCore/rendering/RenderFlexibleBox.cpp | 501 |
1 files changed, 294 insertions, 207 deletions
diff --git a/Source/WebCore/rendering/RenderFlexibleBox.cpp b/Source/WebCore/rendering/RenderFlexibleBox.cpp index e11d865f1..c95e37e2d 100644 --- a/Source/WebCore/rendering/RenderFlexibleBox.cpp +++ b/Source/WebCore/rendering/RenderFlexibleBox.cpp @@ -49,59 +49,52 @@ struct RenderFlexibleBox::OrderHashTraits : WTF::GenericHashTraits<int> { static bool isDeletedValue(int value) { return value == std::numeric_limits<int>::min() + 1; } }; -class RenderFlexibleBox::OrderIterator { -public: - OrderIterator(RenderFlexibleBox* flexibleBox, const OrderHashSet& orderValues) - : m_flexibleBox(flexibleBox) - , m_currentChild(0) - , m_orderValuesIterator(0) - { - copyToVector(orderValues, m_orderValues); - std::sort(m_orderValues.begin(), m_orderValues.end()); - first(); - } +RenderFlexibleBox::OrderIterator::OrderIterator(const RenderFlexibleBox* flexibleBox) + : m_flexibleBox(flexibleBox) + , m_currentChild(0) + , m_orderValuesIterator(0) +{ +} - RenderBox* currentChild() { return m_currentChild; } +void RenderFlexibleBox::OrderIterator::setOrderValues(const OrderHashSet& orderValues) +{ + reset(); + copyToVector(orderValues, m_orderValues); + std::sort(m_orderValues.begin(), m_orderValues.end()); +} - RenderBox* first() - { - reset(); - return next(); - } +RenderBox* RenderFlexibleBox::OrderIterator::first() +{ + reset(); + return next(); +} - RenderBox* next() - { - do { - if (!m_currentChild) { +RenderBox* RenderFlexibleBox::OrderIterator::next() +{ + do { + if (!m_currentChild) { + if (m_orderValuesIterator == m_orderValues.end()) + return 0; + if (m_orderValuesIterator) { + ++m_orderValuesIterator; if (m_orderValuesIterator == m_orderValues.end()) return 0; - if (m_orderValuesIterator) { - ++m_orderValuesIterator; - if (m_orderValuesIterator == m_orderValues.end()) - return 0; - } else - m_orderValuesIterator = m_orderValues.begin(); - - m_currentChild = m_flexibleBox->firstChildBox(); } else - m_currentChild = m_currentChild->nextSiblingBox(); - } while (!m_currentChild || m_currentChild->style()->order() != *m_orderValuesIterator); + m_orderValuesIterator = m_orderValues.begin(); - return m_currentChild; - } + m_currentChild = m_flexibleBox->firstChildBox(); + } else + m_currentChild = m_currentChild->nextSiblingBox(); + } while (!m_currentChild || m_currentChild->style()->order() != *m_orderValuesIterator); - void reset() - { - m_currentChild = 0; - m_orderValuesIterator = 0; - } + return m_currentChild; +} -private: - RenderFlexibleBox* m_flexibleBox; - RenderBox* m_currentChild; - Vector<int> m_orderValues; - Vector<int>::const_iterator m_orderValuesIterator; -}; +void RenderFlexibleBox::OrderIterator::reset() +{ + m_currentChild = 0; + m_orderValuesIterator = 0; +} struct RenderFlexibleBox::LineContext { LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, size_t numberOfChildren, LayoutUnit maxAscent) @@ -130,8 +123,9 @@ struct RenderFlexibleBox::Violation { }; -RenderFlexibleBox::RenderFlexibleBox(Node* node) - : RenderBlock(node) +RenderFlexibleBox::RenderFlexibleBox(Element* element) + : RenderBlock(element) + , m_orderIterator(this) , m_numberOfInFlowChildrenOnFirstLine(-1) { setChildrenInline(false); // All of our children must be block-level. @@ -141,6 +135,13 @@ RenderFlexibleBox::~RenderFlexibleBox() { } +RenderFlexibleBox* RenderFlexibleBox::createAnonymous(Document* document) +{ + RenderFlexibleBox* renderer = new (document->renderArena()) RenderFlexibleBox(0); + renderer->setDocumentForAnonymous(document); + return renderer; +} + const char* RenderFlexibleBox::renderName() const { return "RenderFlexibleBox"; @@ -161,60 +162,57 @@ static LayoutUnit marginLogicalWidthForChild(RenderBox* child, RenderStyle* pare return margin; } +void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start honoring it though until + // the flex shorthand stops setting it to 0. + // See https://bugs.webkit.org/show_bug.cgi?id=116117, + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->isOutOfFlowPositioned()) + continue; + + LayoutUnit margin = marginLogicalWidthForChild(child, style()); + bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode(); + LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth(); + LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth(); + minPreferredLogicalWidth += margin; + maxPreferredLogicalWidth += margin; + if (!isColumnFlow()) { + maxLogicalWidth += maxPreferredLogicalWidth; + if (isMultiline()) { + // For multiline, the min preferred width is if you put a break between each item. + minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth); + } else + minLogicalWidth += minPreferredLogicalWidth; + } else { + minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth); + if (isMultiline()) { + // For multiline, the max preferred width is if you never break between items. + maxLogicalWidth += maxPreferredLogicalWidth; + } else + maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth); + } + } + + maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); + + LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth(); + maxLogicalWidth += scrollbarWidth; + minLogicalWidth += scrollbarWidth; +} + void RenderFlexibleBox::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; + RenderStyle* styleToUse = style(); // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for width. if (styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() > 0) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value()); - else { - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; - - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isOutOfFlowPositioned()) - continue; - - LayoutUnit margin = marginLogicalWidthForChild(child, style()); - bool hasOrthogonalWritingMode = child->isHorizontalWritingMode() != isHorizontalWritingMode(); - LayoutUnit minPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->minPreferredLogicalWidth(); - LayoutUnit maxPreferredLogicalWidth = hasOrthogonalWritingMode ? child->logicalHeight() : child->maxPreferredLogicalWidth(); - minPreferredLogicalWidth += margin; - maxPreferredLogicalWidth += margin; - if (!isColumnFlow()) { - 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); - 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); - } - } - - m_maxPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - } - - LayoutUnit scrollbarWidth = 0; - if (hasOverflowClip()) { - if (isHorizontalWritingMode() && styleToUse->overflowY() == OSCROLL) { - ASSERT(layer()->hasVerticalScrollbar()); - scrollbarWidth = verticalScrollbarWidth(); - } else if (!isHorizontalWritingMode() && styleToUse->overflowX() == OSCROLL) { - ASSERT(layer()->hasHorizontalScrollbar()); - scrollbarWidth = horizontalScrollbarHeight(); - } - } - - m_maxPreferredLogicalWidth += scrollbarWidth; - m_minPreferredLogicalWidth += scrollbarWidth; + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width. if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { @@ -252,13 +250,11 @@ int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode di int RenderFlexibleBox::firstLineBoxBaseline() const { - ASSERT(m_orderIterator); - if (isWritingModeRoot() || m_numberOfInFlowChildrenOnFirstLine <= 0) return -1; RenderBox* baselineChild = 0; int childNumber = 0; - for (RenderBox* child = m_orderIterator->first(); child; child = m_orderIterator->next()) { + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) continue; if (alignmentForChild(child) == AlignBaseline && !hasAutoMarginsInCrossAxis(child)) { @@ -302,6 +298,29 @@ int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const 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) { + // 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. + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + EAlignItems previousAlignment = resolveAlignment(oldStyle, child->style()); + if (previousAlignment == AlignStretch && previousAlignment != resolveAlignment(style(), child->style())) + child->setChildNeedsLayout(true, MarkOnlyThis); + } + } +} + void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) { ASSERT(needsLayout()); @@ -310,47 +329,52 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) return; LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - if (inRenderFlowThread()) { - // Regions changing widths can force us to relayout our children. - if (logicalWidthChangedInRegions()) - relayoutChildren = true; - } - updateRegionsAndExclusionsLogicalSize(); + if (updateLogicalWidthAndColumnWidth()) + relayoutChildren = true; - LayoutSize previousSize = size(); + LayoutUnit previousHeight = logicalHeight(); + setLogicalHeight(borderAndPaddingLogicalHeight() + scrollbarLogicalHeight()); + + LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - setLogicalHeight(0); - updateLogicalWidth(); + // Regions changing widths can force us to relayout our children. + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (logicalWidthChangedInRegions(flowThread)) + relayoutChildren = true; + if (updateRegionsAndShapesBeforeChildLayout(flowThread)) + relayoutChildren = true; m_numberOfInFlowChildrenOnFirstLine = -1; - m_overflow.clear(); RenderBlock::startDelayUpdateScrollInfo(); - WTF::Vector<LineContext> lineContexts; + dirtyForLayoutFromPercentageHeightDescendants(); + + Vector<LineContext> lineContexts; OrderHashSet orderValues; - computeMainAxisPreferredSizes(relayoutChildren, orderValues); - m_orderIterator = adoptPtr(new OrderIterator(this, orderValues)); - layoutFlexItems(*m_orderIterator, lineContexts); + computeMainAxisPreferredSizes(orderValues); + m_orderIterator.setOrderValues(orderValues); + + ChildFrameRects oldChildRects; + appendChildFrameRects(oldChildRects); + layoutFlexItems(relayoutChildren, lineContexts); - LayoutUnit oldClientAfterEdge = clientLogicalBottom(); updateLogicalHeight(); - repositionLogicalHeightDependentFlexItems(*m_orderIterator, lineContexts, oldClientAfterEdge); + repositionLogicalHeightDependentFlexItems(lineContexts); RenderBlock::finishDelayUpdateScrollInfo(); - if (size() != previousSize) + if (logicalHeight() != previousHeight) relayoutChildren = true; layoutPositionedObjects(relayoutChildren || isRoot()); - computeRegionRangeForBlock(); - clearChildOverrideSizes(); + updateRegionsAndShapesAfterChildLayout(flowThread); + repaintChildrenDuringLayoutIfMoved(oldChildRects); // FIXME: css3/flexbox/repaint-rtl-column.html seems to repaint more overflow than it needs to. - computeOverflow(oldClientAfterEdge); + computeOverflow(clientLogicalBottomAfterRepositioning()); statePusher.pop(); updateLayerTransform(); @@ -364,41 +388,67 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit) setNeedsLayout(false); } -void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) +void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects) { - ASSERT(m_orderIterator); + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { + if (!child->isOutOfFlowPositioned()) + childFrameRects.append(child->frameRect()); + } +} - for (RenderBox* child = m_orderIterator->first(); child; child = m_orderIterator->next()) { +void RenderFlexibleBox::repaintChildrenDuringLayoutIfMoved(const ChildFrameRects& oldChildRects) +{ + size_t childIndex = 0; + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { + if (child->isOutOfFlowPositioned()) + continue; + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]); + ++childIndex; + } + ASSERT(childIndex == oldChildRects.size()); +} + +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)) return; } } -void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(OrderIterator& iterator, WTF::Vector<LineContext>& lineContexts, LayoutUnit& oldClientAfterEdge) +void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector<LineContext>& lineContexts) { LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset; - alignFlexLines(iterator, lineContexts); + alignFlexLines(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 updateLogicalHeight. if (!isMultiline() && lineContexts.size() == 1) lineContexts[0].crossAxisExtent = crossAxisContentExtent(); - alignChildren(iterator, lineContexts); + alignChildren(lineContexts); - if (style()->flexWrap() == FlexWrapReverse) { - if (isHorizontalFlow()) - oldClientAfterEdge = clientLogicalBottom(); - flipForWrapReverse(iterator, lineContexts, crossAxisStartEdge); - } + if (style()->flexWrap() == FlexWrapReverse) + flipForWrapReverse(lineContexts, crossAxisStartEdge); // direction:rtl + flex-direction:column means the cross-axis direction is flipped. - flipForRightToLeftColumn(iterator); + flipForRightToLeftColumn(); } -void RenderFlexibleBox::clearChildOverrideSizes() +LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning() { - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) - child->clearOverrideSize(); + LayoutUnit maxChildLogicalBottom = 0; + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->isOutOfFlowPositioned()) + continue; + LayoutUnit childLogicalBottom = logicalTopForChild(child) + logicalHeightForChild(child) + marginAfterForChild(child); + maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom); + } + return std::max(clientLogicalBottom(), maxChildLogicalBottom); } bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox* child) const @@ -476,8 +526,14 @@ LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHei { if (isColumnFlow()) { LogicalExtentComputedValues computedValues; - computeLogicalHeight(contentLogicalHeight, logicalTop(), computedValues); - return std::max(LayoutUnit(0), computedValues.m_extent - borderAndPaddingLogicalHeight() - scrollbarLogicalHeight()); + LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(); + if (contentLogicalHeight > LayoutUnit::max() - borderPaddingAndScrollbar) + contentLogicalHeight -= borderPaddingAndScrollbar; + LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar; + computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues); + if (computedValues.m_extent == LayoutUnit::max()) + return computedValues.m_extent; + return std::max(LayoutUnit(0), computedValues.m_extent - borderPaddingAndScrollbar); } return contentLogicalWidth(); } @@ -486,9 +542,14 @@ LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox* child, Si { // 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(sizeType, size); - return child->adjustContentBoxLogicalWidthForBoxSizing(valueForLength(size, contentLogicalWidth(), view())); + return child->computeContentLogicalHeight(size); + // 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(); } WritingMode RenderFlexibleBox::transformedWritingMode() const @@ -664,21 +725,10 @@ LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox* child) const void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox* child, const LayoutPoint& location) { - LayoutRect oldFrameRect = child->frameRect(); - if (isHorizontalFlow()) child->setLocation(location); else child->setLocation(location.transposedPoint()); - - // If the child moved, we have to repaint it as well as any floating/positioned - // descendants. An exception is if we need a layout. In this case, we know we're going to - // repaint ourselves (and the child) anyway. - // FIXME: In some cases, we might overpaint as we move a child multiple times. We could reduce - // overpainting by keeping track of the original position of a child and running this check on - // the final position. - if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) - child->repaintDuringLayoutIfMoved(oldFrameRect); } LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox* child) const @@ -691,10 +741,19 @@ LayoutUnit RenderFlexibleBox::mainAxisScrollbarExtentForChild(RenderBox* child) return isHorizontalFlow() ? child->verticalScrollbarWidth() : child->horizontalScrollbarHeight(); } -LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* child) +LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* child, bool hasInfiniteLineLength) { + bool hasOverrideSize = child->hasOverrideWidth() || child->hasOverrideHeight(); + if (hasOverrideSize) + child->clearOverrideSize(); + Length flexBasis = flexBasisForChild(child); - if (flexBasis.isAuto()) { + if (flexBasis.isAuto() || (flexBasis.isFixed() && !flexBasis.value() && hasInfiniteLineLength)) { + if (hasOrthogonalFlow(child)) { + if (hasOverrideSize) + child->setChildNeedsLayout(true, MarkOnlyThis); + child->layoutIfNeeded(); + } LayoutUnit mainAxisExtent = hasOrthogonalFlow(child) ? child->logicalHeight() : child->maxPreferredLogicalWidth(); ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0); return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child); @@ -702,7 +761,7 @@ LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* return std::max(LayoutUnit(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis)); } -void RenderFlexibleBox::layoutFlexItems(OrderIterator& iterator, WTF::Vector<LineContext>& lineContexts) +void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren, Vector<LineContext>& lineContexts) { OrderedFlexItemList orderedChildren; LayoutUnit preferredMainAxisExtent; @@ -710,18 +769,31 @@ void RenderFlexibleBox::layoutFlexItems(OrderIterator& iterator, WTF::Vector<Lin double totalWeightedFlexShrink; LayoutUnit minMaxAppliedMainAxisExtent; + m_orderIterator.first(); LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore(); - while (computeNextFlexLine(iterator, orderedChildren, preferredMainAxisExtent, totalFlexGrow, totalWeightedFlexShrink, minMaxAppliedMainAxisExtent)) { + bool hasInfiniteLineLength = false; + while (computeNextFlexLine(orderedChildren, preferredMainAxisExtent, totalFlexGrow, totalWeightedFlexShrink, minMaxAppliedMainAxisExtent, hasInfiniteLineLength)) { LayoutUnit availableFreeSpace = mainAxisContentExtent(preferredMainAxisExtent) - preferredMainAxisExtent; FlexSign flexSign = (minMaxAppliedMainAxisExtent < preferredMainAxisExtent + availableFreeSpace) ? PositiveFlexibility : NegativeFlexibility; InflexibleFlexItemSize inflexibleItems; - WTF::Vector<LayoutUnit> childSizes; - while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes)) { + Vector<LayoutUnit> childSizes; + while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes, hasInfiniteLineLength)) { ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0); ASSERT(inflexibleItems.size() > 0); } - layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, lineContexts); + layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, relayoutChildren, lineContexts); + } + if (hasLineIfEmpty()) { + // Even if computeNextFlexLine returns true, the flexbox might not have + // a line because all our children might be out of flow positioned. + // Instead of just checking if we have a line, make sure the flexbox + // has at least a line's worth of height to cover this case. + LayoutUnit minHeight = borderAndPaddingLogicalHeight() + + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) + + scrollbarLogicalHeight(); + if (height() < minHeight) + setLogicalHeight(minHeight); } } @@ -758,6 +830,8 @@ LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const OrderedFlexItemLi void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox* child, LayoutUnit autoMarginOffset) { + ASSERT(autoMarginOffset >= 0); + if (isHorizontalFlow()) { if (child->style()->marginLeft().isAuto()) child->setMarginLeft(autoMarginOffset); @@ -788,6 +862,7 @@ LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCro bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox* child, LayoutUnit availableAlignmentSpace) { ASSERT(!child->isOutOfFlowPositioned()); + ASSERT(availableAlignmentSpace >= 0); bool isHorizontal = isHorizontalFlow(); Length start = isHorizontal ? child->style()->marginTop() : child->style()->marginLeft(); @@ -837,7 +912,7 @@ LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin, RenderView* return minimumValueForLength(margin, availableSize, view); } -void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, OrderHashSet& orderValues) +void RenderFlexibleBox::computeMainAxisPreferredSizes(OrderHashSet& orderValues) { RenderView* renderView = view(); for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { @@ -846,14 +921,6 @@ void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, Ord if (child->isOutOfFlowPositioned()) continue; - // Only need to layout here if we will need to get the logicalHeight of the child in computeNextFlexLine. - Length childMainAxisMin = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight(); - if (hasOrthogonalFlow(child) && (flexBasisForChild(child).isAuto() || childMainAxisMin.isAuto())) { - if (!relayoutChildren) - child->setChildNeedsLayout(true, MarkOnlyThis); - child->layoutIfNeeded(); - } - // Before running the flex algorithm, 'auto' has a margin of 0. // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins. if (isHorizontalFlow()) { @@ -868,9 +935,8 @@ void RenderFlexibleBox::computeMainAxisPreferredSizes(bool relayoutChildren, Ord LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox* child, LayoutUnit childSize) { - // FIXME: Support intrinsic min/max lengths. Length max = isHorizontalFlow() ? child->style()->maxWidth() : child->style()->maxHeight(); - if (max.isSpecified()) { + if (max.isSpecifiedOrIntrinsic()) { LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); if (maxExtent != -1 && childSize > maxExtent) childSize = maxExtent; @@ -878,35 +944,33 @@ LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox* child, Layo Length min = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight(); LayoutUnit minExtent = 0; - if (min.isSpecified()) + if (min.isSpecifiedOrIntrinsic()) minExtent = computeMainAxisExtentForChild(child, MinSize, min); - else if (min.isAuto()) { - minExtent = hasOrthogonalFlow(child) ? child->logicalHeight() : child->minPreferredLogicalWidth(); - minExtent -= mainAxisBorderAndPaddingExtentForChild(child); - } return std::max(childSize, minExtent); } -bool RenderFlexibleBox::computeNextFlexLine(OrderIterator& iterator, OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent) +bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& preferredMainAxisExtent, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& minMaxAppliedMainAxisExtent, bool& hasInfiniteLineLength) { orderedChildren.clear(); preferredMainAxisExtent = 0; totalFlexGrow = totalWeightedFlexShrink = 0; minMaxAppliedMainAxisExtent = 0; - if (!iterator.currentChild()) + if (!m_orderIterator.currentChild()) return false; LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max()); + hasInfiniteLineLength = lineBreakLength == LayoutUnit::max(); + bool lineHasInFlowItem = false; - for (RenderBox* child = iterator.currentChild(); child; child = iterator.next()) { + for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) { orderedChildren.append(child); continue; } - LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(child); + LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); LayoutUnit childMainAxisMarginBoxExtent = mainAxisBorderAndPaddingExtentForChild(child) + childMainAxisExtent; childMainAxisMarginBoxExtent += isHorizontalFlow() ? child->marginWidth() : child->marginHeight(); @@ -924,12 +988,12 @@ bool RenderFlexibleBox::computeNextFlexLine(OrderIterator& iterator, OrderedFlex return true; } -void RenderFlexibleBox::freezeViolations(const WTF::Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems) +void RenderFlexibleBox::freezeViolations(const Vector<Violation>& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, bool hasInfiniteLineLength) { for (size_t i = 0; i < violations.size(); ++i) { RenderBox* child = violations[i].child; LayoutUnit childSize = violations[i].childSize; - LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child); + LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); availableFreeSpace -= childSize - preferredChildSize; totalFlexGrow -= child->style()->flexGrow(); totalWeightedFlexShrink -= child->style()->flexShrink() * preferredChildSize; @@ -938,13 +1002,13 @@ void RenderFlexibleBox::freezeViolations(const WTF::Vector<Violation>& violation } // Returns true if we successfully ran the algorithm and sized the flex items. -bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes) +bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, Vector<LayoutUnit>& childSizes, bool hasInfiniteLineLength) { childSizes.clear(); LayoutUnit totalViolation = 0; LayoutUnit usedFreeSpace = 0; - WTF::Vector<Violation> minViolations; - WTF::Vector<Violation> maxViolations; + Vector<Violation> minViolations; + Vector<Violation> maxViolations; for (size_t i = 0; i < children.size(); ++i) { RenderBox* child = children[i]; if (child->isOutOfFlowPositioned()) { @@ -955,12 +1019,15 @@ bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedF if (inflexibleItems.contains(child)) childSizes.append(inflexibleItems.get(child)); else { - LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child); + LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); LayoutUnit childSize = preferredChildSize; - if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && isfinite(totalFlexGrow)) - childSize += roundedLayoutUnit(availableFreeSpace * child->style()->flexGrow() / totalFlexGrow); - else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && isfinite(totalWeightedFlexShrink)) - childSize += roundedLayoutUnit(availableFreeSpace * child->style()->flexShrink() * preferredChildSize / totalWeightedFlexShrink); + double extraSpace = 0; + if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow)) + extraSpace = availableFreeSpace * child->style()->flexGrow() / totalFlexGrow; + 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); LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize); childSizes.append(adjustedChildSize); @@ -976,7 +1043,7 @@ bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedF } if (totalViolation) - freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems); + freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, hasInfiniteLineLength); else availableFreeSpace -= usedFreeSpace; @@ -1037,9 +1104,7 @@ void RenderFlexibleBox::prepareChildForPositionedLayout(RenderBox* child, Layout EAlignItems RenderFlexibleBox::alignmentForChild(RenderBox* child) const { - EAlignItems align = child->style()->alignSelf(); - if (align == AlignAuto) - align = style()->alignItems(); + EAlignItems align = resolveAlignment(style(), child->style()); if (align == AlignBaseline && hasOrthogonalFlow(child)) align = AlignFlexStart; @@ -1065,7 +1130,22 @@ size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItem return count; } -void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, WTF::Vector<LineContext>& lineContexts) +bool RenderFlexibleBox::needToStretchChild(RenderBox* child) +{ + if (alignmentForChild(child) != AlignStretch) + return false; + + Length crossAxisLength = isHorizontalFlow() ? child->style()->height() : child->style()->width(); + return crossAxisLength.isAuto(); +} + +void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox* child) +{ + if (hasAutoMarginsInCrossAxis(child)) + child->updateLogicalHeight(); +} + +void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector<LineContext>& lineContexts) { ASSERT(childSizes.size() == children.size()); @@ -1087,10 +1167,17 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons prepareChildForPositionedLayout(child, mainAxisOffset, crossAxisOffset, FlipForRowReverse); continue; } + LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(child); setLogicalOverrideSize(child, childPreferredSize); // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905. - child->setChildNeedsLayout(true, MarkOnlyThis); + if (needToStretchChild(child) || childPreferredSize != mainAxisExtentForChild(child)) + child->setChildNeedsLayout(true, MarkOnlyThis); + else { + // To avoid double applying margin changes in updateAutoMarginsInCrossAxis, we reset the margins here. + resetAutoMarginsAndLogicalTopInCrossAxis(child); + } + updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child); child->layoutIfNeeded(); updateAutoMarginsInMainAxis(child, autoMarginOffset); @@ -1106,7 +1193,7 @@ void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, cons childCrossAxisMarginBoxExtent = maxAscent + maxDescent; } else childCrossAxisMarginBoxExtent = crossAxisExtentForChild(child) + crossAxisMarginExtentForChild(child); - if (!isColumnFlow() && style()->logicalHeight().isAuto()) + if (!isColumnFlow()) setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent + crossAxisScrollbarExtent())); maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent); @@ -1196,7 +1283,7 @@ static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace return 0; } -void RenderFlexibleBox::alignFlexLines(OrderIterator& iterator, WTF::Vector<LineContext>& lineContexts) +void RenderFlexibleBox::alignFlexLines(Vector<LineContext>& lineContexts) { if (!isMultiline() || style()->alignContent() == AlignContentFlexStart) return; @@ -1205,11 +1292,11 @@ void RenderFlexibleBox::alignFlexLines(OrderIterator& iterator, WTF::Vector<Line for (size_t i = 0; i < lineContexts.size(); ++i) availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; - RenderBox* child = iterator.first(); + RenderBox* child = m_orderIterator.first(); LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style()->alignContent(), 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()) + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) adjustAlignmentForChild(child, lineOffset); if (style()->alignContent() == AlignContentStretch && availableCrossAxisSpace > 0) @@ -1234,18 +1321,18 @@ void RenderFlexibleBox::adjustAlignmentForChild(RenderBox* child, LayoutUnit del setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta)); } -void RenderFlexibleBox::alignChildren(OrderIterator& iterator, const WTF::Vector<LineContext>& lineContexts) +void RenderFlexibleBox::alignChildren(const Vector<LineContext>& lineContexts) { // Keep track of the space between the baseline edge and the after edge of the box for each line. - WTF::Vector<LayoutUnit> minMarginAfterBaselines; + Vector<LayoutUnit> minMarginAfterBaselines; - RenderBox* child = iterator.first(); + RenderBox* child = m_orderIterator.first(); for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { LayoutUnit minMarginAfterBaseline = LayoutUnit::max(); LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent; - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { ASSERT(child); if (child->isOutOfFlowPositioned()) { if (style()->flexWrap() == FlexWrapReverse) @@ -1253,7 +1340,7 @@ void RenderFlexibleBox::alignChildren(OrderIterator& iterator, const WTF::Vector continue; } - if (updateAutoMarginsInCrossAxis(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child))) + if (updateAutoMarginsInCrossAxis(child, std::max(LayoutUnit(0), availableAlignmentSpaceForChild(lineCrossAxisExtent, child)))) continue; switch (alignmentForChild(child)) { @@ -1296,10 +1383,10 @@ void RenderFlexibleBox::alignChildren(OrderIterator& iterator, const WTF::Vector // 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. - child = iterator.first(); + child = m_orderIterator.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()) { + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { ASSERT(child); if (alignmentForChild(child) == AlignBaseline && !hasAutoMarginsInCrossAxis(child) && minMarginAfterBaseline) adjustAlignmentForChild(child, minMarginAfterBaseline); @@ -1320,7 +1407,7 @@ void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox* child, LayoutUni child->setOverrideLogicalContentHeight(desiredLogicalHeight - child->borderAndPaddingLogicalHeight()); child->setLogicalHeight(0); child->setChildNeedsLayout(true, MarkOnlyThis); - child->layoutIfNeeded(); + child->layout(); } } } else if (isColumnFlow() && child->style()->logicalWidth().isAuto()) { @@ -1332,19 +1419,19 @@ void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox* child, LayoutUni if (childWidth != child->logicalWidth()) { child->setOverrideLogicalContentWidth(childWidth - child->borderAndPaddingLogicalWidth()); child->setChildNeedsLayout(true, MarkOnlyThis); - child->layoutIfNeeded(); + child->layout(); } } } } -void RenderFlexibleBox::flipForRightToLeftColumn(OrderIterator& iterator) +void RenderFlexibleBox::flipForRightToLeftColumn() { if (style()->isLeftToRightDirection() || !isColumnFlow()) return; LayoutUnit crossExtent = crossAxisExtent(); - for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { if (child->isOutOfFlowPositioned()) continue; LayoutPoint location = flowAwareLocationForChild(child); @@ -1353,12 +1440,12 @@ void RenderFlexibleBox::flipForRightToLeftColumn(OrderIterator& iterator) } } -void RenderFlexibleBox::flipForWrapReverse(OrderIterator& iterator, const WTF::Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge) +void RenderFlexibleBox::flipForWrapReverse(const Vector<LineContext>& lineContexts, LayoutUnit crossAxisStartEdge) { LayoutUnit contentExtent = crossAxisContentExtent(); - RenderBox* child = iterator.first(); + RenderBox* child = m_orderIterator.first(); for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = iterator.next()) { + for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { ASSERT(child); LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge; |
