| Index: third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
|
| diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
|
| index 00eac215cf1bfc6b54086ba8fb96585d5b80c25a..fb288fdd7ac9688968fc6b1b9dc4a15f057ad6e1 100644
|
| --- a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
|
| +++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp
|
| @@ -162,18 +162,23 @@ class BlockChildrenLayoutInfo {
|
| public:
|
| BlockChildrenLayoutInfo(LayoutBlockFlow* blockFlow, LayoutUnit beforeEdge, LayoutUnit afterEdge)
|
| : m_marginInfo(blockFlow, beforeEdge, afterEdge)
|
| + , m_previousBreakAfterValue(BreakAuto)
|
| , m_isAtFirstInFlowChild(true) { }
|
|
|
| const MarginInfo& marginInfo() const { return m_marginInfo; }
|
| MarginInfo& marginInfo() { return m_marginInfo; }
|
| LayoutUnit& previousFloatLogicalBottom() { return m_previousFloatLogicalBottom; }
|
|
|
| + EBreak previousBreakAfterValue() const { return m_previousBreakAfterValue; }
|
| + void setPreviousBreakAfterValue(EBreak value) { m_previousBreakAfterValue = value; }
|
| +
|
| bool isAtFirstInFlowChild() const { return m_isAtFirstInFlowChild; }
|
| void clearIsAtFirstInFlowChild() { m_isAtFirstInFlowChild = false; }
|
|
|
| private:
|
| MarginInfo m_marginInfo;
|
| LayoutUnit m_previousFloatLogicalBottom;
|
| + EBreak m_previousBreakAfterValue;
|
| bool m_isAtFirstInFlowChild;
|
| };
|
|
|
| @@ -376,7 +381,21 @@ inline bool LayoutBlockFlow::layoutBlockFlow(bool relayoutChildren, LayoutUnit &
|
| initMaxMarginValues();
|
| setHasMarginBeforeQuirk(style()->hasMarginBeforeQuirk());
|
| setHasMarginAfterQuirk(style()->hasMarginAfterQuirk());
|
| + }
|
| +
|
| + if (state.isPaginated()) {
|
| setPaginationStrutPropagatedFromChild(LayoutUnit());
|
| +
|
| + // Start with any applicable computed break-after and break-before values for this
|
| + // object. During child layout, breakBefore will be joined with the breakBefore value of
|
| + // the first in-flow child, and breakAfter will be joined with the breakAfter value of the
|
| + // last in-flow child. This is done in order to honor the requirement that a class A break
|
| + // point [1] may only exists *between* in-flow siblings (i.e. not before the first child
|
| + // and not after the last child).
|
| + //
|
| + // [1] https://drafts.csswg.org/css-break/#possible-breaks
|
| + setBreakBefore(LayoutBlock::breakBefore());
|
| + setBreakAfter(LayoutBlock::breakAfter());
|
| }
|
|
|
| LayoutUnit beforeEdge = borderBefore() + paddingBefore();
|
| @@ -580,6 +599,30 @@ bool LayoutBlockFlow::positionAndLayoutOnceIfNeeded(LayoutBox& child, LayoutUnit
|
| return true;
|
| }
|
|
|
| +bool LayoutBlockFlow::insertForcedBreakBeforeChildIfNeeded(LayoutBox& child, BlockChildrenLayoutInfo& layoutInfo)
|
| +{
|
| + if (layoutInfo.isAtFirstInFlowChild()) {
|
| + // There's no class A break point before the first child (only *between* siblings), so
|
| + // steal its break value and join it with what we already have here.
|
| + setBreakBefore(joinFragmentainerBreakValues(breakBefore(), child.breakBefore()));
|
| + return false;
|
| + }
|
| +
|
| + // Figure out if a forced break should be inserted in front of the child. If we insert a forced
|
| + // break, the margins on this child may not collapse with those preceding the break.
|
| + EBreak classABreakPointValue = child.classABreakPointValue(layoutInfo.previousBreakAfterValue());
|
| + if (isForcedFragmentainerBreakValue(classABreakPointValue)) {
|
| + layoutInfo.marginInfo().clearMargin();
|
| + LayoutUnit oldLogicalTop = logicalHeight();
|
| + LayoutUnit newLogicalTop = applyForcedBreak(oldLogicalTop, classABreakPointValue);
|
| + setLogicalHeight(newLogicalTop);
|
| + LayoutUnit paginationStrut = newLogicalTop - oldLogicalTop;
|
| + child.setPaginationStrut(paginationStrut);
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| void LayoutBlockFlow::layoutBlockChild(LayoutBox& child, BlockChildrenLayoutInfo& layoutInfo)
|
| {
|
| MarginInfo& marginInfo = layoutInfo.marginInfo();
|
| @@ -594,7 +637,7 @@ void LayoutBlockFlow::layoutBlockChild(LayoutBox& child, BlockChildrenLayoutInfo
|
| // be correct. Only if we're wrong (when we compute the real logical top position)
|
| // will we have to potentially relayout.
|
| LayoutUnit estimateWithoutPagination;
|
| - LayoutUnit logicalTopEstimate = estimateLogicalTopPosition(child, marginInfo, estimateWithoutPagination);
|
| + LayoutUnit logicalTopEstimate = estimateLogicalTopPosition(child, layoutInfo, estimateWithoutPagination);
|
|
|
| // Cache our old rect so that we can dirty the proper paint invalidation rects if the child moves.
|
| LayoutRect oldRect = child.frameRect();
|
| @@ -609,6 +652,11 @@ void LayoutBlockFlow::layoutBlockChild(LayoutBox& child, BlockChildrenLayoutInfo
|
| bool childIsSelfCollapsing = child.isSelfCollapsingBlock();
|
| bool childDiscardMarginBefore = mustDiscardMarginBeforeForChild(child);
|
| bool childDiscardMarginAfter = mustDiscardMarginAfterForChild(child);
|
| + bool paginated = view()->layoutState()->isPaginated();
|
| +
|
| + // If there should be a forced break before the child, we need to insert it before attempting
|
| + // to collapse margins or apply clearance.
|
| + bool forcedBreakWasInserted = paginated && insertForcedBreakBeforeChildIfNeeded(child, layoutInfo);
|
|
|
| // Now determine the correct ypos based off examination of collapsing margin
|
| // values.
|
| @@ -618,9 +666,11 @@ void LayoutBlockFlow::layoutBlockChild(LayoutBox& child, BlockChildrenLayoutInfo
|
| bool childDiscardMargin = childDiscardMarginBefore || childDiscardMarginAfter;
|
| LayoutUnit newLogicalTop = clearFloatsIfNeeded(child, marginInfo, oldPosMarginBefore, oldNegMarginBefore, logicalTopBeforeClear, childIsSelfCollapsing, childDiscardMargin);
|
|
|
| - // Now check for pagination.
|
| - bool paginated = view()->layoutState()->isPaginated();
|
| - if (paginated) {
|
| + // If there's a forced break in front of this child, its final position has already been
|
| + // determined. Otherwise, see if there are other reasons for breaking before it
|
| + // (break-inside:avoid, or not enough space for the first piece of child content to fit in the
|
| + // current fragmentainer), and adjust the position accordingly.
|
| + if (paginated && !forcedBreakWasInserted) {
|
| if (estimateWithoutPagination != newLogicalTop) {
|
| // We got a new position due to clearance or margin collapsing. Before we attempt to
|
| // paginate (which may result in the position changing again), let's try again at the
|
| @@ -675,10 +725,9 @@ void LayoutBlockFlow::layoutBlockChild(LayoutBox& child, BlockChildrenLayoutInfo
|
| child.invalidatePaintForOverhangingFloats(true);
|
|
|
| if (paginated) {
|
| - // Check for an after page/column break.
|
| - LayoutUnit newHeight = applyAfterBreak(child, logicalHeight(), marginInfo);
|
| - if (newHeight != size().height())
|
| - setLogicalHeight(newHeight);
|
| + // Keep track of the break-after value of the child, so that it can be joined with the
|
| + // break-before value of the next in-flow object at the next class A break point.
|
| + layoutInfo.setPreviousBreakAfterValue(child.breakAfter());
|
| }
|
|
|
| if (child.isLayoutMultiColumnSpannerPlaceholder()) {
|
| @@ -689,12 +738,16 @@ void LayoutBlockFlow::layoutBlockChild(LayoutBox& child, BlockChildrenLayoutInfo
|
|
|
| LayoutUnit LayoutBlockFlow::adjustBlockChildForPagination(LayoutUnit logicalTop, LayoutBox& child, BlockChildrenLayoutInfo& layoutInfo, bool atBeforeSideOfBlock)
|
| {
|
| + // Forced breaks trumps unforced ones, and if we have a forced break, we shouldn't even be here.
|
| + ASSERT(layoutInfo.isAtFirstInFlowChild() || !isForcedFragmentainerBreakValue(child.classABreakPointValue(layoutInfo.previousBreakAfterValue())));
|
| +
|
| LayoutBlockFlow* childBlockFlow = child.isLayoutBlockFlow() ? toLayoutBlockFlow(&child) : 0;
|
|
|
| - // Calculate the pagination strut for this child. A strut may come from three sources:
|
| + // See if we need a soft (unforced) break in front of this child, and set the pagination strut
|
| + // in that case. An unforced break may come from two sources:
|
| // 1. The first piece of content inside the child doesn't fit in the current page or column
|
| - // 2. A forced break before the child
|
| - // 3. The child itself is unsplittable and doesn't fit in the current page or column.
|
| + // 2. The child itself has breaking restrictions (break-inside:avoid, replaced content, etc.)
|
| + // and doesn't fully fit in the current page or column.
|
| //
|
| // No matter which source, if we need to insert a strut, it should always take us to the exact
|
| // top of a page or column further ahead, or be zero.
|
| @@ -709,15 +762,12 @@ LayoutUnit LayoutBlockFlow::adjustBlockChildForPagination(LayoutUnit logicalTop,
|
| LayoutUnit strutFromContent = childBlockFlow ? childBlockFlow->paginationStrutPropagatedFromChild() : LayoutUnit();
|
| LayoutUnit logicalTopWithContentStrut = logicalTop + strutFromContent;
|
|
|
| - // If the object has a page or column break value of "before", then we should shift to the top of the next page.
|
| - LayoutUnit logicalTopAfterForcedBreak = applyBeforeBreak(child, logicalTop);
|
| -
|
| // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
|
| LayoutUnit logicalTopAfterUnsplittable = adjustForUnsplittableChild(child, logicalTop);
|
|
|
| // Pick the largest offset. Tall unsplittable content may take us to a page or column further
|
| // ahead than the next one.
|
| - LayoutUnit logicalTopAfterPagination = std::max(logicalTopWithContentStrut, std::max(logicalTopAfterForcedBreak, logicalTopAfterUnsplittable));
|
| + LayoutUnit logicalTopAfterPagination = std::max(logicalTopWithContentStrut, logicalTopAfterUnsplittable);
|
| LayoutUnit newLogicalTop = logicalTop;
|
| if (LayoutUnit paginationStrut = logicalTopAfterPagination - logicalTop) {
|
| ASSERT(paginationStrut > 0);
|
| @@ -725,7 +775,7 @@ LayoutUnit LayoutBlockFlow::adjustBlockChildForPagination(LayoutUnit logicalTop,
|
| // first in-flow child, but the child isn't flush with the content edge of its container, due to e.g. clearance,
|
| // there's a class C break point before the child. Otherwise we should propagate the strut to our parent block,
|
| // and attempt to break there instead. See https://drafts.csswg.org/css-break/#possible-breaks
|
| - if (layoutInfo.isAtFirstInFlowChild() && atBeforeSideOfBlock && logicalTopAfterForcedBreak == logicalTop && allowsPaginationStrut()) {
|
| + if (layoutInfo.isAtFirstInFlowChild() && atBeforeSideOfBlock && allowsPaginationStrut()) {
|
| // FIXME: Should really check if we're exceeding the page height before propagating the strut, but we don't
|
| // have all the information to do so (the strut only has the remaining amount to push). Gecko gets this wrong too
|
| // and pushes to the next page anyway, so not too concerned about it.
|
| @@ -1049,7 +1099,7 @@ void LayoutBlockFlow::layoutBlockChildren(bool relayoutChildren, SubtreeLayoutSc
|
|
|
| if (child->isOutOfFlowPositioned()) {
|
| child->containingBlock()->insertPositionedObject(child);
|
| - adjustPositionedBlock(*child, marginInfo);
|
| + adjustPositionedBlock(*child, layoutInfo);
|
| continue;
|
| }
|
| if (child->isFloating()) {
|
| @@ -1331,11 +1381,18 @@ LayoutUnit LayoutBlockFlow::collapseMargins(LayoutBox& child, MarginInfo& margin
|
| return logicalTop;
|
| }
|
|
|
| -void LayoutBlockFlow::adjustPositionedBlock(LayoutBox& child, const MarginInfo& marginInfo)
|
| +void LayoutBlockFlow::adjustPositionedBlock(LayoutBox& child, const BlockChildrenLayoutInfo& layoutInfo)
|
| {
|
| LayoutUnit logicalTop = logicalHeight();
|
| +
|
| + // Forced breaks are only specified on in-flow objects, but auto-positioned out-of-flow objects
|
| + // may be affected by a break-after value of the previous in-flow object.
|
| + if (view()->layoutState()->isPaginated())
|
| + logicalTop = applyForcedBreak(logicalTop, layoutInfo.previousBreakAfterValue());
|
| +
|
| updateStaticInlinePositionForChild(child, logicalTop);
|
|
|
| + const MarginInfo& marginInfo = layoutInfo.marginInfo();
|
| if (!marginInfo.canCollapseWithMarginBefore()) {
|
| // Positioned blocks don't collapse margins, so add the margin provided by
|
| // the container now. The child's own margin is added later when calculating its logical top.
|
| @@ -1493,15 +1550,16 @@ void LayoutBlockFlow::marginBeforeEstimateForChild(LayoutBox& child, LayoutUnit&
|
| childBlockFlow->marginBeforeEstimateForChild(*grandchildBox, positiveMarginBefore, negativeMarginBefore, discardMarginBefore);
|
| }
|
|
|
| -LayoutUnit LayoutBlockFlow::estimateLogicalTopPosition(LayoutBox& child, const MarginInfo& marginInfo, LayoutUnit& estimateWithoutPagination)
|
| +LayoutUnit LayoutBlockFlow::estimateLogicalTopPosition(LayoutBox& child, const BlockChildrenLayoutInfo& layoutInfo, LayoutUnit& estimateWithoutPagination)
|
| {
|
| + const MarginInfo& marginInfo = layoutInfo.marginInfo();
|
| // FIXME: We need to eliminate the estimation of vertical position, because when it's wrong we sometimes trigger a pathological
|
| // relayout if there are intruding floats.
|
| LayoutUnit logicalTopEstimate = logicalHeight();
|
| + LayoutUnit positiveMarginBefore;
|
| + LayoutUnit negativeMarginBefore;
|
| + bool discardMarginBefore = false;
|
| if (!marginInfo.canCollapseWithMarginBefore()) {
|
| - LayoutUnit positiveMarginBefore;
|
| - LayoutUnit negativeMarginBefore;
|
| - bool discardMarginBefore = false;
|
| if (child.selfNeedsLayout()) {
|
| // Try to do a basic estimation of how the collapse is going to go.
|
| marginBeforeEstimateForChild(child, positiveMarginBefore, negativeMarginBefore, discardMarginBefore);
|
| @@ -1530,8 +1588,25 @@ LayoutUnit LayoutBlockFlow::estimateLogicalTopPosition(LayoutBox& child, const M
|
| estimateWithoutPagination = logicalTopEstimate;
|
|
|
| if (layoutState->isPaginated()) {
|
| - // If the object has a page or column break value of "before", then we should shift to the top of the next page.
|
| - logicalTopEstimate = applyBeforeBreak(child, logicalTopEstimate);
|
| + if (!layoutInfo.isAtFirstInFlowChild()) {
|
| + // Estimate the need for a forced break in front of this child. The final break policy
|
| + // at this class A break point isn't known until we have laid out the children of
|
| + // |child|. There may be forced break-before values set on first-children inside that
|
| + // get propagated up to the child. Just make an estimate with what we know so far.
|
| + EBreak breakValue = child.classABreakPointValue(layoutInfo.previousBreakAfterValue());
|
| + if (isForcedFragmentainerBreakValue(breakValue)) {
|
| + logicalTopEstimate = applyForcedBreak(logicalHeight(), breakValue);
|
| + // Disregard previous margins, since they will collapse with the fragmentainer
|
| + // boundary, due to the forced break. Only apply margins that have been specified
|
| + // on the child or its descendants.
|
| + if (!discardMarginBefore)
|
| + logicalTopEstimate += positiveMarginBefore - negativeMarginBefore;
|
| +
|
| + // Clearance may already have taken us past the beginning of the next
|
| + // fragmentainer.
|
| + return std::max(estimateWithoutPagination, logicalTopEstimate);
|
| + }
|
| + }
|
|
|
| // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one.
|
| logicalTopEstimate = adjustForUnsplittableChild(child, logicalTopEstimate);
|
| @@ -1590,6 +1665,13 @@ void LayoutBlockFlow::handleAfterSideOfBlock(LayoutBox* lastChild, LayoutUnit be
|
|
|
| // Update our bottom collapsed margin info.
|
| setCollapsedBottomMargin(marginInfo);
|
| +
|
| + // There's no class A break point right after the last child, only *between* siblings. So
|
| + // propagate the break-after value, and keep looking for a class A break point (at the next
|
| + // in-flow block-level object), where we'll join this break-after value with the break-before
|
| + // value there.
|
| + if (view()->layoutState()->isPaginated() && lastChild)
|
| + setBreakAfter(joinFragmentainerBreakValues(breakAfter(), lastChild->breakAfter()));
|
| }
|
|
|
| void LayoutBlockFlow::setMustDiscardMarginBefore(bool value)
|
| @@ -1707,22 +1789,43 @@ bool LayoutBlockFlow::mustSeparateMarginAfterForChild(const LayoutBox& child) co
|
| return false;
|
| }
|
|
|
| -LayoutUnit LayoutBlockFlow::applyBeforeBreak(LayoutBox& child, LayoutUnit logicalOffset)
|
| +LayoutUnit LayoutBlockFlow::applyForcedBreak(LayoutUnit logicalOffset, EBreak breakValue)
|
| {
|
| - if (child.hasForcedBreakBefore())
|
| + // TODO(mstensho): honor breakValue. There are different types of forced breaks. We currently
|
| + // just assume that we want to break to the top of the next fragmentainer of the fragmentation
|
| + // context we're in. However, we may want to find the next left or right page - even if we're
|
| + // inside a multicol container when printing.
|
| + if (isForcedFragmentainerBreakValue(breakValue))
|
| return nextPageLogicalTop(logicalOffset, AssociateWithFormerPage);
|
| return logicalOffset;
|
| }
|
|
|
| -LayoutUnit LayoutBlockFlow::applyAfterBreak(LayoutBox& child, LayoutUnit logicalOffset, MarginInfo& marginInfo)
|
| +void LayoutBlockFlow::setBreakBefore(EBreak breakValue)
|
| {
|
| - if (child.hasForcedBreakAfter()) {
|
| - // So our margin doesn't participate in the next collapsing steps.
|
| - marginInfo.clearMargin();
|
| + if (breakValue != BreakAuto && !isBreakBetweenControllable(breakValue))
|
| + breakValue = BreakAuto;
|
| + if (breakValue == BreakAuto && !m_rareData)
|
| + return;
|
| + ensureRareData().m_breakBefore = breakValue;
|
| +}
|
|
|
| - return nextPageLogicalTop(logicalOffset, AssociateWithFormerPage);
|
| - }
|
| - return logicalOffset;
|
| +void LayoutBlockFlow::setBreakAfter(EBreak breakValue)
|
| +{
|
| + if (breakValue != BreakAuto && !isBreakBetweenControllable(breakValue))
|
| + breakValue = BreakAuto;
|
| + if (breakValue == BreakAuto && !m_rareData)
|
| + return;
|
| + ensureRareData().m_breakAfter = breakValue;
|
| +}
|
| +
|
| +EBreak LayoutBlockFlow::breakBefore() const
|
| +{
|
| + return m_rareData ? static_cast<EBreak>(m_rareData->m_breakBefore) : BreakAuto;
|
| +}
|
| +
|
| +EBreak LayoutBlockFlow::breakAfter() const
|
| +{
|
| + return m_rareData ? static_cast<EBreak>(m_rareData->m_breakAfter) : BreakAuto;
|
| }
|
|
|
| void LayoutBlockFlow::addOverflowFromFloats()
|
| @@ -2411,6 +2514,14 @@ bool LayoutBlockFlow::positionNewFloats(LineWidth* width)
|
| if (childBox->style()->clear() & ClearRight)
|
| logicalTop = std::max(lowestFloatLogicalBottom(FloatingObject::FloatRight), logicalTop);
|
|
|
| + bool isPaginated = view()->layoutState()->isPaginated();
|
| + if (isPaginated && !childrenInline()) {
|
| + // Forced breaks are inserted at class A break points. Floats may be affected by a
|
| + // break-after value on the previous in-flow sibling.
|
| + if (LayoutBox* previousInFlowBox = childBox->previousInFlowSiblingBox())
|
| + logicalTop = applyForcedBreak(logicalTop, previousInFlowBox->breakAfter());
|
| + }
|
| +
|
| LayoutPoint floatLogicalLocation = computeLogicalLocationForFloat(floatingObject, logicalTop);
|
|
|
| setLogicalLeftForFloat(floatingObject, floatLogicalLocation.x());
|
| @@ -2419,8 +2530,6 @@ bool LayoutBlockFlow::positionNewFloats(LineWidth* width)
|
| setLogicalTopForChild(*childBox, floatLogicalLocation.y() + marginBeforeForChild(*childBox));
|
|
|
| SubtreeLayoutScope layoutScope(*childBox);
|
| - LayoutState* layoutState = view()->layoutState();
|
| - bool isPaginated = layoutState->isPaginated();
|
| if (isPaginated && !childBox->needsLayout())
|
| childBox->markForPaginationRelayoutIfNeeded(layoutScope);
|
|
|
|
|