diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/WebCore/rendering/RenderBlockFlow.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/WebCore/rendering/RenderBlockFlow.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderBlockFlow.cpp | 1889 |
1 files changed, 620 insertions, 1269 deletions
diff --git a/Source/WebCore/rendering/RenderBlockFlow.cpp b/Source/WebCore/rendering/RenderBlockFlow.cpp index 33bce4025..cf439e6fa 100644 --- a/Source/WebCore/rendering/RenderBlockFlow.cpp +++ b/Source/WebCore/rendering/RenderBlockFlow.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2003-2015 Apple Inc. All rights reserved. + * Copyright (C) 2003-2013 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -27,30 +27,28 @@ #include "Editor.h" #include "FloatingObjects.h" #include "Frame.h" -#include "FrameSelection.h" -#include "HTMLElement.h" #include "HitTestLocation.h" #include "InlineTextBox.h" #include "LayoutRepainter.h" -#include "RenderCombineText.h" #include "RenderFlowThread.h" -#include "RenderInline.h" #include "RenderIterator.h" #include "RenderLayer.h" -#include "RenderLineBreak.h" -#include "RenderListItem.h" -#include "RenderMarquee.h" #include "RenderMultiColumnFlowThread.h" #include "RenderMultiColumnSet.h" #include "RenderNamedFlowFragment.h" -#include "RenderTableCell.h" #include "RenderText.h" #include "RenderView.h" -#include "Settings.h" #include "SimpleLineLayoutFunctions.h" #include "VerticalPositionCache.h" #include "VisiblePosition.h" -#include <wtf/NeverDestroyed.h> + +#if ENABLE(CSS_SHAPES) +#include "ShapeInsideInfo.h" +#endif + +#if ENABLE(IOS_TEXT_AUTOSIZING) +#include "HTMLElement.h" +#endif namespace WebCore { @@ -65,7 +63,7 @@ COMPILE_ASSERT(sizeof(RenderBlockFlow::MarginValues) == sizeof(LayoutUnit[4]), M COMPILE_ASSERT(sizeof(RenderBlockFlow::MarginInfo) == sizeof(SameSizeAsMarginInfo), MarginInfo_should_stay_small); // Our MarginInfo state used when laying out block children. -RenderBlockFlow::MarginInfo::MarginInfo(const RenderBlockFlow& block, LayoutUnit beforeBorderPadding, LayoutUnit afterBorderPadding) +RenderBlockFlow::MarginInfo::MarginInfo(RenderBlockFlow& block, LayoutUnit beforeBorderPadding, LayoutUnit afterBorderPadding) : m_atBeforeSideOfBlock(true) , m_atAfterSideOfBlock(false) , m_hasMarginBeforeQuirk(false) @@ -75,7 +73,10 @@ RenderBlockFlow::MarginInfo::MarginInfo(const RenderBlockFlow& block, LayoutUnit { const RenderStyle& blockStyle = block.style(); ASSERT(block.isRenderView() || block.parent()); - m_canCollapseWithChildren = !block.createsNewFormattingContext() && !block.isRenderView(); + m_canCollapseWithChildren = !block.isRenderView() && !block.isRoot() && !block.isOutOfFlowPositioned() + && !block.isFloating() && !block.isTableCell() && !block.hasOverflowClip() && !block.isInlineBlockOrInlineTable() + && !block.isRenderFlowThread() && !block.isWritingModeRoot() && !block.parent()->isFlexibleBox() + && blockStyle.hasAutoColumnCount() && blockStyle.hasAutoColumnWidth() && !blockStyle.columnSpan(); m_canCollapseMarginBeforeWithChildren = m_canCollapseWithChildren && !beforeBorderPadding && blockStyle.marginBeforeCollapse() != MSEPARATE; @@ -94,8 +95,8 @@ RenderBlockFlow::MarginInfo::MarginInfo(const RenderBlockFlow& block, LayoutUnit m_negativeMargin = (m_canCollapseMarginBeforeWithChildren && !block.mustDiscardMarginBefore()) ? block.maxNegativeMarginBefore() : LayoutUnit(); } -RenderBlockFlow::RenderBlockFlow(Element& element, Ref<RenderStyle>&& style) - : RenderBlock(element, WTFMove(style), RenderBlockFlowFlag) +RenderBlockFlow::RenderBlockFlow(Element& element, PassRef<RenderStyle> style) + : RenderBlock(element, std::move(style), RenderBlockFlowFlag) #if ENABLE(IOS_TEXT_AUTOSIZING) , m_widthForTextAutosizing(-1) , m_lineCountForTextAutosizing(NOT_SET) @@ -104,8 +105,8 @@ RenderBlockFlow::RenderBlockFlow(Element& element, Ref<RenderStyle>&& style) setChildrenInline(true); } -RenderBlockFlow::RenderBlockFlow(Document& document, Ref<RenderStyle>&& style) - : RenderBlock(document, WTFMove(style), RenderBlockFlowFlag) +RenderBlockFlow::RenderBlockFlow(Document& document, PassRef<RenderStyle> style) + : RenderBlock(document, std::move(style), RenderBlockFlowFlag) #if ENABLE(IOS_TEXT_AUTOSIZING) , m_widthForTextAutosizing(-1) , m_lineCountForTextAutosizing(NOT_SET) @@ -122,17 +123,27 @@ void RenderBlockFlow::createMultiColumnFlowThread() { RenderMultiColumnFlowThread* flowThread = new RenderMultiColumnFlowThread(document(), RenderStyle::createAnonymousStyleWithDisplay(&style(), BLOCK)); flowThread->initializeStyle(); - setChildrenInline(false); // Do this to avoid wrapping inline children that are just going to move into the flow thread. - deleteLines(); + moveAllChildrenTo(flowThread, true); RenderBlock::addChild(flowThread); - flowThread->populate(); // Called after the flow thread is inserted so that we are reachable by the flow thread. setMultiColumnFlowThread(flowThread); } void RenderBlockFlow::destroyMultiColumnFlowThread() { - multiColumnFlowThread()->evacuateAndDestroy(); - ASSERT(!multiColumnFlowThread()); + // Get the flow thread out of our list. + multiColumnFlowThread()->removeFromParent(); + + // Destroy all the multicolumn sets. + destroyLeftoverChildren(); + + // Move all the children of the flow thread into our block. + multiColumnFlowThread()->moveAllChildrenTo(this, true); + + // Now destroy the flow thread. + multiColumnFlowThread()->destroy(); + + // Clear the multi-column flow thread pointer. + setMultiColumnFlowThread(nullptr); } void RenderBlockFlow::insertedIntoTree() @@ -143,60 +154,61 @@ void RenderBlockFlow::insertedIntoTree() void RenderBlockFlow::willBeDestroyed() { + // Mark as being destroyed to avoid trouble with merges in removeChild(). + m_beingDestroyed = true; + if (renderNamedFlowFragment()) - setRenderNamedFlowFragment(nullptr); + setRenderNamedFlowFragment(0); + + if (!documentBeingDestroyed()) { + if (firstChild() && firstChild()->isRunIn()) + moveRunInToOriginalPosition(*firstChild()); + } // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. destroyLeftoverChildren(); + // Destroy our continuation before anything other than anonymous children. + // The reason we don't destroy it before anonymous children is that they may + // have continuations of their own that are anonymous children of our continuation. + RenderBoxModelObject* continuation = this->continuation(); + if (continuation) { + continuation->destroy(); + setContinuation(0); + } + if (!documentBeingDestroyed()) { if (firstRootBox()) { // We can't wait for RenderBox::destroy to clear the selection, // because by then we will have nuked the line boxes. + // FIXME: The FrameSelection should be responsible for this when it + // is notified of DOM mutations. if (isSelectionBorder()) - frame().selection().setNeedsSelectionUpdate(); + view().clearSelection(); // If we are an anonymous block, then our line boxes might have children // that will outlast this block. In the non-anonymous block case those // children will be destroyed by the time we return from this function. if (isAnonymousBlock()) { - for (auto* box = firstRootBox(); box; box = box->nextRootBox()) { + for (auto box = firstRootBox(); box; box = box->nextRootBox()) { while (auto childBox = box->firstChild()) childBox->removeFromParent(); } } } else if (parent()) - parent()->dirtyLinesFromChangedChild(*this); + parent()->dirtyLinesFromChangedChild(this); } m_lineBoxes.deleteLineBoxes(); - removeFromUpdateScrollInfoAfterLayoutTransaction(); + removeFromDelayedUpdateScrollInfoSet(); // NOTE: This jumps down to RenderBox, bypassing RenderBlock since it would do duplicate work. RenderBox::willBeDestroyed(); } -RenderBlockFlow* RenderBlockFlow::previousSiblingWithOverhangingFloats(bool& parentHasFloats) const -{ - // Attempt to locate a previous sibling with overhanging floats. We skip any elements that are - // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted - // to avoid floats. - parentHasFloats = false; - for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) { - if (is<RenderBlockFlow>(*sibling)) { - auto& siblingBlock = downcast<RenderBlockFlow>(*sibling); - if (!siblingBlock.avoidsFloats()) - return &siblingBlock; - } - if (sibling->isFloating()) - parentHasFloats = true; - } - return nullptr; -} - -void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats() +void RenderBlockFlow::clearFloats() { if (m_floatingObjects) m_floatingObjects->setHorizontalWritingMode(isHorizontalWritingMode()); @@ -213,7 +225,7 @@ void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats() } // Inline blocks are covered by the isReplaced() check in the avoidFloats method. - if (avoidsFloats() || isDocumentElementRenderer() || isRenderView() || isFloatingOrOutOfFlowPositioned() || isTableCell()) { + if (avoidsFloats() || isRoot() || isRenderView() || isFloatingOrOutOfFlowPositioned() || isTableCell()) { if (m_floatingObjects) m_floatingObjects->clear(); if (!oldIntrudingFloatSet.isEmpty()) @@ -233,32 +245,39 @@ void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats() // We should not process floats if the parent node is not a RenderBlock. Otherwise, we will add // floats in an invalid context. This will cause a crash arising from a bad cast on the parent. // See <rdar://problem/8049753>, where float property is applied on a text node in a SVG. - bool isBlockInsideInline = isAnonymousInlineBlock(); - if (!is<RenderBlockFlow>(parent()) && !isBlockInsideInline) + if (!parent() || !parent()->isRenderBlockFlow()) return; + // Attempt to locate a previous sibling with overhanging floats. We skip any elements that are + // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted + // to avoid floats. + RenderBlockFlow* parentBlock = toRenderBlockFlow(parent()); + bool parentHasFloats = false; + RenderObject* prev = previousSibling(); + while (prev && (prev->isFloatingOrOutOfFlowPositioned() || !prev->isBox() || !prev->isRenderBlockFlow() || toRenderBlockFlow(prev)->avoidsFloats())) { + if (prev->isFloating()) + parentHasFloats = true; + prev = prev->previousSibling(); + } + // First add in floats from the parent. Self-collapsing blocks let their parent track any floats that intrude into // them (as opposed to floats they contain themselves) so check for those here too. - RenderBlockFlow& parentBlock = downcast<RenderBlockFlow>(isBlockInsideInline ? *containingBlock() : *parent()); - bool parentHasFloats = isBlockInsideInline ? parentBlock.containsFloats() : false; - RenderBlockFlow* previousBlock = nullptr; - if (!isBlockInsideInline) - previousBlock = previousSiblingWithOverhangingFloats(parentHasFloats); LayoutUnit logicalTopOffset = logicalTop(); - if (parentHasFloats || (parentBlock.lowestFloatLogicalBottom() > logicalTopOffset && previousBlock && previousBlock->isSelfCollapsingBlock())) - addIntrudingFloats(&parentBlock, &parentBlock, parentBlock.logicalLeftOffsetForContent(), logicalTopOffset); + if (parentHasFloats || (parentBlock->lowestFloatLogicalBottom() > logicalTopOffset && prev && toRenderBlockFlow(prev)->isSelfCollapsingBlock())) + addIntrudingFloats(parentBlock, parentBlock->logicalLeftOffsetForContent(), logicalTopOffset); LayoutUnit logicalLeftOffset = 0; - if (previousBlock) - logicalTopOffset -= previousBlock->logicalTop(); + if (prev) + logicalTopOffset -= toRenderBox(prev)->logicalTop(); else { - previousBlock = &parentBlock; - logicalLeftOffset += parentBlock.logicalLeftOffsetForContent(); + prev = parentBlock; + logicalLeftOffset += parentBlock->logicalLeftOffsetForContent(); } // Add overhanging floats from the previous RenderBlock, but only if it has a float that intrudes into our space. - if (previousBlock->m_floatingObjects && previousBlock->lowestFloatLogicalBottom() > logicalTopOffset) - addIntrudingFloats(previousBlock, &parentBlock, logicalLeftOffset, logicalTopOffset); + RenderBlockFlow* block = toRenderBlockFlow(prev); + if (block->m_floatingObjects && block->lowestFloatLogicalBottom() > logicalTopOffset) + addIntrudingFloats(block, logicalLeftOffset, logicalTopOffset); if (childrenInline()) { LayoutUnit changeLogicalTop = LayoutUnit::max(); @@ -267,12 +286,12 @@ void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats() const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); - std::unique_ptr<FloatingObject> oldFloatingObject = floatMap.take(&floatingObject.renderer()); + FloatingObject* floatingObject = it->get(); + std::unique_ptr<FloatingObject> oldFloatingObject = floatMap.take(&floatingObject->renderer()); LayoutUnit logicalBottom = logicalBottomForFloat(floatingObject); if (oldFloatingObject) { - LayoutUnit oldLogicalBottom = logicalBottomForFloat(*oldFloatingObject); - if (logicalWidthForFloat(floatingObject) != logicalWidthForFloat(*oldFloatingObject) || logicalLeftForFloat(floatingObject) != logicalLeftForFloat(*oldFloatingObject)) { + LayoutUnit oldLogicalBottom = logicalBottomForFloat(oldFloatingObject.get()); + if (logicalWidthForFloat(floatingObject) != logicalWidthForFloat(oldFloatingObject.get()) || logicalLeftForFloat(floatingObject) != logicalLeftForFloat(oldFloatingObject.get())) { changeLogicalTop = 0; changeLogicalBottom = std::max(changeLogicalBottom, std::max(logicalBottom, oldLogicalBottom)); } else { @@ -281,7 +300,7 @@ void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats() changeLogicalBottom = std::max(changeLogicalBottom, std::max(logicalBottom, oldLogicalBottom)); } LayoutUnit logicalTop = logicalTopForFloat(floatingObject); - LayoutUnit oldLogicalTop = logicalTopForFloat(*oldFloatingObject); + LayoutUnit oldLogicalTop = logicalTopForFloat(oldFloatingObject.get()); if (logicalTop != oldLogicalTop) { changeLogicalTop = std::min(changeLogicalTop, std::min(logicalTop, oldLogicalTop)); changeLogicalBottom = std::max(changeLogicalBottom, std::max(logicalTop, oldLogicalTop)); @@ -301,8 +320,8 @@ void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats() auto end = floatMap.end(); for (auto it = floatMap.begin(); it != end; ++it) { - const auto& floatingObject = *it->value.get(); - if (!floatingObject.isDescendant()) { + FloatingObject* floatingObject = it->value.get(); + if (!floatingObject->isDescendant()) { changeLogicalTop = 0; changeLogicalBottom = std::max(changeLogicalBottom, logicalBottomForFloat(floatingObject)); } @@ -325,107 +344,6 @@ void RenderBlockFlow::rebuildFloatingObjectSetFromIntrudingFloats() } } -void RenderBlockFlow::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - if (!style().hasAutoColumnCount() || !style().hasAutoColumnWidth()) { - // The min/max intrinsic widths calculated really tell how much space elements need when - // laid out inside the columns. In order to eventually end up with the desired column width, - // we need to convert them to values pertaining to the multicol container. - int columnCount = style().hasAutoColumnCount() ? 1 : style().columnCount(); - LayoutUnit columnWidth; - LayoutUnit colGap = columnGap(); - LayoutUnit gapExtra = (columnCount - 1) * colGap; - if (style().hasAutoColumnWidth()) - minLogicalWidth = minLogicalWidth * columnCount + gapExtra; - else { - columnWidth = style().columnWidth(); - minLogicalWidth = std::min(minLogicalWidth, columnWidth); - } - // FIXME: If column-count is auto here, we should resolve it to calculate the maximum - // intrinsic width, instead of pretending that it's 1. The only way to do that is by - // performing a layout pass, but this is not an appropriate time or place for layout. The - // good news is that if height is unconstrained and there are no explicit breaks, the - // resolved column-count really should be 1. - maxLogicalWidth = std::max(maxLogicalWidth, columnWidth) * columnCount + gapExtra; - } -} - -void RenderBlockFlow::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - if (childrenInline()) - computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); - else - computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); - - maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); - - adjustIntrinsicLogicalWidthsForColumns(minLogicalWidth, maxLogicalWidth); - - if (!style().autoWrap() && childrenInline()) { - // A horizontal marquee with inline children has no minimum width. - if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) - minLogicalWidth = 0; - } - - if (is<RenderTableCell>(*this)) { - Length tableCellWidth = downcast<RenderTableCell>(*this).styleOrColLogicalWidth(); - if (tableCellWidth.isFixed() && tableCellWidth.value() > 0) - maxLogicalWidth = std::max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth.value())); - } - - int scrollbarWidth = intrinsicScrollbarLogicalWidth(); - maxLogicalWidth += scrollbarWidth; - minLogicalWidth += scrollbarWidth; -} - -bool RenderBlockFlow::recomputeLogicalWidthAndColumnWidth() -{ - bool changed = recomputeLogicalWidth(); - - LayoutUnit oldColumnWidth = computedColumnWidth(); - computeColumnCountAndWidth(); - - return changed || oldColumnWidth != computedColumnWidth(); -} - -LayoutUnit RenderBlockFlow::columnGap() const -{ - if (style().hasNormalColumnGap()) - return style().fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. - return style().columnGap(); -} - -void RenderBlockFlow::computeColumnCountAndWidth() -{ - // Calculate our column width and column count. - // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744 - unsigned desiredColumnCount = 1; - LayoutUnit desiredColumnWidth = contentLogicalWidth(); - - // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination. - if (document().paginated() || (style().hasAutoColumnCount() && style().hasAutoColumnWidth()) || !style().hasInlineColumnAxis()) { - setComputedColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); - return; - } - - LayoutUnit availWidth = desiredColumnWidth; - LayoutUnit colGap = columnGap(); - LayoutUnit colWidth = std::max<LayoutUnit>(LayoutUnit::fromPixel(1), LayoutUnit(style().columnWidth())); - int colCount = std::max<int>(1, style().columnCount()); - - if (style().hasAutoColumnWidth() && !style().hasAutoColumnCount()) { - desiredColumnCount = colCount; - desiredColumnWidth = std::max<LayoutUnit>(0, (availWidth - ((desiredColumnCount - 1) * colGap)) / desiredColumnCount); - } else if (!style().hasAutoColumnWidth() && style().hasAutoColumnCount()) { - desiredColumnCount = std::max<LayoutUnit>(1, (availWidth + colGap) / (colWidth + colGap)); - desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap; - } else { - desiredColumnCount = std::max<LayoutUnit>(std::min<LayoutUnit>(colCount, (availWidth + colGap) / (colWidth + colGap)), 1); - desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap; - } - setComputedColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); -} - void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight) { ASSERT(needsLayout()); @@ -435,10 +353,10 @@ void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalH LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - if (recomputeLogicalWidthAndColumnWidth()) + if (updateLogicalWidthAndColumnWidth()) relayoutChildren = true; - rebuildFloatingObjectSetFromIntrudingFloats(); + clearFloats(); LayoutUnit previousHeight = logicalHeight(); // FIXME: should this start out as borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(), @@ -446,12 +364,13 @@ void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalH setLogicalHeight(0); bool pageLogicalHeightChanged = false; - checkForPaginationLogicalHeightChange(relayoutChildren, pageLogicalHeight, pageLogicalHeightChanged); + bool hasSpecifiedPageLogicalHeight = false; + checkForPaginationLogicalHeightChange(pageLogicalHeight, pageLogicalHeightChanged, hasSpecifiedPageLogicalHeight); const RenderStyle& styleToUse = style(); - LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || styleToUse.isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged); + LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || styleToUse.isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged, columnInfo()); - preparePaginationBeforeBlockLayout(relayoutChildren); + prepareShapesAndPaginationBeforeBlockLayout(relayoutChildren); if (!relayoutChildren) relayoutChildren = namedFlowFragmentNeedsUpdate(); @@ -486,10 +405,10 @@ void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalH // Expand our intrinsic height to encompass floats. LayoutUnit toAdd = borderAndPaddingAfter() + scrollbarLogicalHeight(); - if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && createsNewFormattingContext()) + if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats()) setLogicalHeight(lowestFloatLogicalBottom() + toAdd); - if (relayoutForPagination(statePusher) || relayoutToAvoidWidows(statePusher)) { + if (relayoutForPagination(hasSpecifiedPageLogicalHeight, pageLogicalHeight, statePusher) || relayoutToAvoidWidows(statePusher)) { ASSERT(!shouldBreakAtLineToAvoidWidow()); return; } @@ -500,8 +419,8 @@ void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalH // Before updating the final size of the flow thread make sure a forced break is applied after the content. // This ensures the size information is correctly computed for the last auto-height region receiving content. - if (is<RenderFlowThread>(*this)) - downcast<RenderFlowThread>(*this).applyBreakAfterContent(oldClientAfterEdge); + if (isRenderFlowThread()) + toRenderFlowThread(this)->applyBreakAfterContent(oldClientAfterEdge); updateLogicalHeight(); LayoutUnit newHeight = logicalHeight(); @@ -521,7 +440,9 @@ void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalH if (heightChanged) relayoutChildren = true; - layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer()); + layoutPositionedObjects(relayoutChildren || isRoot()); + + updateShapesAfterBlockLayout(heightChanged); // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway). computeOverflow(oldClientAfterEdge); @@ -561,6 +482,11 @@ void RenderBlockFlow::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalH else repaintRect = LayoutRect(repaintLogicalTop, repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft); + // The repaint rect may be split across columns, in which case adjustRectForColumns() will return the union. + adjustRectForColumns(repaintRect); + + repaintRect.inflate(maximalOutlineSize(PaintPhaseOutline)); + if (hasOverflowClip()) { // Adjust repaint rect for scroll offset repaintRect.move(-scrolledContentOffset()); @@ -637,11 +563,12 @@ void RenderBlockFlow::layoutBlockChildren(bool relayoutChildren, LayoutUnit& max void RenderBlockFlow::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom) { - if (lineLayoutPath() == UndeterminedPath) - setLineLayoutPath(SimpleLineLayout::canUseFor(*this) ? SimpleLinesPath : LineBoxesPath); + if (m_lineLayoutPath == UndeterminedPath) + m_lineLayoutPath = SimpleLineLayout::canUseFor(*this) ? SimpleLinesPath : LineBoxesPath; - if (lineLayoutPath() == SimpleLinesPath) { - layoutSimpleLines(relayoutChildren, repaintLogicalTop, repaintLogicalBottom); + if (m_lineLayoutPath == SimpleLinesPath) { + deleteLineBoxesBeforeSimpleLineLayout(); + layoutSimpleLines(repaintLogicalTop, repaintLogicalBottom); return; } @@ -670,20 +597,22 @@ void RenderBlockFlow::layoutBlockChild(RenderBox& child, MarginInfo& marginInfo, #if !ASSERT_DISABLED LayoutSize oldLayoutDelta = view().layoutDelta(); #endif - // Position the child as though it didn't collapse with the top. + // Go ahead and position the child as though it didn't collapse with the top. setLogicalTopForChild(child, logicalTopEstimate, ApplyLayoutDelta); estimateRegionRangeForBoxChild(child); - RenderBlockFlow* childBlockFlow = is<RenderBlockFlow>(child) ? &downcast<RenderBlockFlow>(child) : nullptr; + RenderBlockFlow* childBlockFlow = child.isRenderBlockFlow() ? toRenderBlockFlow(&child) : nullptr; bool markDescendantsWithFloats = false; if (logicalTopEstimate != oldLogicalTop && !child.avoidsFloats() && childBlockFlow && childBlockFlow->containsFloats()) markDescendantsWithFloats = true; +#if ENABLE(SUBPIXEL_LAYOUT) else if (UNLIKELY(logicalTopEstimate.mightBeSaturated())) // logicalTopEstimate, returned by estimateLogicalTopPosition, might be saturated for // very large elements. If it does the comparison with oldLogicalTop might yield a // false negative as adding and removing margins, borders etc from a saturated number // might yield incorrect results. If this is the case always mark for layout. markDescendantsWithFloats = true; +#endif else if (!child.avoidsFloats() || child.shrinkToAvoidFloats()) { // If an element might be affected by the presence of floats, then always mark it for // layout. @@ -699,7 +628,8 @@ void RenderBlockFlow::layoutBlockChild(RenderBox& child, MarginInfo& marginInfo, previousFloatLogicalBottom = std::max(previousFloatLogicalBottom, oldLogicalTop + childBlockFlow->lowestFloatLogicalBottom()); } - child.markForPaginationRelayoutIfNeeded(); + if (!child.needsLayout()) + child.markForPaginationRelayoutIfNeeded(); bool childHadLayout = child.everHadLayout(); bool childNeededLayout = child.needsLayout(); @@ -727,23 +657,28 @@ void RenderBlockFlow::layoutBlockChild(RenderBox& child, MarginInfo& marginInfo, // when collapseMargins dynamically adds overhanging floats because of a child with negative margins. if (logicalTopAfterClear != logicalTopEstimate || child.needsLayout() || (paginated && childBlockFlow && childBlockFlow->shouldBreakAtLineToAvoidWidow())) { if (child.shrinkToAvoidFloats()) { - // The child's width depends on the line width. When the child shifts to clear an item, its width can - // change (because it has more available line width). So mark the item as dirty. + // The child's width depends on the line width. + // When the child shifts to clear an item, its width can + // change (because it has more available line width). + // So go ahead and mark the item as dirty. child.setChildNeedsLayout(MarkOnlyThis); } if (childBlockFlow) { if (!child.avoidsFloats() && childBlockFlow->containsFloats()) childBlockFlow->markAllDescendantsWithFloatsForLayout(); - child.markForPaginationRelayoutIfNeeded(); + if (!child.needsLayout()) + child.markForPaginationRelayoutIfNeeded(); } + + // Our guess was wrong. Make the child lay itself out again. + child.layoutIfNeeded(); } - if (updateRegionRangeForBoxChild(child)) + if (updateRegionRangeForBoxChild(child)) { child.setNeedsLayout(MarkOnlyThis); - - // In case our guess was wrong, relayout the child. - child.layoutIfNeeded(); + child.layoutIfNeeded(); + } // We are no longer at the top of the block if we encounter a non-empty child. // This has to be done after checking for clear, so that margins can be reset if a clear occurred. @@ -753,8 +688,13 @@ void RenderBlockFlow::layoutBlockChild(RenderBox& child, MarginInfo& marginInfo, // Now place the child in the correct left position determineLogicalLeftPositionForChild(child, ApplyLayoutDelta); + LayoutSize childOffset = child.location() - oldRect.location(); +#if ENABLE(CSS_SHAPES) + relayoutShapeDescendantIfMoved(child.isRenderBlock() ? toRenderBlock(&child) : nullptr, childOffset); +#endif + // Update our height now that the child has been placed in the correct position. - setLogicalHeight(logicalHeight() + logicalHeightForChildForFragmentation(child)); + setLogicalHeight(logicalHeight() + logicalHeightForChild(child)); if (mustSeparateMarginAfterForChild(child)) { setLogicalHeight(logicalHeight() + marginAfterForChild(child)); marginInfo.clearMargin(); @@ -764,7 +704,6 @@ void RenderBlockFlow::layoutBlockChild(RenderBox& child, MarginInfo& marginInfo, if (childBlockFlow && childBlockFlow->containsFloats()) maxFloatLogicalBottom = std::max(maxFloatLogicalBottom, addOverhangingFloats(*childBlockFlow, !childNeededLayout)); - LayoutSize childOffset = child.location() - oldRect.location(); if (childOffset.width() || childOffset.height()) { view().addLayoutDelta(childOffset); @@ -781,8 +720,6 @@ void RenderBlockFlow::layoutBlockChild(RenderBox& child, MarginInfo& marginInfo, } if (paginated) { - if (RenderFlowThread* flowThread = flowThreadContainingBlock()) - flowThread->flowThreadDescendantBoxLaidOut(&child); // Check for an after page/column break. LayoutUnit newHeight = applyAfterBreak(child, logicalHeight(), marginInfo); if (newHeight != height()) @@ -798,7 +735,7 @@ void RenderBlockFlow::adjustPositionedBlock(RenderBox& child, const MarginInfo& bool hasStaticBlockPosition = child.style().hasStaticBlockPosition(isHorizontal); LayoutUnit logicalTop = logicalHeight(); - updateStaticInlinePositionForChild(child, logicalTop, DoNotIndentText); + updateStaticInlinePositionForChild(child, logicalTop); if (!marginInfo.canCollapseWithMarginBefore()) { // Positioned blocks don't collapse margins, so add the margin provided by @@ -819,7 +756,7 @@ void RenderBlockFlow::adjustPositionedBlock(RenderBox& child, const MarginInfo& LayoutUnit RenderBlockFlow::marginOffsetForSelfCollapsingBlock() { ASSERT(isSelfCollapsingBlock()); - RenderBlockFlow* parentBlock = downcast<RenderBlockFlow>(parent()); + RenderBlockFlow* parentBlock = toRenderBlockFlow(parent()); if (parentBlock && style().clear() && parentBlock->getClearDelta(*this, logicalHeight())) return marginValuesForChild(*this).positiveMarginBefore(); return LayoutUnit(); @@ -865,10 +802,10 @@ void RenderBlockFlow::adjustFloatingBlock(const MarginInfo& marginInfo) setLogicalHeight(logicalHeight() - marginOffset); } -void RenderBlockFlow::updateStaticInlinePositionForChild(RenderBox& child, LayoutUnit logicalTop, IndentTextOrNot shouldIndentText) +void RenderBlockFlow::updateStaticInlinePositionForChild(RenderBox& child, LayoutUnit logicalTop) { if (child.style().isOriginalDisplayInlineType()) - setStaticInlinePositionForChild(child, logicalTop, startAlignedOffsetForLine(logicalTop, shouldIndentText)); + setStaticInlinePositionForChild(child, logicalTop, startAlignedOffsetForLine(logicalTop, false)); else setStaticInlinePositionForChild(child, logicalTop, startOffsetForContent(logicalTop)); } @@ -892,7 +829,7 @@ RenderBlockFlow::MarginValues RenderBlockFlow::marginValuesForChild(RenderBox& c LayoutUnit beforeMargin = 0; LayoutUnit afterMargin = 0; - RenderBlockFlow* childRenderBlock = is<RenderBlockFlow>(child) ? &downcast<RenderBlockFlow>(child) : nullptr; + RenderBlockFlow* childRenderBlock = child.isRenderBlockFlow() ? toRenderBlockFlow(&child) : nullptr; // If the child has the same directionality as we do, then we can just return its // margins in the same direction. @@ -942,45 +879,17 @@ RenderBlockFlow::MarginValues RenderBlockFlow::marginValuesForChild(RenderBox& c return MarginValues(childBeforePositive, childBeforeNegative, childAfterPositive, childAfterNegative); } -bool RenderBlockFlow::childrenPreventSelfCollapsing() const -{ - if (!childrenInline()) - return RenderBlock::childrenPreventSelfCollapsing(); - - // If the block has inline children, see if we generated any line boxes. If we have any - // line boxes, then we can only be self-collapsing if we have nothing but anonymous inline blocks - // that are also self-collapsing inside us. - if (!hasLines()) - return false; - - if (simpleLineLayout()) - return true; // We have simple line layout lines, so we can't be self-collapsing. - - for (auto* child = firstRootBox(); child; child = child->nextRootBox()) { - if (!child->hasAnonymousInlineBlock() || !child->anonymousInlineBlock()->isSelfCollapsingBlock()) - return true; - } - return false; // We have no line boxes, so we must be self-collapsing. -} - LayoutUnit RenderBlockFlow::collapseMargins(RenderBox& child, MarginInfo& marginInfo) { - return collapseMarginsWithChildInfo(&child, child.previousSibling(), marginInfo); -} + bool childDiscardMarginBefore = mustDiscardMarginBeforeForChild(child); + bool childDiscardMarginAfter = mustDiscardMarginAfterForChild(child); + bool childIsSelfCollapsing = child.isSelfCollapsingBlock(); -LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, RenderObject* prevSibling, MarginInfo& marginInfo) -{ - bool childDiscardMarginBefore = child ? mustDiscardMarginBeforeForChild(*child) : false; - bool childDiscardMarginAfter = child ? mustDiscardMarginAfterForChild(*child) : false; - bool childIsSelfCollapsing = child ? child->isSelfCollapsingBlock() : false; - bool beforeQuirk = child ? hasMarginBeforeQuirk(*child) : false; - bool afterQuirk = child ? hasMarginAfterQuirk(*child) : false; - // The child discards the before margin when the the after margin has discard in the case of a self collapsing block. childDiscardMarginBefore = childDiscardMarginBefore || (childDiscardMarginAfter && childIsSelfCollapsing); - + // Get the four margin values for the child and cache them. - const MarginValues childMargins = child ? marginValuesForChild(*child) : MarginValues(0, 0, 0, 0); + const MarginValues childMargins = marginValuesForChild(child); // Get our max pos and neg top margins. LayoutUnit posTop = childMargins.positiveMarginBefore(); @@ -993,31 +902,34 @@ LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, Rende negTop = std::max(negTop, childMargins.negativeMarginAfter()); } + // See if the top margin is quirky. We only care if this child has + // margins that will collapse with us. + bool topQuirk = hasMarginBeforeQuirk(child); + if (marginInfo.canCollapseWithMarginBefore()) { if (!childDiscardMarginBefore && !marginInfo.discardMargin()) { // This child is collapsing with the top of the // block. If it has larger margin values, then we need to update // our own maximal values. - if (!document().inQuirksMode() || !marginInfo.quirkContainer() || !beforeQuirk) + if (!document().inQuirksMode() || !marginInfo.quirkContainer() || !topQuirk) setMaxMarginBeforeValues(std::max(posTop, maxPositiveMarginBefore()), std::max(negTop, maxNegativeMarginBefore())); // The minute any of the margins involved isn't a quirk, don't // collapse it away, even if the margin is smaller (www.webreference.com // has an example of this, a <dt> with 0.8em author-specified inside // a <dl> inside a <td>. - if (!marginInfo.determinedMarginBeforeQuirk() && !beforeQuirk && (posTop - negTop)) { + if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) { setHasMarginBeforeQuirk(false); marginInfo.setDeterminedMarginBeforeQuirk(true); } - if (!marginInfo.determinedMarginBeforeQuirk() && beforeQuirk && !marginBefore()) { + if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && !marginBefore()) // We have no top margin and our top child has a quirky margin. // We will pick up this quirky margin and pass it through. // This deals with the <td><div><p> case. // Don't do this for a block that split two inlines though. You do // still apply margins in this case. setHasMarginBeforeQuirk(true); - } } else // The before margin of the container will also discard all the margins it is collapsing with. setMustDiscardMarginBefore(); @@ -1030,18 +942,18 @@ LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, Rende } if (marginInfo.quirkContainer() && marginInfo.atBeforeSideOfBlock() && (posTop - negTop)) - marginInfo.setHasMarginBeforeQuirk(beforeQuirk); + marginInfo.setHasMarginBeforeQuirk(topQuirk); LayoutUnit beforeCollapseLogicalTop = logicalHeight(); LayoutUnit logicalTop = beforeCollapseLogicalTop; LayoutUnit clearanceForSelfCollapsingBlock; - + RenderObject* prev = child.previousSibling(); // If the child's previous sibling is a self-collapsing block that cleared a float then its top border edge has been set at the bottom border edge // of the float. Since we want to collapse the child's top margin with the self-collapsing block's top and bottom margins we need to adjust our parent's height to match the // margin top of the self-collapsing block. If the resulting collapsed margin leaves the child still intruding into the float then we will want to clear it. - if (!marginInfo.canCollapseWithMarginBefore() && is<RenderBlockFlow>(prevSibling) && downcast<RenderBlockFlow>(*prevSibling).isSelfCollapsingBlock()) { - clearanceForSelfCollapsingBlock = downcast<RenderBlockFlow>(*prevSibling).marginOffsetForSelfCollapsingBlock(); + if (!marginInfo.canCollapseWithMarginBefore() && prev && prev->isRenderBlockFlow() && toRenderBlockFlow(prev)->isSelfCollapsingBlock()) { + clearanceForSelfCollapsingBlock = toRenderBlockFlow(prev)->marginOffsetForSelfCollapsingBlock(); setLogicalHeight(logicalHeight() - clearanceForSelfCollapsingBlock); } @@ -1069,13 +981,13 @@ LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, Rende logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg; } } else { - if (child && mustSeparateMarginBeforeForChild(*child)) { + if (mustSeparateMarginBeforeForChild(child)) { ASSERT(!marginInfo.discardMargin() || (marginInfo.discardMargin() && !marginInfo.margin())); // If we are at the before side of the block and we collapse, ignore the computed margin // and just add the child margin to the container height. This will correctly position // the child inside the container. LayoutUnit separateMargin = !marginInfo.canCollapseWithMarginBefore() ? marginInfo.margin() : LayoutUnit::fromPixel(0); - setLogicalHeight(logicalHeight() + separateMargin + marginBeforeForChild(*child)); + setLogicalHeight(logicalHeight() + separateMargin + marginBeforeForChild(child)); logicalTop = logicalHeight(); } else if (!marginInfo.discardMargin() && (!marginInfo.atBeforeSideOfBlock() || (!marginInfo.canCollapseMarginBeforeWithChildren() @@ -1095,7 +1007,7 @@ LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, Rende marginInfo.clearMargin(); if (marginInfo.margin()) - marginInfo.setHasMarginAfterQuirk(afterQuirk); + marginInfo.setHasMarginAfterQuirk(hasMarginAfterQuirk(child)); } // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins @@ -1108,10 +1020,10 @@ LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, Rende setLogicalHeight(logicalHeight() + (logicalTop - oldLogicalTop)); } - if (is<RenderBlockFlow>(prevSibling) && !prevSibling->isFloatingOrOutOfFlowPositioned()) { + if (prev && prev->isRenderBlockFlow() && !prev->isFloatingOrOutOfFlowPositioned()) { // If |child| is a self-collapsing block it may have collapsed into a previous sibling and although it hasn't reduced the height of the parent yet // any floats from the parent will now overhang. - RenderBlockFlow& block = downcast<RenderBlockFlow>(*prevSibling); + RenderBlockFlow& block = toRenderBlockFlow(*prev); LayoutUnit oldLogicalHeight = logicalHeight(); setLogicalHeight(logicalTop); if (block.containsFloats() && !block.avoidsFloats() && (block.logicalTop() + block.lowestFloatLogicalBottom()) > logicalTop) @@ -1122,8 +1034,8 @@ LayoutUnit RenderBlockFlow::collapseMarginsWithChildInfo(RenderBox* child, Rende // into the margin area of the self-collapsing block then the float it clears is now intruding into |child|. Layout again so that we can look for // floats in the parent that overhang |child|'s new logical top. bool logicalTopIntrudesIntoFloat = clearanceForSelfCollapsingBlock > 0 && logicalTop < beforeCollapseLogicalTop; - if (child && logicalTopIntrudesIntoFloat && containsFloats() && !child->avoidsFloats() && lowestFloatLogicalBottom() > logicalTop) - child->setNeedsLayout(); + if (logicalTopIntrudesIntoFloat && containsFloats() && !child.avoidsFloats() && lowestFloatLogicalBottom() > logicalTop) + child.setNeedsLayout(); } return logicalTop; @@ -1212,10 +1124,10 @@ void RenderBlockFlow::marginBeforeEstimateForChild(RenderBox& child, LayoutUnit& positiveMarginBefore = std::max(positiveMarginBefore, beforeChildMargin); negativeMarginBefore = std::max(negativeMarginBefore, -beforeChildMargin); - if (!is<RenderBlockFlow>(child)) + if (!child.isRenderBlockFlow()) return; - RenderBlockFlow& childBlock = downcast<RenderBlockFlow>(child); + RenderBlockFlow& childBlock = toRenderBlockFlow(child); if (childBlock.childrenInline() || childBlock.isWritingModeRoot()) return; @@ -1236,10 +1148,10 @@ void RenderBlockFlow::marginBeforeEstimateForChild(RenderBox& child, LayoutUnit& // Make sure to update the block margins now for the grandchild box so that we're looking at current values. if (grandchildBox->needsLayout()) { grandchildBox->computeAndSetBlockDirectionMargins(this); - if (is<RenderBlock>(*grandchildBox)) { - RenderBlock& grandchildBlock = downcast<RenderBlock>(*grandchildBox); - grandchildBlock.setHasMarginBeforeQuirk(grandchildBox->style().hasMarginBeforeQuirk()); - grandchildBlock.setHasMarginAfterQuirk(grandchildBox->style().hasMarginAfterQuirk()); + if (grandchildBox->isRenderBlock()) { + RenderBlock* grandchildBlock = toRenderBlock(grandchildBox); + grandchildBlock->setHasMarginBeforeQuirk(grandchildBox->style().hasMarginBeforeQuirk()); + grandchildBlock->setHasMarginAfterQuirk(grandchildBox->style().hasMarginAfterQuirk()); } } @@ -1291,8 +1203,8 @@ LayoutUnit RenderBlockFlow::estimateLogicalTopPosition(RenderBox& child, const M // 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); - if (!child.selfNeedsLayout() && is<RenderBlock>(child)) - logicalTopEstimate += downcast<RenderBlock>(child).paginationStrut(); + if (!child.selfNeedsLayout() && child.isRenderBlock()) + logicalTopEstimate += toRenderBlock(child).paginationStrut(); } return logicalTopEstimate; @@ -1331,10 +1243,10 @@ void RenderBlockFlow::handleAfterSideOfBlock(LayoutUnit beforeSide, LayoutUnit a // bottom edge of the float that the child clears. The correct vertical position for the margin-collapsing we want // to perform now is at the child's margin-top - so adjust our height to that position. RenderObject* lastBlock = lastChild(); - if (is<RenderBlockFlow>(lastBlock) && downcast<RenderBlockFlow>(*lastBlock).isSelfCollapsingBlock()) - setLogicalHeight(logicalHeight() - downcast<RenderBlockFlow>(*lastBlock).marginOffsetForSelfCollapsingBlock()); + if (lastBlock && lastBlock->isRenderBlockFlow() && toRenderBlockFlow(lastBlock)->isSelfCollapsingBlock()) + setLogicalHeight(logicalHeight() - toRenderBlockFlow(lastBlock)->marginOffsetForSelfCollapsingBlock()); - // If we can't collapse with children then add in the bottom margin. + // If we can't collapse with children then go ahead and add in the bottom margin. if (!marginInfo.discardMargin() && (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore() && (!document().inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.hasMarginAfterQuirk()))) setLogicalHeight(logicalHeight() + marginInfo.margin()); @@ -1420,9 +1332,9 @@ bool RenderBlockFlow::mustDiscardMarginBeforeForChild(const RenderBox& child) co { ASSERT(!child.selfNeedsLayout()); if (!child.isWritingModeRoot()) - return is<RenderBlockFlow>(child) ? downcast<RenderBlockFlow>(child).mustDiscardMarginBefore() : (child.style().marginBeforeCollapse() == MDISCARD); + return child.isRenderBlockFlow() ? toRenderBlockFlow(child).mustDiscardMarginBefore() : (child.style().marginBeforeCollapse() == MDISCARD); if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) - return is<RenderBlockFlow>(child) ? downcast<RenderBlockFlow>(child).mustDiscardMarginAfter() : (child.style().marginAfterCollapse() == MDISCARD); + return child.isRenderBlockFlow() ? toRenderBlockFlow(child).mustDiscardMarginAfter() : (child.style().marginAfterCollapse() == MDISCARD); // FIXME: We return false here because the implementation is not geometrically complete. We have values only for before/after, not start/end. // In case the boxes are perpendicular we assume the property is not specified. @@ -1433,9 +1345,9 @@ bool RenderBlockFlow::mustDiscardMarginAfterForChild(const RenderBox& child) con { ASSERT(!child.selfNeedsLayout()); if (!child.isWritingModeRoot()) - return is<RenderBlockFlow>(child) ? downcast<RenderBlockFlow>(child).mustDiscardMarginAfter() : (child.style().marginAfterCollapse() == MDISCARD); + return child.isRenderBlockFlow() ? toRenderBlockFlow(child).mustDiscardMarginAfter() : (child.style().marginAfterCollapse() == MDISCARD); if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) - return is<RenderBlockFlow>(child) ? downcast<RenderBlockFlow>(child).mustDiscardMarginBefore() : (child.style().marginBeforeCollapse() == MDISCARD); + return child.isRenderBlockFlow() ? toRenderBlockFlow(child).mustDiscardMarginBefore() : (child.style().marginBeforeCollapse() == MDISCARD); // FIXME: See |mustDiscardMarginBeforeForChild| above. return false; @@ -1471,7 +1383,7 @@ static bool inNormalFlow(RenderBox& child) { RenderBlock* curr = child.containingBlock(); while (curr && curr != &child.view()) { - if (curr->isRenderFlowThread()) + if (curr->hasColumns() || curr->isRenderFlowThread()) return true; if (curr->isFloatingOrOutOfFlowPositioned()) return false; @@ -1485,16 +1397,18 @@ LayoutUnit RenderBlockFlow::applyBeforeBreak(RenderBox& child, LayoutUnit logica // FIXME: Add page break checking here when we support printing. RenderFlowThread* flowThread = flowThreadContainingBlock(); bool isInsideMulticolFlowThread = flowThread && !flowThread->isRenderNamedFlowThread(); - bool checkColumnBreaks = flowThread && flowThread->shouldCheckColumnBreaks(); + bool checkColumnBreaks = isInsideMulticolFlowThread || view().layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view().layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); - bool checkBeforeAlways = (checkColumnBreaks && child.style().breakBefore() == ColumnBreakBetween) - || (checkPageBreaks && alwaysPageBreak(child.style().breakBefore())) - || (checkRegionBreaks && child.style().breakBefore() == RegionBreakBetween); + bool checkBeforeAlways = (checkColumnBreaks && child.style().columnBreakBefore() == PBALWAYS) + || (checkPageBreaks && child.style().pageBreakBefore() == PBALWAYS) + || (checkRegionBreaks && child.style().regionBreakBefore() == PBALWAYS); if (checkBeforeAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) { if (checkColumnBreaks) { if (isInsideMulticolFlowThread) checkRegionBreaks = true; + else + view().layoutState()->addForcedColumnBreak(&child, logicalOffset); } if (checkRegionBreaks) { LayoutUnit offsetBreakAdjustment = 0; @@ -1511,12 +1425,12 @@ LayoutUnit RenderBlockFlow::applyAfterBreak(RenderBox& child, LayoutUnit logical // FIXME: Add page break checking here when we support printing. RenderFlowThread* flowThread = flowThreadContainingBlock(); bool isInsideMulticolFlowThread = flowThread && !flowThread->isRenderNamedFlowThread(); - bool checkColumnBreaks = flowThread && flowThread->shouldCheckColumnBreaks(); + bool checkColumnBreaks = isInsideMulticolFlowThread || view().layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view().layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); - bool checkAfterAlways = (checkColumnBreaks && child.style().breakAfter() == ColumnBreakBetween) - || (checkPageBreaks && alwaysPageBreak(child.style().breakAfter())) - || (checkRegionBreaks && child.style().breakAfter() == RegionBreakBetween); + bool checkAfterAlways = (checkColumnBreaks && child.style().columnBreakAfter() == PBALWAYS) + || (checkPageBreaks && child.style().pageBreakAfter() == PBALWAYS) + || (checkRegionBreaks && child.style().regionBreakAfter() == PBALWAYS); if (checkAfterAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) { LayoutUnit marginOffset = marginInfo.canCollapseWithMarginBefore() ? LayoutUnit() : marginInfo.margin(); @@ -1526,6 +1440,8 @@ LayoutUnit RenderBlockFlow::applyAfterBreak(RenderBox& child, LayoutUnit logical if (checkColumnBreaks) { if (isInsideMulticolFlowThread) checkRegionBreaks = true; + else + view().layoutState()->addForcedColumnBreak(&child, logicalOffset); } if (checkRegionBreaks) { LayoutUnit offsetBreakAdjustment = 0; @@ -1539,7 +1455,7 @@ LayoutUnit RenderBlockFlow::applyAfterBreak(RenderBox& child, LayoutUnit logical LayoutUnit RenderBlockFlow::adjustBlockChildForPagination(LayoutUnit logicalTopAfterClear, LayoutUnit estimateWithoutPagination, RenderBox& child, bool atBeforeSideOfBlock) { - RenderBlock* childRenderBlock = is<RenderBlock>(child) ? &downcast<RenderBlock>(child) : nullptr; + RenderBlock* childRenderBlock = child.isRenderBlock() ? toRenderBlock(&child) : nullptr; if (estimateWithoutPagination != logicalTopAfterClear) { // Our guess prior to pagination movement was wrong. Before we attempt to paginate, let's try again at the new @@ -1548,15 +1464,18 @@ LayoutUnit RenderBlockFlow::adjustBlockChildForPagination(LayoutUnit logicalTopA setLogicalTopForChild(child, logicalTopAfterClear, ApplyLayoutDelta); if (child.shrinkToAvoidFloats()) { - // The child's width depends on the line width. When the child shifts to clear an item, its width can - // change (because it has more available line width). So mark the item as dirty. + // The child's width depends on the line width. + // When the child shifts to clear an item, its width can + // change (because it has more available line width). + // So go ahead and mark the item as dirty. child.setChildNeedsLayout(MarkOnlyThis); } if (childRenderBlock) { if (!child.avoidsFloats() && childRenderBlock->containsFloats()) - downcast<RenderBlockFlow>(*childRenderBlock).markAllDescendantsWithFloatsForLayout(); - child.markForPaginationRelayoutIfNeeded(); + toRenderBlockFlow(childRenderBlock)->markAllDescendantsWithFloatsForLayout(); + if (!child.needsLayout()) + child.markForPaginationRelayoutIfNeeded(); } // Our guess was wrong. Make the child lay itself out again. @@ -1608,20 +1527,20 @@ LayoutUnit RenderBlockFlow::adjustBlockChildForPagination(LayoutUnit logicalTopA result += paginationStrut; } - // Similar to how we apply clearance. Boost height() to be the place where we're going to position the child. + // Similar to how we apply clearance. Go ahead and boost height() to be the place where we're going to position the child. setLogicalHeight(logicalHeight() + (result - oldTop)); // Return the final adjusted logical top. return result; } -static inline LayoutUnit calculateMinimumPageHeight(RenderStyle& renderStyle, RootInlineBox& lastLine, LayoutUnit lineTop, LayoutUnit lineBottom) +static inline LayoutUnit calculateMinimumPageHeight(RenderStyle* renderStyle, RootInlineBox* lastLine, LayoutUnit lineTop, LayoutUnit lineBottom) { // We may require a certain minimum number of lines per page in order to satisfy // orphans and widows, and that may affect the minimum page height. - unsigned lineCount = std::max<unsigned>(renderStyle.hasAutoOrphans() ? 1 : renderStyle.orphans(), renderStyle.hasAutoWidows() ? 1 : renderStyle.widows()); + unsigned lineCount = std::max<unsigned>(renderStyle->hasAutoOrphans() ? 1 : renderStyle->orphans(), renderStyle->hasAutoWidows() ? 1 : renderStyle->widows()); if (lineCount > 1) { - RootInlineBox* line = &lastLine; + RootInlineBox* line = lastLine; for (unsigned i = 1; i < lineCount && line->prevRootBox(); i++) line = line->prevRootBox(); @@ -1633,26 +1552,8 @@ static inline LayoutUnit calculateMinimumPageHeight(RenderStyle& renderStyle, Ro return lineBottom - lineTop; } -static inline bool needsAppleMailPaginationQuirk(RootInlineBox& lineBox) +void RenderBlockFlow::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta, RenderFlowThread* flowThread) { - const auto& renderer = lineBox.renderer(); - - if (!renderer.document().settings()) - return false; - - if (!renderer.document().settings()->appleMailPaginationQuirkEnabled()) - return false; - - if (renderer.element() && renderer.element()->idForStyleResolution() == AtomicString("messageContentContainer", AtomicString::ConstructFromLiteral)) - return true; - - return false; -} - -void RenderBlockFlow::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta, bool& overflowsRegion, RenderFlowThread* flowThread) -{ - // FIXME: Ignore anonymous inline blocks. Handle the delta already having been set because of - // collapsing margins from a previous anonymous inline block. // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since // the line on the top of the next page will appear too far down relative to the same kind of line at the top @@ -1672,12 +1573,11 @@ void RenderBlockFlow::adjustLinePositionForPagination(RootInlineBox* lineBox, La // FIXME: Another problem with simply moving lines is that the available line width may change (because of floats). // Technically if the location we move the line to has a different line width than our old position, then we need to dirty the // line and all following lines. - overflowsRegion = false; LayoutRect logicalVisualOverflow = lineBox->logicalVisualOverflowRect(lineBox->lineTop(), lineBox->lineBottom()); LayoutUnit logicalOffset = std::min(lineBox->lineTopWithLeading(), logicalVisualOverflow.y()); LayoutUnit logicalBottom = std::max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY()); LayoutUnit lineHeight = logicalBottom - logicalOffset; - updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(style(), *lineBox, logicalOffset, logicalBottom)); + updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(&style(), lineBox, logicalOffset, logicalBottom)); logicalOffset += delta; lineBox->setPaginationStrut(0); lineBox->setIsFirstAfterPageBreak(false); @@ -1685,30 +1585,12 @@ void RenderBlockFlow::adjustLinePositionForPagination(RootInlineBox* lineBox, La bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); // If lineHeight is greater than pageLogicalHeight, but logicalVisualOverflow.height() still fits, we are // still going to add a strut, so that the visible overflow fits on a single page. - if (!pageLogicalHeight || !hasNextPage(logicalOffset)) { + if (!pageLogicalHeight || (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight) + || !hasNextPage(logicalOffset)) // FIXME: In case the line aligns with the top of the page (or it's slightly shifted downwards) it will not be marked as the first line in the page. // From here, the fix is not straightforward because it's not easy to always determine when the current line is the first in the page. return; - } - - if (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight) { - // We are so tall that we are bigger than a page. Before we give up and just leave the line where it is, try drilling into the - // line and computing a new height that excludes anything we consider "blank space". We will discard margins, descent, and even overflow. If we are - // able to fit with the blank space and overflow excluded, we will give the line its own page with the highest non-blank element being aligned with the - // top of the page. - // FIXME: We are still honoring gigantic margins, which does leave open the possibility of blank pages caused by this heuristic. It remains to be seen whether or not - // this will be a real-world issue. For now we don't try to deal with this problem. - logicalOffset = intMaxForLayoutUnit; - logicalBottom = intMinForLayoutUnit; - lineBox->computeReplacedAndTextLineTopAndBottom(logicalOffset, logicalBottom); - lineHeight = logicalBottom - logicalOffset; - if (logicalOffset == intMaxForLayoutUnit || lineHeight > pageLogicalHeight) - return; // Give up. We're genuinely too big even after excluding blank space and overflow. - pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); - } - LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary); - overflowsRegion = (lineHeight > remainingLogicalHeight); int lineIndex = lineCount(lineBox); if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineIndex)) { @@ -1723,20 +1605,13 @@ void RenderBlockFlow::adjustLinePositionForPagination(RootInlineBox* lineBox, La // Split the top margin in order to avoid splitting the visible part of the line. remainingLogicalHeight -= std::min(lineHeight - pageLogicalHeight, std::max<LayoutUnit>(0, logicalVisualOverflow.y() - lineBox->lineTopWithLeading())); } - LayoutUnit remainingLogicalHeightAtNewOffset = pageRemainingLogicalHeightForOffset(logicalOffset + remainingLogicalHeight, ExcludePageBoundary); - overflowsRegion = (lineHeight > remainingLogicalHeightAtNewOffset); LayoutUnit totalLogicalHeight = lineHeight + std::max<LayoutUnit>(0, logicalOffset); LayoutUnit pageLogicalHeightAtNewOffset = hasUniformPageLogicalHeight ? pageLogicalHeight : pageLogicalHeightForOffset(logicalOffset + remainingLogicalHeight); setPageBreak(logicalOffset, lineHeight - remainingLogicalHeight); if (((lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset) || (!style().hasAutoOrphans() && style().orphans() >= lineIndex)) - && !isOutOfFlowPositioned() && !isTableCell()) { - auto firstRootBox = this->firstRootBox(); - auto firstRootBoxOverflowRect = firstRootBox->logicalVisualOverflowRect(firstRootBox->lineTop(), firstRootBox->lineBottom()); - auto firstLineUpperOverhang = std::max(-firstRootBoxOverflowRect.y(), LayoutUnit()); - if (needsAppleMailPaginationQuirk(*lineBox)) - return; - setPaginationStrut(remainingLogicalHeight + logicalOffset + firstLineUpperOverhang); - } else { + && !isOutOfFlowPositioned() && !isTableCell()) + setPaginationStrut(remainingLogicalHeight + std::max<LayoutUnit>(0, logicalOffset)); + else { delta += remainingLogicalHeight; lineBox->setPaginationStrut(remainingLogicalHeight); lineBox->setIsFirstAfterPageBreak(true); @@ -1807,23 +1682,30 @@ bool RenderBlockFlow::hasNextPage(LayoutUnit logicalOffset, PageBoundaryRule pag RenderRegion* region = flowThread->regionAtBlockOffset(this, pageOffset, true); if (!region) return false; - if (region->isLastRegion()) return region->isRenderRegionSet() || region->style().regionFragment() == BreakRegionFragment || (pageBoundaryRule == IncludePageBoundary && pageOffset == region->logicalTopForFlowThreadContent()); - RenderRegion* startRegion = nullptr; - RenderRegion* endRegion = nullptr; + RenderRegion* startRegion = 0; + RenderRegion* endRegion = 0; flowThread->getRegionRangeForBox(this, startRegion, endRegion); - return (endRegion && region != endRegion); + + if (region == endRegion) + return false; + return true; } LayoutUnit RenderBlockFlow::adjustForUnsplittableChild(RenderBox& child, LayoutUnit logicalOffset, bool includeMargins) { - if (!childBoxIsUnsplittableForFragmentation(child)) - return logicalOffset; - + bool checkColumnBreaks = view().layoutState()->isPaginatingColumns(); + bool checkPageBreaks = !checkColumnBreaks && view().layoutState()->m_pageLogicalHeight; RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); + bool isUnsplittable = child.isUnsplittableForPagination() || (checkColumnBreaks && child.style().columnBreakInside() == PBAVOID) + || (checkPageBreaks && child.style().pageBreakInside() == PBAVOID) + || (checkRegionBreaks && child.style().regionBreakInside() == PBAVOID); + if (!isUnsplittable) + return logicalOffset; LayoutUnit childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : LayoutUnit()); LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); @@ -1865,6 +1747,8 @@ void RenderBlockFlow::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minH { if (RenderFlowThread* flowThread = flowThreadContainingBlock()) flowThread->updateMinimumPageHeight(this, offsetFromLogicalTopOfFirstPage() + offset, minHeight); + else if (ColumnInfo* colInfo = view().layoutState()->m_columnInfo) + colInfo->updateMinimumColumnHeight(minHeight); } LayoutUnit RenderBlockFlow::nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const @@ -1882,34 +1766,25 @@ LayoutUnit RenderBlockFlow::nextPageLogicalTop(LayoutUnit logicalOffset, PageBou LayoutUnit RenderBlockFlow::pageLogicalTopForOffset(LayoutUnit offset) const { - // Unsplittable objects clear out the pageLogicalHeight in the layout state as a way of signaling that no - // pagination should occur. Therefore we have to check this first and bail if the value has been set to 0. - LayoutUnit pageLogicalHeight = view().layoutState()->m_pageLogicalHeight; - if (!pageLogicalHeight) - return 0; - LayoutUnit firstPageLogicalTop = isHorizontalWritingMode() ? view().layoutState()->m_pageOffset.height() : view().layoutState()->m_pageOffset.width(); LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? view().layoutState()->m_layoutOffset.height() : view().layoutState()->m_layoutOffset.width(); LayoutUnit cumulativeOffset = offset + blockLogicalTop; RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (!flowThread) + if (!flowThread) { + LayoutUnit pageLogicalHeight = view().layoutState()->pageLogicalHeight(); + if (!pageLogicalHeight) + return 0; return cumulativeOffset - roundToInt(cumulativeOffset - firstPageLogicalTop) % roundToInt(pageLogicalHeight); - return firstPageLogicalTop + flowThread->pageLogicalTopForOffset(cumulativeOffset - firstPageLogicalTop); + } + return flowThread->pageLogicalTopForOffset(cumulativeOffset); } LayoutUnit RenderBlockFlow::pageLogicalHeightForOffset(LayoutUnit offset) const { - // Unsplittable objects clear out the pageLogicalHeight in the layout state as a way of signaling that no - // pagination should occur. Therefore we have to check this first and bail if the value has been set to 0. - LayoutUnit pageLogicalHeight = view().layoutState()->m_pageLogicalHeight; - if (!pageLogicalHeight) - return 0; - - // Now check for a flow thread. RenderFlowThread* flowThread = flowThreadContainingBlock(); if (!flowThread) - return pageLogicalHeight; + return view().layoutState()->m_pageLogicalHeight; return flowThread->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage()); } @@ -1932,35 +1807,6 @@ LayoutUnit RenderBlockFlow::pageRemainingLogicalHeightForOffset(LayoutUnit offse return flowThread->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule); } -LayoutUnit RenderBlockFlow::logicalHeightForChildForFragmentation(const RenderBox& child) const -{ - // This method is required because regions do not fragment monolithic elements but instead - // they let them overflow the region they flow in. This behaviour is different from the - // multicol/printing implementations, which have not yet been updated to correctly handle - // monolithic elements. - // As a result, for the moment, this method will only be used for regions, the multicol and - // printing implementations will stick to the existing behaviour until their fragmentation - // implementation is updated to match the regions implementation. - if (!flowThreadContainingBlock() || !flowThreadContainingBlock()->isRenderNamedFlowThread()) - return logicalHeightForChild(child); - - // For unsplittable elements, this method will just return the height of the element that - // fits into the current region, without the height of the part that overflows the region. - // This is done for all regions, except the last one because in that case, the logical - // height of the flow thread needs to also - if (!childBoxIsUnsplittableForFragmentation(child) || !pageLogicalHeightForOffset(logicalTopForChild(child))) - return logicalHeightForChild(child); - - // If we're on the last page this block fragments to, the logical height of the flow thread must include - // the entire unsplittable child because any following children will not be moved to the next page - // so they will need to be laid out below the current unsplittable child. - LayoutUnit childLogicalTop = logicalTopForChild(child); - if (!hasNextPage(childLogicalTop)) - return logicalHeightForChild(child); - - LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(childLogicalTop, ExcludePageBoundary); - return std::min(child.logicalHeight(), remainingLogicalHeight); -} void RenderBlockFlow::layoutLineGridBox() { @@ -1978,7 +1824,7 @@ void RenderBlockFlow::layoutLineGridBox() VerticalPositionCache verticalPositionCache; lineGridBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache); - setLineGridBox(WTFMove(lineGridBox)); + setLineGridBox(std::move(lineGridBox)); // FIXME: If any of the characteristics of the box change compared to the old one, then we need to do a deep dirtying // (similar to what happens when the page height changes). Ideally, though, we only do this if someone is actually snapping @@ -2024,20 +1870,13 @@ void RenderBlockFlow::styleDidChange(StyleDifference diff, const RenderStyle* ol if (auto fragment = renderNamedFlowFragment()) fragment->setStyle(RenderNamedFlowFragment::createStyle(style())); - if (diff >= StyleDifferenceRepaint) { - // FIXME: This could use a cheaper style-only test instead of SimpleLineLayout::canUseFor. - if (selfNeedsLayout() || !m_simpleLineLayout || !SimpleLineLayout::canUseFor(*this)) - invalidateLineLayoutPath(); + if (diff >= StyleDifferenceRepaint) + invalidateLineLayoutPath(); + + if (multiColumnFlowThread()) { + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) + child->setStyle(RenderStyle::createAnonymousStyleWithDisplay(&style(), BLOCK)); } - - if (multiColumnFlowThread()) - updateStylesForColumnChildren(); -} - -void RenderBlockFlow::updateStylesForColumnChildren() -{ - for (auto* child = firstChildBox(); child && (child->isInFlowRenderFlowThread() || child->isRenderMultiColumnSet()); child = child->nextSiblingBox()) - child->setStyle(RenderStyle::createAnonymousStyleWithDisplay(&style(), BLOCK)); } void RenderBlockFlow::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) @@ -2045,14 +1884,9 @@ void RenderBlockFlow::styleWillChange(StyleDifference diff, const RenderStyle& n const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr; s_canPropagateFloatIntoSibling = oldStyle ? !isFloatingOrOutOfFlowPositioned() && !avoidsFloats() : false; - if (oldStyle) { - EPosition oldPosition = oldStyle->position(); - EPosition newPosition = newStyle.position(); - - if (parent() && diff == StyleDifferenceLayout && oldPosition != newPosition) { - if (containsFloats() && !isFloating() && !isOutOfFlowPositioned() && newStyle.hasOutOfFlowPosition()) - markAllDescendantsWithFloatsForLayout(); - } + if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle.position()) { + if (containsFloats() && !isFloating() && !isOutOfFlowPositioned() && newStyle.hasOutOfFlowPosition()) + markAllDescendantsWithFloatsForLayout(); } RenderBlock::styleWillChange(diff, newStyle); @@ -2072,8 +1906,11 @@ void RenderBlockFlow::deleteLines() RenderBlock::deleteLines(); } -void RenderBlockFlow::moveFloatsTo(RenderBlockFlow* toBlockFlow) +void RenderBlockFlow::moveAllChildrenIncludingFloatsTo(RenderBlock* toBlock, bool fullRemoveInsert) { + RenderBlockFlow* toBlockFlow = toRenderBlockFlow(toBlock); + moveAllChildrenTo(toBlockFlow, fullRemoveInsert); + // When a portion of the render tree is being detached, anonymous blocks // will be combined as their children are deleted. In this process, the // anonymous block later in the tree is merged into the one preceeding it. @@ -2110,13 +1947,6 @@ void RenderBlockFlow::moveFloatsTo(RenderBlockFlow* toBlockFlow) } } -void RenderBlockFlow::moveAllChildrenIncludingFloatsTo(RenderBlock& toBlock, bool fullRemoveInsert) -{ - RenderBlockFlow& toBlockFlow = downcast<RenderBlockFlow>(toBlock); - moveAllChildrenTo(&toBlockFlow, fullRemoveInsert); - moveFloatsTo(&toBlockFlow); -} - void RenderBlockFlow::addOverflowFromFloats() { if (!m_floatingObjects) @@ -2125,9 +1955,9 @@ void RenderBlockFlow::addOverflowFromFloats() const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); - if (floatingObject.isDescendant()) - addOverflowFromChild(&floatingObject.renderer(), IntSize(xPositionForFloatIncludingMargin(floatingObject), yPositionForFloatIncludingMargin(floatingObject))); + FloatingObject* r = it->get(); + if (r->isDescendant()) + addOverflowFromChild(&r->renderer(), IntSize(xPositionForFloatIncludingMargin(r), yPositionForFloatIncludingMargin(r))); } } @@ -2135,7 +1965,7 @@ void RenderBlockFlow::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomp { RenderBlock::computeOverflow(oldClientAfterEdge, recomputeFloats); - if (!multiColumnFlowThread() && (recomputeFloats || createsNewFormattingContext() || hasSelfPaintingLayer())) + if (!hasColumns() && (recomputeFloats || isRoot() || expandsToEncloseOverhangingFloats() || hasSelfPaintingLayer())) addOverflowFromFloats(); } @@ -2148,38 +1978,23 @@ void RenderBlockFlow::repaintOverhangingFloats(bool paintAllDescendants) // FIXME: Avoid disabling LayoutState. At the very least, don't disable it for floats originating // in this block. Better yet would be to push extra state for the containers of other floats. - LayoutStateDisabler layoutStateDisabler(view()); + LayoutStateDisabler layoutStateDisabler(&view()); const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); + FloatingObject* floatingObject = it->get(); // Only repaint the object if it is overhanging, is not in its own layer, and // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter // condition is replaced with being a descendant of us. - auto& renderer = floatingObject.renderer(); if (logicalBottomForFloat(floatingObject) > logicalHeight() - && !renderer.hasSelfPaintingLayer() - && (floatingObject.shouldPaint() || (paintAllDescendants && renderer.isDescendantOf(this)))) { - renderer.repaint(); - renderer.repaintOverhangingFloats(false); + && !floatingObject->renderer().hasSelfPaintingLayer() + && (floatingObject->shouldPaint() || (paintAllDescendants && floatingObject->renderer().isDescendantOf(this)))) { + floatingObject->renderer().repaint(); + floatingObject->renderer().repaintOverhangingFloats(false); } } } -void RenderBlockFlow::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& point) -{ - RenderBlock::paintColumnRules(paintInfo, point); - - if (!multiColumnFlowThread() || paintInfo.context().paintingDisabled()) - return; - - // Iterate over our children and paint the column rules as needed. - for (auto& columnSet : childrenOfType<RenderMultiColumnSet>(*this)) { - LayoutPoint childPoint = columnSet.location() + flipForWritingModeForChild(&columnSet, point); - columnSet.paintColumnRules(paintInfo, childPoint); - } -} - void RenderBlockFlow::paintFloats(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool preservePhase) { if (!m_floatingObjects) @@ -2188,26 +2003,23 @@ void RenderBlockFlow::paintFloats(PaintInfo& paintInfo, const LayoutPoint& paint const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); - auto& renderer = floatingObject.renderer(); + FloatingObject* r = it->get(); // Only paint the object if our m_shouldPaint flag is set. - if (floatingObject.shouldPaint() && !renderer.hasSelfPaintingLayer()) { + if (r->shouldPaint() && !r->renderer().hasSelfPaintingLayer()) { PaintInfo currentPaintInfo(paintInfo); currentPaintInfo.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; // FIXME: LayoutPoint version of xPositionForFloatIncludingMargin would make this much cleaner. - LayoutPoint childPoint = flipFloatForWritingModeForChild(floatingObject, - LayoutPoint(paintOffset.x() + xPositionForFloatIncludingMargin(floatingObject) - renderer.x(), - paintOffset.y() + yPositionForFloatIncludingMargin(floatingObject) - renderer.y())); - renderer.paint(currentPaintInfo, childPoint); + LayoutPoint childPoint = flipFloatForWritingModeForChild(r, LayoutPoint(paintOffset.x() + xPositionForFloatIncludingMargin(r) - r->renderer().x(), paintOffset.y() + yPositionForFloatIncludingMargin(r) - r->renderer().y())); + r->renderer().paint(currentPaintInfo, childPoint); if (!preservePhase) { currentPaintInfo.phase = PaintPhaseChildBlockBackgrounds; - renderer.paint(currentPaintInfo, childPoint); + r->renderer().paint(currentPaintInfo, childPoint); currentPaintInfo.phase = PaintPhaseFloat; - renderer.paint(currentPaintInfo, childPoint); + r->renderer().paint(currentPaintInfo, childPoint); currentPaintInfo.phase = PaintPhaseForeground; - renderer.paint(currentPaintInfo, childPoint); + r->renderer().paint(currentPaintInfo, childPoint); currentPaintInfo.phase = PaintPhaseOutline; - renderer.paint(currentPaintInfo, childPoint); + r->renderer().paint(currentPaintInfo, childPoint); } } } @@ -2219,20 +2031,20 @@ void RenderBlockFlow::clipOutFloatingObjects(RenderBlock& rootBlock, const Paint const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); + FloatingObject* floatingObject = it->get(); LayoutRect floatBox(offsetFromRootBlock.width() + xPositionForFloatIncludingMargin(floatingObject), offsetFromRootBlock.height() + yPositionForFloatIncludingMargin(floatingObject), - floatingObject.renderer().width(), floatingObject.renderer().height()); + floatingObject->renderer().width(), floatingObject->renderer().height()); rootBlock.flipForWritingMode(floatBox); floatBox.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y()); - paintInfo->context().clipOut(snappedIntRect(floatBox)); + paintInfo->context->clipOut(pixelSnappedIntRect(floatBox)); } } } void RenderBlockFlow::createFloatingObjects() { - m_floatingObjects = std::make_unique<FloatingObjects>(*this); + m_floatingObjects = adoptPtr(new FloatingObjects(*this)); } void RenderBlockFlow::removeFloatingObjects() @@ -2240,8 +2052,6 @@ void RenderBlockFlow::removeFloatingObjects() if (!m_floatingObjects) return; - markSiblingsWithFloatsForLayout(); - m_floatingObjects->clear(); } @@ -2264,25 +2074,23 @@ FloatingObject* RenderBlockFlow::insertFloatingObject(RenderBox& floatBox) std::unique_ptr<FloatingObject> floatingObject = FloatingObject::create(floatBox); - // Our location is irrelevant if we're unsplittable or no pagination is in effect. Just lay out the float. + // Our location is irrelevant if we're unsplittable or no pagination is in effect. + // Just go ahead and lay out the float. bool isChildRenderBlock = floatBox.isRenderBlock(); if (isChildRenderBlock && !floatBox.needsLayout() && view().layoutState()->pageLogicalHeightChanged()) floatBox.setChildNeedsLayout(MarkOnlyThis); bool needsBlockDirectionLocationSetBeforeLayout = isChildRenderBlock && view().layoutState()->needsBlockDirectionLocationSetBeforeLayout(); - if (!needsBlockDirectionLocationSetBeforeLayout || isWritingModeRoot()) { - // We are unsplittable if we're a block flow root. + if (!needsBlockDirectionLocationSetBeforeLayout || isWritingModeRoot()) // We are unsplittable if we're a block flow root. floatBox.layoutIfNeeded(); - floatingObject->setShouldPaint(!floatBox.hasSelfPaintingLayer()); - } else { floatBox.updateLogicalWidth(); floatBox.computeAndSetBlockDirectionMargins(this); } - setLogicalWidthForFloat(*floatingObject, logicalWidthForChild(floatBox) + marginStartForChild(floatBox) + marginEndForChild(floatBox)); + setLogicalWidthForFloat(floatingObject.get(), logicalWidthForChild(floatBox) + marginStartForChild(floatBox) + marginEndForChild(floatBox)); - return m_floatingObjects->add(WTFMove(floatingObject)); + return m_floatingObjects->add(std::move(floatingObject)); } void RenderBlockFlow::removeFloatingObject(RenderBox& floatBox) @@ -2291,7 +2099,7 @@ void RenderBlockFlow::removeFloatingObject(RenderBox& floatBox) const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto it = floatingObjectSet.find<RenderBox&, FloatingObjectHashTranslator>(floatBox); if (it != floatingObjectSet.end()) { - auto& floatingObject = *it->get(); + FloatingObject* floatingObject = it->get(); if (childrenInline()) { LayoutUnit logicalTop = logicalTopForFloat(floatingObject); LayoutUnit logicalBottom = logicalBottomForFloat(floatingObject); @@ -2305,19 +2113,19 @@ void RenderBlockFlow::removeFloatingObject(RenderBox& floatBox) // accomplished by pretending they have a height of 1. logicalBottom = std::max(logicalBottom, logicalTop + 1); } - if (floatingObject.originatingLine()) { - floatingObject.originatingLine()->removeFloat(floatBox); + if (floatingObject->originatingLine()) { + floatingObject->originatingLine()->removeFloat(floatBox); if (!selfNeedsLayout()) { - ASSERT(&floatingObject.originatingLine()->renderer() == this); - floatingObject.originatingLine()->markDirty(); + ASSERT(&floatingObject->originatingLine()->renderer() == this); + floatingObject->originatingLine()->markDirty(); } #if !ASSERT_DISABLED - floatingObject.setOriginatingLine(0); + floatingObject->setOriginatingLine(0); #endif } markLinesDirtyInBlockRange(0, logicalBottom); } - m_floatingObjects->remove(&floatingObject); + m_floatingObjects->remove(floatingObject); } } } @@ -2329,7 +2137,7 @@ void RenderBlockFlow::removeFloatingObjectsBelow(FloatingObject* lastFloat, int const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); FloatingObject* curr = floatingObjectSet.last().get(); - while (curr != lastFloat && (!curr->isPlaced() || logicalTopForFloat(*curr) >= logicalOffset)) { + while (curr != lastFloat && (!curr->isPlaced() || logicalTopForFloat(curr) >= logicalOffset)) { m_floatingObjects->remove(curr); if (floatingObjectSet.isEmpty()) break; @@ -2353,27 +2161,42 @@ LayoutUnit RenderBlockFlow::logicalRightOffsetForPositioningFloat(LayoutUnit log return adjustLogicalRightOffsetForLine(offset, applyTextIndent); } -LayoutPoint RenderBlockFlow::computeLogicalLocationForFloat(const FloatingObject& floatingObject, LayoutUnit logicalTopOffset) +LayoutPoint RenderBlockFlow::computeLogicalLocationForFloat(const FloatingObject* floatingObject, LayoutUnit logicalTopOffset) const { - auto& childBox = floatingObject.renderer(); + RenderBox& childBox = floatingObject->renderer(); LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent(logicalTopOffset); // Constant part of left offset. - LayoutUnit logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); // Constant part of right offset. + LayoutUnit logicalRightOffset; // Constant part of right offset. +#if ENABLE(CSS_SHAPES) + // FIXME Bug 102948: This only works for shape outside directly set on this block. + ShapeInsideInfo* shapeInsideInfo = this->layoutShapeInsideInfo(); + // FIXME: Implement behavior for right floats. + if (shapeInsideInfo) { + LayoutSize floatLogicalSize = logicalSizeForFloat(floatingObject); + // floatingObject's logicalSize doesn't contain the actual height at this point, so we need to calculate it + floatLogicalSize.setHeight(logicalHeightForChild(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox)); + + // FIXME: If the float doesn't fit in the shape we should push it under the content box + logicalTopOffset = shapeInsideInfo->computeFirstFitPositionForFloat(floatLogicalSize); + if (logicalHeight() > logicalTopOffset) + logicalTopOffset = logicalHeight(); + + SegmentList segments = shapeInsideInfo->computeSegmentsForLine(logicalTopOffset, floatLogicalSize.height()); + // FIXME Bug 102949: Add support for shapes with multiple segments. + if (segments.size() == 1) { + // The segment offsets are relative to the content box. + logicalRightOffset = logicalLeftOffset + segments[0].logicalRight; + logicalLeftOffset += segments[0].logicalLeft; + } + } else +#endif + logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); LayoutUnit floatLogicalWidth = std::min(logicalWidthForFloat(floatingObject), logicalRightOffset - logicalLeftOffset); // The width we look for. LayoutUnit floatLogicalLeft; bool insideFlowThread = flowThreadContainingBlock(); - bool isInitialLetter = childBox.style().styleType() == FIRST_LETTER && childBox.style().initialLetterDrop() > 0; - - if (isInitialLetter) { - int letterClearance = lowestInitialLetterLogicalBottom() - logicalTopOffset; - if (letterClearance > 0) { - logicalTopOffset += letterClearance; - setLogicalHeight(logicalHeight() + letterClearance); - } - } - + if (childBox.style().floating() == LeftFloat) { LayoutUnit heightRemainingLeft = 1; LayoutUnit heightRemainingRight = 1; @@ -2409,34 +2232,6 @@ LayoutPoint RenderBlockFlow::computeLogicalLocationForFloat(const FloatingObject floatLogicalLeft -= logicalWidthForFloat(floatingObject); } - if (isInitialLetter) { - const RenderStyle& style = firstLineStyle(); - const FontMetrics& fontMetrics = style.fontMetrics(); - if (fontMetrics.hasCapHeight()) { - LayoutUnit heightOfLine = lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); - LayoutUnit beforeMarginBorderPadding = childBox.borderAndPaddingBefore() + childBox.marginBefore(); - - // Make an adjustment to align with the cap height of a theoretical block line. - LayoutUnit adjustment = fontMetrics.ascent() + (heightOfLine - fontMetrics.height()) / 2 - fontMetrics.capHeight() - beforeMarginBorderPadding; - logicalTopOffset += adjustment; - - // For sunken and raised caps, we have to make some adjustments. Test if we're sunken or raised (dropHeightDelta will be - // positive for raised and negative for sunken). - int dropHeightDelta = childBox.style().initialLetterHeight() - childBox.style().initialLetterDrop(); - - // If we're sunken, the float needs to shift down but lines still need to avoid it. In order to do that we increase the float's margin. - if (dropHeightDelta < 0) { - LayoutUnit marginTopIncrease = -dropHeightDelta * heightOfLine; - childBox.setMarginBefore(childBox.marginTop() + marginTopIncrease); - } - - // If we're raised, then we actually have to grow the height of the block, since the lines have to be pushed down as though we're placing - // empty lines beside the first letter. - if (dropHeightDelta > 0) - setLogicalHeight(logicalHeight() + dropHeightDelta * heightOfLine); - } - } - return LayoutPoint(floatLogicalLeft, logicalTopOffset); } @@ -2473,18 +2268,19 @@ bool RenderBlockFlow::positionNewFloats() // The float cannot start above the top position of the last positioned float. if (lastPlacedFloatingObject) - logicalTop = std::max(logicalTopForFloat(*lastPlacedFloatingObject), logicalTop); + logicalTop = std::max(logicalTopForFloat(lastPlacedFloatingObject), logicalTop); auto end = floatingObjectSet.end(); // Now walk through the set of unpositioned floats and place them. for (; it != end; ++it) { - auto& floatingObject = *it->get(); + FloatingObject* floatingObject = it->get(); // The containing block is responsible for positioning floats, so if we have floats in our // list that come from somewhere else, do not attempt to position them. - auto& childBox = floatingObject.renderer(); - if (childBox.containingBlock() != this) + if (floatingObject->renderer().containingBlock() != this) continue; + RenderBox& childBox = floatingObject->renderer(); + LayoutUnit childLogicalLeftMargin = style().isLeftToRightDirection() ? marginStartForChild(childBox) : marginEndForChild(childBox); LayoutRect oldRect = childBox.frameRect(); @@ -2503,11 +2299,13 @@ bool RenderBlockFlow::positionNewFloats() estimateRegionRangeForBoxChild(childBox); - childBox.markForPaginationRelayoutIfNeeded(); - childBox.layoutIfNeeded(); - LayoutState* layoutState = view().layoutState(); bool isPaginated = layoutState->isPaginated(); + if (isPaginated && !childBox.needsLayout()) + childBox.markForPaginationRelayoutIfNeeded(); + + childBox.layoutIfNeeded(); + if (isPaginated) { // If we are unsplittable and don't fit, then we need to move down. // We include our margins as part of the unsplittable area. @@ -2516,14 +2314,14 @@ bool RenderBlockFlow::positionNewFloats() // See if we have a pagination strut that is making us move down further. // Note that an unsplittable child can't also have a pagination strut, so this is // exclusive with the case above. - RenderBlock* childBlock = is<RenderBlock>(childBox) ? &downcast<RenderBlock>(childBox) : nullptr; + RenderBlock* childBlock = childBox.isRenderBlock() ? toRenderBlock(&childBox) : nullptr; if (childBlock && childBlock->paginationStrut()) { newLogicalTop += childBlock->paginationStrut(); childBlock->setPaginationStrut(0); } if (newLogicalTop != floatLogicalLocation.y()) { - floatingObject.setPaginationStrut(newLogicalTop - floatLogicalLocation.y()); + floatingObject->setPaginationStrut(newLogicalTop - floatLogicalLocation.y()); floatLogicalLocation = computeLogicalLocationForFloat(floatingObject, newLogicalTop); setLogicalLeftForFloat(floatingObject, floatLogicalLocation.x()); @@ -2544,13 +2342,13 @@ bool RenderBlockFlow::positionNewFloats() setLogicalTopForFloat(floatingObject, floatLogicalLocation.y()); - setLogicalHeightForFloat(floatingObject, logicalHeightForChildForFragmentation(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox)); + setLogicalHeightForFloat(floatingObject, logicalHeightForChild(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox)); - m_floatingObjects->addPlacedObject(&floatingObject); + m_floatingObjects->addPlacedObject(floatingObject); #if ENABLE(CSS_SHAPES) if (ShapeOutsideInfo* shapeOutside = childBox.shapeOutsideInfo()) - shapeOutside->setReferenceBoxLogicalSize(logicalSizeForChild(childBox)); + shapeOutside->setShapeSize(logicalWidthForChild(childBox), logicalHeightForChild(childBox)); #endif // If the child moved, we have to repaint it. if (childBox.checkForRepaintDuringLayout()) @@ -2559,7 +2357,7 @@ bool RenderBlockFlow::positionNewFloats() return true; } -void RenderBlockFlow::clearFloats(EClear clear) +void RenderBlockFlow::newLine(EClear clear) { positionNewFloats(); // set y position @@ -2621,23 +2419,8 @@ LayoutUnit RenderBlockFlow::lowestFloatLogicalBottom(FloatingObject::Type floatT const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); - if (floatingObject.isPlaced() && floatingObject.type() & floatType) - lowestFloatBottom = std::max(lowestFloatBottom, logicalBottomForFloat(floatingObject)); - } - return lowestFloatBottom; -} - -LayoutUnit RenderBlockFlow::lowestInitialLetterLogicalBottom() const -{ - if (!m_floatingObjects) - return 0; - LayoutUnit lowestFloatBottom = 0; - const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - auto end = floatingObjectSet.end(); - for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); - if (floatingObject.isPlaced() && floatingObject.renderer().style().styleType() == FIRST_LETTER && floatingObject.renderer().style().initialLetterDrop() > 0) + FloatingObject* floatingObject = it->get(); + if (floatingObject->isPlaced() && floatingObject->type() & floatType) lowestFloatBottom = std::max(lowestFloatBottom, logicalBottomForFloat(floatingObject)); } return lowestFloatBottom; @@ -2646,7 +2429,7 @@ LayoutUnit RenderBlockFlow::lowestInitialLetterLogicalBottom() const LayoutUnit RenderBlockFlow::addOverhangingFloats(RenderBlockFlow& child, bool makeChildPaintOtherFloats) { // Prevent floats from being added to the canvas by the root element, e.g., <html>. - if (!child.containsFloats() || child.createsNewFormattingContext()) + if (child.hasOverflowClip() || !child.containsFloats() || child.isRoot() || child.hasColumns() || child.isWritingModeRoot()) return 0; LayoutUnit childLogicalTop = child.logicalTop(); @@ -2657,14 +2440,14 @@ LayoutUnit RenderBlockFlow::addOverhangingFloats(RenderBlockFlow& child, bool ma // overflow. auto childEnd = child.m_floatingObjects->set().end(); for (auto childIt = child.m_floatingObjects->set().begin(); childIt != childEnd; ++childIt) { - auto& floatingObject = *childIt->get(); + FloatingObject* floatingObject = childIt->get(); LayoutUnit floatLogicalBottom = std::min(logicalBottomForFloat(floatingObject), LayoutUnit::max() - childLogicalTop); LayoutUnit logicalBottom = childLogicalTop + floatLogicalBottom; lowestFloatLogicalBottom = std::max(lowestFloatLogicalBottom, logicalBottom); if (logicalBottom > logicalHeight()) { // If the object is not in the list, we add it now. - if (!containsFloat(floatingObject.renderer())) { + if (!containsFloat(floatingObject->renderer())) { LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(-childLogicalLeft, -childLogicalTop) : LayoutSize(-childLogicalTop, -childLogicalLeft); bool shouldPaint = false; @@ -2672,31 +2455,31 @@ LayoutUnit RenderBlockFlow::addOverhangingFloats(RenderBlockFlow& child, bool ma // behaves properly). We always want to propagate the desire to paint the float as // far out as we can, to the outermost block that overlaps the float, stopping only // if we hit a self-painting layer boundary. - if (floatingObject.renderer().enclosingFloatPaintingLayer() == enclosingFloatPaintingLayer()) { - floatingObject.setShouldPaint(false); + if (floatingObject->renderer().enclosingFloatPaintingLayer() == enclosingFloatPaintingLayer()) { + floatingObject->setShouldPaint(false); shouldPaint = true; } // We create the floating object list lazily. if (!m_floatingObjects) createFloatingObjects(); - m_floatingObjects->add(floatingObject.copyToNewContainer(offset, shouldPaint, true)); + m_floatingObjects->add(floatingObject->copyToNewContainer(offset, shouldPaint, true)); } } else { - const auto& renderer = floatingObject.renderer(); - if (makeChildPaintOtherFloats && !floatingObject.shouldPaint() && !renderer.hasSelfPaintingLayer() - && renderer.isDescendantOf(&child) && renderer.enclosingFloatPaintingLayer() == child.enclosingFloatPaintingLayer()) { + if (makeChildPaintOtherFloats && !floatingObject->shouldPaint() && !floatingObject->renderer().hasSelfPaintingLayer() + && floatingObject->renderer().isDescendantOf(&child) && floatingObject->renderer().enclosingFloatPaintingLayer() == child.enclosingFloatPaintingLayer()) { // The float is not overhanging from this block, so if it is a descendant of the child, the child should // paint it (the other case is that it is intruding into the child), unless it has its own layer or enclosing // layer. // If makeChildPaintOtherFloats is false, it means that the child must already know about all the floats // it should paint. - floatingObject.setShouldPaint(true); + floatingObject->setShouldPaint(true); } - // Since the float doesn't overhang, it didn't get put into our list. We need to add its overflow in to the child now. - if (floatingObject.isDescendant()) - child.addOverflowFromChild(&renderer, LayoutSize(xPositionForFloatIncludingMargin(floatingObject), yPositionForFloatIncludingMargin(floatingObject))); + // Since the float doesn't overhang, it didn't get put into our list. We need to go ahead and add its overflow in to the + // child now. + if (floatingObject->isDescendant()) + child.addOverflowFromChild(&floatingObject->renderer(), LayoutSize(xPositionForFloatIncludingMargin(floatingObject), yPositionForFloatIncludingMargin(floatingObject))); } } return lowestFloatLogicalBottom; @@ -2704,25 +2487,21 @@ LayoutUnit RenderBlockFlow::addOverhangingFloats(RenderBlockFlow& child, bool ma bool RenderBlockFlow::hasOverhangingFloat(RenderBox& renderer) { - if (!m_floatingObjects || !parent()) + if (!m_floatingObjects || hasColumns() || !parent()) return false; const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - const auto it = floatingObjectSet.find<RenderBox&, FloatingObjectHashTranslator>(renderer); + auto it = floatingObjectSet.find<RenderBox&, FloatingObjectHashTranslator>(renderer); if (it == floatingObjectSet.end()) return false; - return logicalBottomForFloat(*it->get()) > logicalHeight(); + return logicalBottomForFloat(it->get()) > logicalHeight(); } -void RenderBlockFlow::addIntrudingFloats(RenderBlockFlow* prev, RenderBlockFlow* container, LayoutUnit logicalLeftOffset, LayoutUnit logicalTopOffset) +void RenderBlockFlow::addIntrudingFloats(RenderBlockFlow* prev, LayoutUnit logicalLeftOffset, LayoutUnit logicalTopOffset) { ASSERT(!avoidsFloats()); - // If we create our own block formatting context then our contents don't interact with floats outside it, even those from our parent. - if (createsNewFormattingContext()) - return; - // If the parent or previous sibling doesn't have any floats to add, don't bother. if (!prev->m_floatingObjects) return; @@ -2732,9 +2511,9 @@ void RenderBlockFlow::addIntrudingFloats(RenderBlockFlow* prev, RenderBlockFlow* const FloatingObjectSet& prevSet = prev->m_floatingObjects->set(); auto prevEnd = prevSet.end(); for (auto prevIt = prevSet.begin(); prevIt != prevEnd; ++prevIt) { - auto& floatingObject = *prevIt->get(); + FloatingObject* floatingObject = prevIt->get(); if (logicalBottomForFloat(floatingObject) > logicalTopOffset) { - if (!m_floatingObjects || !m_floatingObjects->set().contains<FloatingObject&, FloatingObjectHashTranslator>(floatingObject)) { + if (!m_floatingObjects || !m_floatingObjects->set().contains<FloatingObject&, FloatingObjectHashTranslator>(*floatingObject)) { // We create the floating object list lazily. if (!m_floatingObjects) createFloatingObjects(); @@ -2745,10 +2524,10 @@ void RenderBlockFlow::addIntrudingFloats(RenderBlockFlow* prev, RenderBlockFlow* // into account. Only apply this code if prev is the parent, since otherwise the left margin // will get applied twice. LayoutSize offset = isHorizontalWritingMode() - ? LayoutSize(logicalLeftOffset - (prev != container ? prev->marginLeft() : LayoutUnit()), logicalTopOffset) - : LayoutSize(logicalTopOffset, logicalLeftOffset - (prev != container ? prev->marginTop() : LayoutUnit())); + ? LayoutSize(logicalLeftOffset - (prev != parent() ? prev->marginLeft() : LayoutUnit()), logicalTopOffset) + : LayoutSize(logicalTopOffset, logicalLeftOffset - (prev != parent() ? prev->marginTop() : LayoutUnit())); - m_floatingObjects->add(floatingObject.copyToNewContainer(offset)); + m_floatingObjects->add(floatingObject->copyToNewContainer(offset)); } } } @@ -2769,12 +2548,12 @@ void RenderBlockFlow::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRe for (auto& block : childrenOfType<RenderBlock>(*this)) { if (!floatToRemove && block.isFloatingOrOutOfFlowPositioned()) continue; - if (!is<RenderBlockFlow>(block)) { + if (!block.isRenderBlockFlow()) { if (block.shrinkToAvoidFloats() && block.everHadLayout()) block.setChildNeedsLayout(markParents); continue; } - auto& blockFlow = downcast<RenderBlockFlow>(block); + auto& blockFlow = toRenderBlockFlow(block); if ((floatToRemove ? blockFlow.containsFloat(*floatToRemove) : blockFlow.containsFloats()) || blockFlow.shrinkToAvoidFloats()) blockFlow.markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout); } @@ -2789,21 +2568,21 @@ void RenderBlockFlow::markSiblingsWithFloatsForLayout(RenderBox* floatToRemove) auto end = floatingObjectSet.end(); for (RenderObject* next = nextSibling(); next; next = next->nextSibling()) { - if (!is<RenderBlockFlow>(*next) || next->isFloatingOrOutOfFlowPositioned()) + if (!next->isRenderBlockFlow() || next->isFloatingOrOutOfFlowPositioned() || toRenderBlock(next)->avoidsFloats()) continue; - RenderBlockFlow& nextBlock = downcast<RenderBlockFlow>(*next); + RenderBlockFlow* nextBlock = toRenderBlockFlow(next); for (auto it = floatingObjectSet.begin(); it != end; ++it) { RenderBox& floatingBox = (*it)->renderer(); if (floatToRemove && &floatingBox != floatToRemove) continue; - if (nextBlock.containsFloat(floatingBox)) - nextBlock.markAllDescendantsWithFloatsForLayout(&floatingBox); + if (nextBlock->containsFloat(floatingBox)) + nextBlock->markAllDescendantsWithFloatsForLayout(&floatingBox); } } } -LayoutPoint RenderBlockFlow::flipFloatForWritingModeForChild(const FloatingObject& child, const LayoutPoint& point) const +LayoutPoint RenderBlockFlow::flipFloatForWritingModeForChild(const FloatingObject* child, const LayoutPoint& point) const { if (!style().isFlippedBlocksWritingMode()) return point; @@ -2812,8 +2591,8 @@ LayoutPoint RenderBlockFlow::flipFloatForWritingModeForChild(const FloatingObjec // it's going to get added back in. We hide this complication here so that the calling code looks normal for the unflipped // case. if (isHorizontalWritingMode()) - return LayoutPoint(point.x(), point.y() + height() - child.renderer().height() - 2 * yPositionForFloatIncludingMargin(child)); - return LayoutPoint(point.x() + width() - child.renderer().width() - 2 * xPositionForFloatIncludingMargin(child), point.y()); + return LayoutPoint(point.x(), point.y() + height() - child->renderer().height() - 2 * yPositionForFloatIncludingMargin(child)); + return LayoutPoint(point.x() + width() - child->renderer().width() - 2 * xPositionForFloatIncludingMargin(child), point.y()); } LayoutUnit RenderBlockFlow::getClearDelta(RenderBox& child, LayoutUnit logicalTop) @@ -2844,7 +2623,7 @@ LayoutUnit RenderBlockFlow::getClearDelta(RenderBox& child, LayoutUnit logicalTo if (!result && child.avoidsFloats()) { LayoutUnit newLogicalTop = logicalTop; while (true) { - LayoutUnit availableLogicalWidthAtNewLogicalTopOffset = availableLogicalWidthForLine(newLogicalTop, DoNotIndentText, logicalHeightForChild(child)); + LayoutUnit availableLogicalWidthAtNewLogicalTopOffset = availableLogicalWidthForLine(newLogicalTop, false, logicalHeightForChild(child)); if (availableLogicalWidthAtNewLogicalTopOffset == availableLogicalWidthForContent(newLogicalTop)) return newLogicalTop - logicalTop; @@ -2894,20 +2673,19 @@ bool RenderBlockFlow::hitTestFloats(const HitTestRequest& request, HitTestResult return false; LayoutPoint adjustedLocation = accumulatedOffset; - if (is<RenderView>(*this)) - adjustedLocation += toLayoutSize(downcast<RenderView>(*this).frameView().scrollPosition()); + if (isRenderView()) + adjustedLocation += toLayoutSize(toRenderView(*this).frameView().scrollPosition()); const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto begin = floatingObjectSet.begin(); for (auto it = floatingObjectSet.end(); it != begin;) { --it; - const auto& floatingObject = *it->get(); - auto& renderer = floatingObject.renderer(); - if (floatingObject.shouldPaint() && !renderer.hasSelfPaintingLayer()) { - LayoutUnit xOffset = xPositionForFloatIncludingMargin(floatingObject) - renderer.x(); - LayoutUnit yOffset = yPositionForFloatIncludingMargin(floatingObject) - renderer.y(); + FloatingObject* floatingObject = it->get(); + if (floatingObject->shouldPaint() && !floatingObject->renderer().hasSelfPaintingLayer()) { + LayoutUnit xOffset = xPositionForFloatIncludingMargin(floatingObject) - floatingObject->renderer().x(); + LayoutUnit yOffset = yPositionForFloatIncludingMargin(floatingObject) - floatingObject->renderer().y(); LayoutPoint childPoint = flipFloatForWritingModeForChild(floatingObject, adjustedLocation + LayoutSize(xOffset, yOffset)); - if (renderer.hitTest(request, result, locationInContainer, childPoint)) { + if (floatingObject->renderer().hitTest(request, result, locationInContainer, childPoint)) { updateHitTestResult(result, locationInContainer.point() - toLayoutSize(childPoint)); return true; } @@ -2921,8 +2699,8 @@ bool RenderBlockFlow::hitTestInlineChildren(const HitTestRequest& request, HitTe { ASSERT(childrenInline()); - if (auto simpleLineLayout = this->simpleLineLayout()) - return SimpleLineLayout::hitTestFlow(*this, *simpleLineLayout, request, result, locationInContainer, accumulatedOffset, hitTestAction); + if (m_simpleLineLayout) + return SimpleLineLayout::hitTestFlow(*this, *m_simpleLineLayout, request, result, locationInContainer, accumulatedOffset, hitTestAction); return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction); } @@ -2937,17 +2715,17 @@ void RenderBlockFlow::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutU if (childrenInline()) { const_cast<RenderBlockFlow&>(*this).ensureLineBoxes(); - for (auto* box = firstRootBox(); box; box = box->nextRootBox()) { + for (auto box = firstRootBox(); box; box = box->nextRootBox()) { if (box->firstChild()) - left = std::min(left, x + LayoutUnit(box->firstChild()->x())); + left = std::min(left, x + static_cast<LayoutUnit>(box->firstChild()->x())); if (box->lastChild()) - right = std::max(right, x + LayoutUnit(ceilf(box->lastChild()->logicalRight()))); + right = std::max(right, x + static_cast<LayoutUnit>(ceilf(box->lastChild()->logicalRight()))); } } else { for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) { if (!obj->isFloatingOrOutOfFlowPositioned()) { - if (is<RenderBlockFlow>(*obj) && !obj->hasOverflowClip()) - downcast<RenderBlockFlow>(*obj).adjustForBorderFit(x + obj->x(), left, right); + if (obj->isRenderBlockFlow() && !obj->hasOverflowClip()) + toRenderBlockFlow(obj)->adjustForBorderFit(x + obj->x(), left, right); else if (obj->style().visibility() == VISIBLE) { // We are a replaced element or some kind of non-block-flow object. left = std::min(left, x + obj->x()); @@ -2961,11 +2739,11 @@ void RenderBlockFlow::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutU const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); + FloatingObject* r = it->get(); // Only examine the object if our m_shouldPaint flag is set. - if (floatingObject.shouldPaint()) { - LayoutUnit floatLeft = xPositionForFloatIncludingMargin(floatingObject) - floatingObject.renderer().x(); - LayoutUnit floatRight = floatLeft + floatingObject.renderer().width(); + if (r->shouldPaint()) { + LayoutUnit floatLeft = xPositionForFloatIncludingMargin(r) - r->renderer().x(); + LayoutUnit floatRight = floatLeft + r->renderer().width(); left = std::min(left, floatLeft); right = std::max(right, floatRight); } @@ -2975,7 +2753,7 @@ void RenderBlockFlow::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutU void RenderBlockFlow::fitBorderToLinesIfNeeded() { - if (style().borderFit() == BorderFitBorder || hasOverrideLogicalContentWidth()) + if (style().borderFit() == BorderFitBorder || hasOverrideWidth()) return; // Walk any normal flow lines to snugly fit. @@ -3004,12 +2782,6 @@ void RenderBlockFlow::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUn if (logicalTop >= logicalBottom) return; - // Floats currently affect the choice whether to use simple line layout path. - if (m_simpleLineLayout) { - invalidateLineLayoutPath(); - return; - } - RootInlineBox* lowestDirtyLine = lastRootBox(); RootInlineBox* afterLowest = lowestDirtyLine; while (lowestDirtyLine && lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && logicalBottom < LayoutUnit::max()) { @@ -3023,66 +2795,47 @@ void RenderBlockFlow::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUn } } -Optional<int> RenderBlockFlow::firstLineBaseline() const +int RenderBlockFlow::firstLineBaseline() const { if (isWritingModeRoot() && !isRubyRun()) - return Optional<int>(); + return -1; if (!childrenInline()) return RenderBlock::firstLineBaseline(); if (!hasLines()) - return Optional<int>(); + return -1; - if (auto simpleLineLayout = this->simpleLineLayout()) - return Optional<int>(SimpleLineLayout::computeFlowFirstLineBaseline(*this, *simpleLineLayout)); + if (m_simpleLineLayout) + return SimpleLineLayout::computeFlowFirstLineBaseline(*this, *m_simpleLineLayout); ASSERT(firstRootBox()); return firstRootBox()->logicalTop() + firstLineStyle().fontMetrics().ascent(firstRootBox()->baselineType()); } -Optional<int> RenderBlockFlow::inlineBlockBaseline(LineDirectionMode lineDirection) const +int RenderBlockFlow::inlineBlockBaseline(LineDirectionMode lineDirection) const { if (isWritingModeRoot() && !isRubyRun()) - return Optional<int>(); - - // Note that here we only take the left and bottom into consideration. Our caller takes the right and top into consideration. - float boxHeight = lineDirection == HorizontalLine ? height() + m_marginBox.bottom() : width() + m_marginBox.left(); - float lastBaseline; - if (!childrenInline()) { - Optional<int> inlineBlockBaseline = RenderBlock::inlineBlockBaseline(lineDirection); - if (!inlineBlockBaseline) - return inlineBlockBaseline; - lastBaseline = inlineBlockBaseline.value(); - } else { - if (!hasLines()) { - if (!hasLineIfEmpty()) - return Optional<int>(); - const auto& fontMetrics = firstLineStyle().fontMetrics(); - return Optional<int>(fontMetrics.ascent() - + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 - + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight())); - } + return -1; - if (auto simpleLineLayout = this->simpleLineLayout()) - lastBaseline = SimpleLineLayout::computeFlowLastLineBaseline(*this, *simpleLineLayout); - else { - bool isFirstLine = lastRootBox() == firstRootBox(); - const auto& style = isFirstLine ? firstLineStyle() : this->style(); - lastBaseline = lastRootBox()->logicalTop() + style.fontMetrics().ascent(lastRootBox()->baselineType()); - } + if (!childrenInline()) + return RenderBlock::inlineBlockBaseline(lineDirection); + + if (!hasLines()) { + if (!hasLineIfEmpty()) + return -1; + const FontMetrics& fontMetrics = firstLineStyle().fontMetrics(); + return fontMetrics.ascent() + + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 + + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); } - // According to the CSS spec http://www.w3.org/TR/CSS21/visudet.html, we shouldn't be performing this min, but should - // instead be returning boxHeight directly. However, we feel that a min here is better behavior (and is consistent - // enough with the spec to not cause tons of breakages). - return Optional<int>(style().overflowY() == OVISIBLE ? lastBaseline : std::min(boxHeight, lastBaseline)); -} -void RenderBlockFlow::setSelectionState(SelectionState state) -{ - if (state != SelectionNone) - ensureLineBoxes(); - RenderBoxModelObject::setSelectionState(state); + if (m_simpleLineLayout) + return SimpleLineLayout::computeFlowLastLineBaseline(*this, *m_simpleLineLayout); + + bool isFirstLine = lastRootBox() == firstRootBox(); + const RenderStyle& style = isFirstLine ? firstLineStyle() : this->style(); + return lastRootBox()->logicalTop() + style.fontMetrics().ascent(lastRootBox()->baselineType()); } GapRects RenderBlockFlow::inlineSelectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, @@ -3096,7 +2849,8 @@ GapRects RenderBlockFlow::inlineSelectionGaps(RenderBlock& rootBlock, const Layo if (!hasLines()) { if (containsStart) { - // Update our lastLogicalTop to be the bottom of the block. <hr>s or empty blocks with height can trip this case. + // Go ahead and update our lastLogicalTop to be the bottom of the block. <hr>s or empty blocks with height can trip this + // case. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache); lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache); @@ -3114,7 +2868,7 @@ GapRects RenderBlockFlow::inlineSelectionGaps(RenderBlock& rootBlock, const Layo LayoutUnit selHeight = curr->selectionHeightAdjustedForPrecedingBlock(); if (!containsStart && !lastSelectedLine && - selectionState() != SelectionStart && selectionState() != SelectionBoth && !isRubyBase()) + selectionState() != SelectionStart && selectionState() != SelectionBoth) result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, selTop, cache, paintInfo)); LayoutRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight); @@ -3132,7 +2886,7 @@ GapRects RenderBlockFlow::inlineSelectionGaps(RenderBlock& rootBlock, const Layo lastSelectedLine = lastRootBox(); if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { - // Update our lastY to be the bottom of the last selected line. + // Go ahead and update our lastY to be the bottom of the last selected line. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + lastSelectedLine->selectionBottom(); lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom(), cache); lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom(), cache); @@ -3145,26 +2899,14 @@ void RenderBlockFlow::createRenderNamedFlowFragmentIfNeeded() if (!document().cssRegionsEnabled() || renderNamedFlowFragment() || isRenderNamedFlowFragment()) return; - // FIXME: Multicolumn regions not yet supported (http://dev.w3.org/csswg/css-regions/#multi-column-regions) - if (style().isDisplayRegionType() && style().hasFlowFrom() && !style().specifiesColumns()) { + if (style().isDisplayRegionType() && style().hasFlowFrom()) { RenderNamedFlowFragment* flowFragment = new RenderNamedFlowFragment(document(), RenderNamedFlowFragment::createStyle(style())); flowFragment->initializeStyle(); - addChild(flowFragment); setRenderNamedFlowFragment(flowFragment); + addChild(renderNamedFlowFragment()); } } -bool RenderBlockFlow::needsLayoutAfterRegionRangeChange() const -{ - // A block without floats or that expands to enclose them won't need a relayout - // after a region range change. There is no overflow content needing relayout - // in the region chain because the region range can only shrink after the estimation. - if (!containsFloats() || createsNewFormattingContext()) - return false; - - return true; -} - bool RenderBlockFlow::canHaveChildren() const { return !renderNamedFlowFragment() ? RenderBlock::canHaveChildren() : renderNamedFlowFragment()->canHaveChildren(); @@ -3187,31 +2929,27 @@ void RenderBlockFlow::updateLogicalHeight() { RenderBlock::updateLogicalHeight(); - if (renderNamedFlowFragment()) { + if (renderNamedFlowFragment()) renderNamedFlowFragment()->setLogicalHeight(std::max<LayoutUnit>(0, logicalHeight() - borderAndPaddingLogicalHeight())); - renderNamedFlowFragment()->invalidateRegionIfNeeded(); - } } void RenderBlockFlow::setRenderNamedFlowFragment(RenderNamedFlowFragment* flowFragment) { RenderBlockFlowRareData& rareData = ensureRareBlockFlowData(); - if (auto* flowFragmentOnFlow = std::exchange(rareData.m_renderNamedFlowFragment, nullptr)) - flowFragmentOnFlow->destroy(); + if (rareData.m_renderNamedFlowFragment) + rareData.m_renderNamedFlowFragment->destroy(); rareData.m_renderNamedFlowFragment = flowFragment; } void RenderBlockFlow::setMultiColumnFlowThread(RenderMultiColumnFlowThread* flowThread) { - if (flowThread || hasRareBlockFlowData()) { - RenderBlockFlowRareData& rareData = ensureRareBlockFlowData(); - rareData.m_multiColumnFlowThread = flowThread; - } + RenderBlockFlowRareData& rareData = ensureRareBlockFlowData(); + rareData.m_multiColumnFlowThread = flowThread; } static bool shouldCheckLines(const RenderBlockFlow& blockFlow) { - return !blockFlow.isFloatingOrOutOfFlowPositioned() && blockFlow.style().height().isAuto(); + return !blockFlow.isFloatingOrOutOfFlowPositioned() && !blockFlow.isRunIn() && blockFlow.style().height().isAuto(); } RootInlineBox* RenderBlockFlow::lineAtIndex(int i) const @@ -3222,7 +2960,7 @@ RootInlineBox* RenderBlockFlow::lineAtIndex(int i) const return nullptr; if (childrenInline()) { - for (auto* box = firstRootBox(); box; box = box->nextRootBox()) { + for (auto box = firstRootBox(); box; box = box->nextRootBox()) { if (!i--) return box; } @@ -3247,12 +2985,8 @@ int RenderBlockFlow::lineCount(const RootInlineBox* stopRootInlineBox, bool* fou int count = 0; if (childrenInline()) { - if (auto simpleLineLayout = this->simpleLineLayout()) { - ASSERT(!stopRootInlineBox); - return simpleLineLayout->lineCount(); - } - for (auto* box = firstRootBox(); box; box = box->nextRootBox()) { - ++count; + for (auto box = firstRootBox(); box; box = box->nextRootBox()) { + count++; if (box == stopRootInlineBox) { if (found) *found = true; @@ -3283,18 +3017,18 @@ static int getHeightForLineCount(const RenderBlockFlow& block, int lineCount, bo return -1; if (block.childrenInline()) { - for (auto* box = block.firstRootBox(); box; box = box->nextRootBox()) { + for (auto box = block.firstRootBox(); box; box = box->nextRootBox()) { if (++count == lineCount) return box->lineBottom() + (includeBottom ? (block.borderBottom() + block.paddingBottom()) : LayoutUnit()); } } else { - RenderBox* normalFlowChildWithoutLines = nullptr; - for (auto* obj = block.firstChildBox(); obj; obj = obj->nextSiblingBox()) { - if (is<RenderBlockFlow>(*obj) && shouldCheckLines(downcast<RenderBlockFlow>(*obj))) { - int result = getHeightForLineCount(downcast<RenderBlockFlow>(*obj), lineCount, false, count); + RenderBox* normalFlowChildWithoutLines = 0; + for (auto obj = block.firstChildBox(); obj; obj = obj->nextSiblingBox()) { + if (obj->isRenderBlockFlow() && shouldCheckLines(toRenderBlockFlow(*obj))) { + int result = getHeightForLineCount(toRenderBlockFlow(*obj), lineCount, false, count); if (result != -1) return result + obj->y() + (includeBottom ? (block.borderBottom() + block.paddingBottom()) : LayoutUnit()); - } else if (!obj->isFloatingOrOutOfFlowPositioned()) + } else if (!obj->isFloatingOrOutOfFlowPositioned() && !obj->isRunIn()) normalFlowChildWithoutLines = obj; } if (normalFlowChildWithoutLines && !lineCount) @@ -3319,7 +3053,7 @@ void RenderBlockFlow::clearTruncation() ensureLineBoxes(); setHasMarkupTruncation(false); - for (auto* box = firstRootBox(); box; box = box->nextRootBox()) + for (auto box = firstRootBox(); box; box = box->nextRootBox()) box->clearTruncation(); return; } @@ -3332,8 +3066,8 @@ void RenderBlockFlow::clearTruncation() bool RenderBlockFlow::containsNonZeroBidiLevel() const { - for (auto* root = firstRootBox(); root; root = root->nextRootBox()) { - for (auto* box = root->firstLeafChild(); box; box = box->nextLeafChild()) { + for (auto root = firstRootBox(); root; root = root->nextRootBox()) { + for (auto box = root->firstLeafChild(); box; box = box->nextLeafChild()) { if (box->bidiLevel()) return true; } @@ -3349,14 +3083,14 @@ Position RenderBlockFlow::positionForBox(InlineBox *box, bool start) const if (!box->renderer().nonPseudoNode()) return createLegacyEditingPosition(nonPseudoElement(), start ? caretMinOffset() : caretMaxOffset()); - if (!is<InlineTextBox>(*box)) + if (!box->isInlineTextBox()) return createLegacyEditingPosition(box->renderer().nonPseudoNode(), start ? box->renderer().caretMinOffset() : box->renderer().caretMaxOffset()); - auto& textBox = downcast<InlineTextBox>(*box); - return createLegacyEditingPosition(textBox.renderer().nonPseudoNode(), start ? textBox.start() : textBox.start() + textBox.len()); + InlineTextBox* textBox = toInlineTextBox(box); + return createLegacyEditingPosition(box->renderer().nonPseudoNode(), start ? textBox->start() : textBox->start() + textBox->len()); } -VisiblePosition RenderBlockFlow::positionForPointWithInlineChildren(const LayoutPoint& pointInLogicalContents, const RenderRegion* region) +VisiblePosition RenderBlockFlow::positionForPointWithInlineChildren(const LayoutPoint& pointInLogicalContents) { ASSERT(childrenInline()); @@ -3373,9 +3107,6 @@ VisiblePosition RenderBlockFlow::positionForPointWithInlineChildren(const Layout RootInlineBox* firstRootBoxWithChildren = 0; RootInlineBox* lastRootBoxWithChildren = 0; for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { - if (region && root->containingRegion() != region) - continue; - if (!root->firstLeafChild()) continue; if (!firstRootBoxWithChildren) @@ -3431,8 +3162,8 @@ VisiblePosition RenderBlockFlow::positionForPointWithInlineChildren(const Layout if (!isHorizontalWritingMode()) point = point.transposedPoint(); if (closestBox->renderer().isReplaced()) - return positionForPointRespectingEditingBoundaries(*this, downcast<RenderBox>(closestBox->renderer()), point); - return closestBox->renderer().positionForPoint(point, nullptr); + return positionForPointRespectingEditingBoundaries(*this, toRenderBox(closestBox->renderer()), point); + return closestBox->renderer().positionForPoint(point); } if (lastRootBoxWithChildren) { @@ -3449,23 +3180,26 @@ VisiblePosition RenderBlockFlow::positionForPointWithInlineChildren(const Layout return createVisiblePosition(0, DOWNSTREAM); } -VisiblePosition RenderBlockFlow::positionForPoint(const LayoutPoint& point, const RenderRegion* region) +VisiblePosition RenderBlockFlow::positionForPoint(const LayoutPoint& point) { if (auto fragment = renderNamedFlowFragment()) - return fragment->positionForPoint(point, region); - return RenderBlock::positionForPoint(point, region); + return fragment->positionForPoint(point); + return RenderBlock::positionForPoint(point); } -void RenderBlockFlow::addFocusRingRectsForInlineChildren(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) +void RenderBlockFlow::addFocusRingRectsForInlineChildren(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) { ASSERT(childrenInline()); + + ensureLineBoxes(); + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { LayoutUnit top = std::max<LayoutUnit>(curr->lineTop(), curr->top()); LayoutUnit bottom = std::min<LayoutUnit>(curr->lineBottom(), curr->top() + curr->height()); LayoutRect rect(additionalOffset.x() + curr->x(), additionalOffset.y() + top, curr->width(), bottom - top); if (!rect.isEmpty()) - rects.append(rect); + rects.append(pixelSnappedIntRect(rect)); } } @@ -3473,19 +3207,69 @@ void RenderBlockFlow::paintInlineChildren(PaintInfo& paintInfo, const LayoutPoin { ASSERT(childrenInline()); - if (auto simpleLineLayout = this->simpleLineLayout()) { - SimpleLineLayout::paintFlow(*this, *simpleLineLayout, paintInfo, paintOffset); + if (m_simpleLineLayout) { + SimpleLineLayout::paintFlow(*this, *m_simpleLineLayout, paintInfo, paintOffset); return; } m_lineBoxes.paint(this, paintInfo, paintOffset); } -bool RenderBlockFlow::relayoutForPagination(LayoutStateMaintainer& statePusher) +bool RenderBlockFlow::relayoutForPagination(bool hasSpecifiedPageLogicalHeight, LayoutUnit pageLogicalHeight, LayoutStateMaintainer& statePusher) { - if (!multiColumnFlowThread() || !multiColumnFlowThread()->shouldRelayoutForPagination()) + if (!hasColumns() && !multiColumnFlowThread()) + return false; + + if (hasColumns()) { + RefPtr<RenderOverflow> savedOverflow = m_overflow.release(); + if (childrenInline()) + addOverflowFromInlineChildren(); + else + addOverflowFromBlockChildren(); + LayoutUnit layoutOverflowLogicalBottom = (isHorizontalWritingMode() ? layoutOverflowRect().maxY() : layoutOverflowRect().maxX()) - borderAndPaddingBefore(); + + // FIXME: We don't balance properly at all in the presence of forced page breaks. We need to understand what + // the distance between forced page breaks is so that we can avoid making the minimum column height too tall. + ColumnInfo* colInfo = columnInfo(); + if (!hasSpecifiedPageLogicalHeight) { + LayoutUnit columnHeight = pageLogicalHeight; + int minColumnCount = colInfo->forcedBreaks() + 1; + int desiredColumnCount = colInfo->desiredColumnCount(); + if (minColumnCount >= desiredColumnCount) { + // The forced page breaks are in control of the balancing. Just set the column height to the + // maximum page break distance. + if (!pageLogicalHeight) { + LayoutUnit distanceBetweenBreaks = std::max<LayoutUnit>(colInfo->maximumDistanceBetweenForcedBreaks(), + view().layoutState()->pageLogicalOffset(this, borderAndPaddingBefore() + layoutOverflowLogicalBottom) - colInfo->forcedBreakOffset()); + columnHeight = std::max(colInfo->minimumColumnHeight(), distanceBetweenBreaks); + } + } else if (layoutOverflowLogicalBottom > boundedMultiply(pageLogicalHeight, desiredColumnCount)) { + // Now that we know the intrinsic height of the columns, we have to rebalance them. + columnHeight = std::max<LayoutUnit>(colInfo->minimumColumnHeight(), ceilf((float)layoutOverflowLogicalBottom / desiredColumnCount)); + } + + if (columnHeight && columnHeight != pageLogicalHeight) { + statePusher.pop(); + setEverHadLayout(true); + layoutBlock(false, columnHeight); + return true; + } + } + + if (pageLogicalHeight) + colInfo->setColumnCountAndHeight(ceilf((float)layoutOverflowLogicalBottom / pageLogicalHeight), pageLogicalHeight); + + if (columnCount(colInfo)) { + setLogicalHeight(borderAndPaddingBefore() + colInfo->columnHeight() + borderAndPaddingAfter() + scrollbarLogicalHeight()); + clearOverflow(); + } else + m_overflow = savedOverflow.release(); + return false; + } + + if (!multiColumnFlowThread()->shouldRelayoutForPagination()) return false; - multiColumnFlowThread()->setNeedsHeightsRecalculation(false); + multiColumnFlowThread()->setNeedsRebalancing(false); multiColumnFlowThread()->setInBalancingPass(true); // Prevent re-entering this method (and recursion into layout). bool needsRelayout; @@ -3498,17 +3282,15 @@ bool RenderBlockFlow::relayoutForPagination(LayoutStateMaintainer& statePusher) // passes than that, though, but the number of retries should not exceed the number of // columns, unless we have a bug. needsRelayout = false; - for (RenderMultiColumnSet* multicolSet = multiColumnFlowThread()->firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) { - if (multicolSet->recalculateColumnHeight(firstPass)) - needsRelayout = true; - if (needsRelayout) { - // Once a column set gets a new column height, that column set and all successive column - // sets need to be laid out over again, since their logical top will be affected by - // this, and therefore their column heights may change as well, at least if the multicol - // height is constrained. - multicolSet->setChildNeedsLayout(MarkOnlyThis); + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) + if (childBox != multiColumnFlowThread() && childBox->isRenderMultiColumnSet()) { + RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox); + if (multicolSet->recalculateBalancedHeight(firstPass)) { + multicolSet->setChildNeedsLayout(MarkOnlyThis); + needsRelayout = true; + } } - } + if (needsRelayout) { // Layout again. Column balancing resulted in a new height. neededRelayout = true; @@ -3530,66 +3312,38 @@ bool RenderBlockFlow::hasLines() const { ASSERT(childrenInline()); - if (auto simpleLineLayout = this->simpleLineLayout()) - return simpleLineLayout->lineCount(); + if (m_simpleLineLayout) + return m_simpleLineLayout->lineCount(); return lineBoxes().firstLineBox(); } -void RenderBlockFlow::invalidateLineLayoutPath() +void RenderBlockFlow::layoutSimpleLines(LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom) { - switch (lineLayoutPath()) { - case UndeterminedPath: - case ForceLineBoxesPath: - ASSERT(!m_simpleLineLayout); - return; - case LineBoxesPath: - ASSERT(!m_simpleLineLayout); - setLineLayoutPath(UndeterminedPath); - return; - case SimpleLinesPath: - // The simple line layout may have become invalid. - m_simpleLineLayout = nullptr; - setNeedsLayout(); - setLineLayoutPath(UndeterminedPath); - return; - } - ASSERT_NOT_REACHED(); -} - -void RenderBlockFlow::layoutSimpleLines(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom) -{ - bool needsLayout = selfNeedsLayout() || relayoutChildren || !m_simpleLineLayout; - if (needsLayout) { - deleteLineBoxesBeforeSimpleLineLayout(); - m_simpleLineLayout = SimpleLineLayout::create(*this); - } ASSERT(!m_lineBoxes.firstLineBox()); + m_simpleLineLayout = SimpleLineLayout::create(*this); + LayoutUnit lineLayoutHeight = SimpleLineLayout::computeFlowHeight(*this, *m_simpleLineLayout); LayoutUnit lineLayoutTop = borderAndPaddingBefore(); + repaintLogicalTop = lineLayoutTop; - repaintLogicalBottom = needsLayout ? repaintLogicalTop + lineLayoutHeight : repaintLogicalTop; + repaintLogicalBottom = lineLayoutTop + lineLayoutHeight; + setLogicalHeight(lineLayoutTop + lineLayoutHeight + borderAndPaddingAfter()); } void RenderBlockFlow::deleteLineBoxesBeforeSimpleLineLayout() { - ASSERT(lineLayoutPath() == SimpleLinesPath); + ASSERT(m_lineLayoutPath == SimpleLinesPath); lineBoxes().deleteLineBoxes(); - for (auto& renderer : childrenOfType<RenderObject>(*this)) { - if (is<RenderText>(renderer)) - downcast<RenderText>(renderer).deleteLineBoxesBeforeSimpleLineLayout(); - else if (is<RenderLineBreak>(renderer)) - downcast<RenderLineBreak>(renderer).deleteLineBoxesBeforeSimpleLineLayout(); - else - ASSERT_NOT_REACHED(); - } + toRenderText(firstChild())->deleteLineBoxesBeforeSimpleLineLayout(); } void RenderBlockFlow::ensureLineBoxes() { - setLineLayoutPath(ForceLineBoxesPath); + m_lineLayoutPath = ForceLineBoxesPath; + if (!m_simpleLineLayout) return; m_simpleLineLayout = nullptr; @@ -3611,14 +3365,12 @@ void RenderBlockFlow::ensureLineBoxes() clearNeedsLayout(); } -#if ENABLE(TREE_DEBUGGING) -void RenderBlockFlow::showLineTreeAndMark(const InlineBox* markedBox, int depth) const +#ifndef NDEBUG +void RenderBlockFlow::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj) const { + RenderBlock::showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj); for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) - root->showLineTreeAndMark(markedBox, depth); - - if (auto simpleLineLayout = this->simpleLineLayout()) - SimpleLineLayout::showLineLayoutForFlow(*this, *simpleLineLayout, depth); + root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, 1); } #endif @@ -3639,10 +3391,10 @@ void RenderBlockFlow::materializeRareBlockFlowData() #if ENABLE(IOS_TEXT_AUTOSIZING) inline static bool isVisibleRenderText(RenderObject* renderer) { - if (!is<RenderText>(*renderer)) + if (!renderer->isText()) return false; - RenderText& renderText = downcast<RenderText>(*renderer); - return !renderText.linesBoundingBox().isEmpty() && !renderText.text()->containsOnlyWhitespace(); + RenderText* renderText = toRenderText(renderer); + return !renderText->linesBoundingBox().isEmpty() && !renderText->text()->containsOnlyWhitespace(); } inline static bool resizeTextPermitted(RenderObject* render) @@ -3651,25 +3403,31 @@ inline static bool resizeTextPermitted(RenderObject* render) auto renderer = render->parent(); while (renderer) { // Get the first non-shadow HTMLElement and see if it's an input. - if (is<HTMLElement>(renderer->element()) && !renderer->element()->isInShadowTree()) { - const HTMLElement& element = downcast<HTMLElement>(*renderer->element()); - return !is<HTMLInputElement>(element) && !is<HTMLTextAreaElement>(element); + if (renderer->element() && renderer->element()->isHTMLElement() && !renderer->element()->isInShadowTree()) { + const HTMLElement& element = toHTMLElement(*renderer->element()); + return !isHTMLInputElement(element) && !isHTMLTextAreaElement(element); } renderer = renderer->parent(); } return true; } -int RenderBlockFlow::lineCountForTextAutosizing() +int RenderBlockFlow::immediateLineCount() { - if (style().visibility() != VISIBLE) - return 0; - if (childrenInline()) - return lineCount(); + // Copied and modified from RenderBlock::lineCount. // Only descend into list items. int count = 0; - for (auto& listItem : childrenOfType<RenderListItem>(*this)) - count += listItem.lineCount(); + if (style().visibility() == VISIBLE) { + if (childrenInline()) { + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + count++; + } else { + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) { + if (obj->isListItem()) + count += toRenderBlockFlow(obj)->lineCount(); + } + } + } return count; } @@ -3702,7 +3460,7 @@ void RenderBlockFlow::adjustComputedFontSizes(float size, float visibleWidth) unsigned lineCount; if (m_lineCountForTextAutosizing == NOT_SET) { - int count = lineCountForTextAutosizing(); + int count = immediateLineCount(); if (!count) lineCount = NO_LINE; else if (count == 1) @@ -3722,9 +3480,9 @@ void RenderBlockFlow::adjustComputedFontSizes(float size, float visibleWidth) for (RenderObject* descendent = traverseNext(this, isNonBlocksOrNonFixedHeightListItems); descendent; descendent = descendent->traverseNext(this, isNonBlocksOrNonFixedHeightListItems)) { if (isVisibleRenderText(descendent) && resizeTextPermitted(descendent)) { - RenderText& text = downcast<RenderText>(*descendent); - RenderStyle& oldStyle = text.style(); - auto fontDescription = oldStyle.fontDescription(); + RenderText* text = toRenderText(descendent); + RenderStyle& oldStyle = text->style(); + FontDescription fontDescription = oldStyle.fontDescription(); float specifiedSize = fontDescription.specifiedSize(); float scaledSize = roundf(specifiedSize * scale); if (scaledSize > 0 && scaledSize < minFontSize) { @@ -3738,8 +3496,8 @@ void RenderBlockFlow::adjustComputedFontSizes(float size, float visibleWidth) float candidateNewSize = 0; float lineTextMultiplier = lineCount == ONE_LINE ? oneLineTextMultiplier(specifiedSize) : textMultiplier(specifiedSize); candidateNewSize = roundf(std::min(minFontSize, specifiedSize * lineTextMultiplier)); - if (candidateNewSize > specifiedSize && candidateNewSize != fontDescription.computedSize() && text.textNode() && oldStyle.textSizeAdjust().isAuto()) - document().addAutoSizingNode(text.textNode(), candidateNewSize); + if (candidateNewSize > specifiedSize && candidateNewSize != fontDescription.computedSize() && text->textNode() && oldStyle.textSizeAdjust().isAuto()) + document().addAutoSizingNode(text->textNode(), candidateNewSize); } } } @@ -3748,23 +3506,31 @@ void RenderBlockFlow::adjustComputedFontSizes(float size, float visibleWidth) RenderObject* RenderBlockFlow::layoutSpecialExcludedChild(bool relayoutChildren) { - RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread(); - if (!flowThread) - return nullptr; - - setLogicalTopForChild(*flowThread, borderAndPaddingBefore()); + if (!multiColumnFlowThread()) + return 0; - if (relayoutChildren) - flowThread->setChildNeedsLayout(MarkOnlyThis); + // Update the dimensions of our regions before we lay out the flow thread. + // FIXME: Eventually this is going to get way more complicated, and we will be destroying regions + // instead of trying to keep them around. + bool shouldInvalidateRegions = false; + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { + if (childBox == multiColumnFlowThread()) + continue; - if (flowThread->needsLayout()) { - for (RenderMultiColumnSet* columnSet = flowThread->firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) - columnSet->prepareForLayout(!flowThread->inBalancingPass()); + if (relayoutChildren || childBox->needsLayout()) { + if (!multiColumnFlowThread()->inBalancingPass() && childBox->isRenderMultiColumnSet()) + toRenderMultiColumnSet(childBox)->prepareForLayout(); + shouldInvalidateRegions = true; + } + } + + if (shouldInvalidateRegions) + multiColumnFlowThread()->invalidateRegions(); - flowThread->invalidateRegions(MarkOnlyThis); - flowThread->setNeedsHeightsRecalculation(true); - flowThread->layout(); - } else { + if (relayoutChildren) + multiColumnFlowThread()->setChildNeedsLayout(MarkOnlyThis); + + if (multiColumnFlowThread()->requiresBalancing()) { // At the end of multicol layout, relayoutForPagination() is called unconditionally, but if // no children are to be laid out (e.g. fixed width with layout already being up-to-date), // we want to prevent it from doing any work, so that the column balancing machinery doesn't @@ -3773,78 +3539,69 @@ RenderObject* RenderBlockFlow::layoutSpecialExcludedChild(bool relayoutChildren) // are actually required to guarantee this. The calculation of implicit breaks needs to be // preceded by a proper layout pass, since it's layout that sets up content runs, and the // runs get deleted right after every pass. - flowThread->setNeedsHeightsRecalculation(false); + multiColumnFlowThread()->setNeedsRebalancing(shouldInvalidateRegions || multiColumnFlowThread()->needsLayout()); } - determineLogicalLeftPositionForChild(*flowThread); - return flowThread; + setLogicalTopForChild(*multiColumnFlowThread(), borderAndPaddingBefore()); + multiColumnFlowThread()->layoutIfNeeded(); + determineLogicalLeftPositionForChild(*multiColumnFlowThread()); + + return multiColumnFlowThread(); } void RenderBlockFlow::addChild(RenderObject* newChild, RenderObject* beforeChild) { if (multiColumnFlowThread()) return multiColumnFlowThread()->addChild(newChild, beforeChild); - if (beforeChild) { - if (RenderFlowThread* containingFlowThread = flowThreadContainingBlock()) - beforeChild = containingFlowThread->resolveMovedChild(beforeChild); - } RenderBlock::addChild(newChild, beforeChild); } -void RenderBlockFlow::removeChild(RenderObject& oldChild) -{ - if (!documentBeingDestroyed()) { - RenderFlowThread* flowThread = multiColumnFlowThread(); - if (flowThread && flowThread != &oldChild) - flowThread->flowThreadRelativeWillBeRemoved(&oldChild); - } - RenderBlock::removeChild(oldChild); -} - -void RenderBlockFlow::checkForPaginationLogicalHeightChange(bool& relayoutChildren, LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged) +void RenderBlockFlow::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight) { - // If we don't use columns or flow threads, then bail. - if (!isRenderFlowThread() && !multiColumnFlowThread()) + // If we don't use either of the two column implementations or a flow thread, then bail. + if (!isRenderFlowThread() && !multiColumnFlowThread() && !hasColumns()) return; // We don't actually update any of the variables. We just subclassed to adjust our column height. - if (RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread()) { - LogicalExtentComputedValues computedValues; - computeLogicalHeight(LayoutUnit(), logicalTop(), computedValues); - LayoutUnit columnHeight = computedValues.m_extent - borderAndPaddingLogicalHeight() - scrollbarLogicalHeight(); - LayoutUnit oldHeightAvailable = flowThread->columnHeightAvailable(); - flowThread->setColumnHeightAvailable(std::max<LayoutUnit>(columnHeight, 0)); - if (oldHeightAvailable != flowThread->columnHeightAvailable()) - relayoutChildren = true; - } else if (is<RenderFlowThread>(*this)) { - RenderFlowThread& flowThread = downcast<RenderFlowThread>(*this); - - // FIXME: This is a hack to always make sure we have a page logical height, if said height - // is known. The page logical height thing in LayoutState is meaningless for flow - // thread-based pagination (page height isn't necessarily uniform throughout the flow - // thread), but as long as it is used universally as a means to determine whether page - // height is known or not, we need this. Page height is unknown when column balancing is - // enabled and flow thread height is still unknown (i.e. during the first layout pass). When - // it's unknown, we need to prevent the pagination code from assuming page breaks everywhere - // and thereby eating every top margin. It should be trivial to clean up and get rid of this - // hack once the old multicol implementation is gone. - pageLogicalHeight = flowThread.isPageLogicalHeightKnown() ? LayoutUnit(1) : LayoutUnit(0); - - pageLogicalHeightChanged = flowThread.pageLogicalSizeChanged(); - } -} + if (multiColumnFlowThread()) { + updateLogicalHeight(); + multiColumnFlowThread()->setColumnHeightAvailable(std::max<LayoutUnit>(contentLogicalHeight(), 0)); + setLogicalHeight(0); + } else if (hasColumns()) { + ColumnInfo* colInfo = columnInfo(); + + if (!pageLogicalHeight) { + // We need to go ahead and set our explicit page height if one exists, so that we can + // avoid doing two layout passes. + updateLogicalHeight(); + LayoutUnit columnHeight = isRenderView() ? view().pageOrViewLogicalHeight() : contentLogicalHeight(); + if (columnHeight > 0) { + pageLogicalHeight = columnHeight; + hasSpecifiedPageLogicalHeight = true; + } + setLogicalHeight(0); + } -bool RenderBlockFlow::requiresColumns(int desiredColumnCount) const -{ - // If overflow-y is set to paged-x or paged-y on the body or html element, we'll handle the paginating - // in the RenderView instead. - bool isPaginated = (style().overflowY() == OPAGEDX || style().overflowY() == OPAGEDY) && !(isDocumentElementRenderer() || isBody()); + if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) + pageLogicalHeightChanged = true; - return firstChild() && (desiredColumnCount != 1 || !style().hasAutoColumnWidth() || !style().hasInlineColumnAxis() || isPaginated); + colInfo->setColumnHeight(pageLogicalHeight); + + if (!hasSpecifiedPageLogicalHeight && !pageLogicalHeight) + colInfo->clearForcedBreaks(); + + colInfo->setPaginationUnit(paginationUnit()); + } else if (isRenderFlowThread()) { + pageLogicalHeight = 1; // This is just a hack to always make sure we have a page logical height. + pageLogicalHeightChanged = toRenderFlowThread(this)->pageLogicalSizeChanged(); + } } void RenderBlockFlow::setComputedColumnCountAndWidth(int count, LayoutUnit width) { + if (!document().regionBasedColumnsEnabled()) + return RenderBlock::setComputedColumnCountAndWidth(count, width); + bool destroyColumns = !requiresColumns(count); if (destroyColumns) { if (multiColumnFlowThread()) @@ -3858,21 +3615,24 @@ void RenderBlockFlow::setComputedColumnCountAndWidth(int count, LayoutUnit width } } -void RenderBlockFlow::updateColumnProgressionFromStyle(RenderStyle& style) +void RenderBlockFlow::updateColumnProgressionFromStyle(RenderStyle* style) { + if (!document().regionBasedColumnsEnabled()) + return RenderBlock::updateColumnProgressionFromStyle(style); + if (!multiColumnFlowThread()) return; bool needsLayout = false; bool oldProgressionIsInline = multiColumnFlowThread()->progressionIsInline(); - bool newProgressionIsInline = style.hasInlineColumnAxis(); + bool newProgressionIsInline = style->hasInlineColumnAxis(); if (oldProgressionIsInline != newProgressionIsInline) { multiColumnFlowThread()->setProgressionIsInline(newProgressionIsInline); needsLayout = true; } bool oldProgressionIsReversed = multiColumnFlowThread()->progressionIsReversed(); - bool newProgressionIsReversed = style.columnProgression() == ReverseColumnProgression; + bool newProgressionIsReversed = style->columnProgression() == ReverseColumnProgression; if (oldProgressionIsReversed != newProgressionIsReversed) { multiColumnFlowThread()->setProgressionIsReversed(newProgressionIsReversed); needsLayout = true; @@ -3884,6 +3644,9 @@ void RenderBlockFlow::updateColumnProgressionFromStyle(RenderStyle& style) LayoutUnit RenderBlockFlow::computedColumnWidth() const { + if (!document().regionBasedColumnsEnabled()) + return RenderBlock::computedColumnWidth(); + if (multiColumnFlowThread()) return multiColumnFlowThread()->computedColumnWidth(); return contentLogicalWidth(); @@ -3891,6 +3654,9 @@ LayoutUnit RenderBlockFlow::computedColumnWidth() const unsigned RenderBlockFlow::computedColumnCount() const { + if (!document().regionBasedColumnsEnabled()) + return RenderBlock::computedColumnCount(); + if (multiColumnFlowThread()) return multiColumnFlowThread()->computedColumnCount(); @@ -3921,420 +3687,5 @@ bool RenderBlockFlow::isLeftLayoutOverflowAllowed() const return hasLeftOverflow; } -struct InlineMinMaxIterator { -/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to - inline min/max width calculations. Note the following about the way it walks: - (1) Positioned content is skipped (since it does not contribute to min/max width of a block) - (2) We do not drill into the children of floats or replaced elements, since you can't break - in the middle of such an element. - (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have - distinct borders/margin/padding that contribute to the min/max width. -*/ - const RenderBlockFlow& parent; - RenderObject* current; - bool endOfInline; - bool initial; - - InlineMinMaxIterator(const RenderBlockFlow& p) - : parent(p) - , current(nullptr) - , endOfInline(false) - , initial(true) - { } - - RenderObject* next(); -}; - -RenderObject* InlineMinMaxIterator::next() -{ - RenderObject* result = nullptr; - bool oldEndOfInline = endOfInline; - endOfInline = false; - do { - if (!oldEndOfInline && (current && !current->isFloating() && !current->isReplaced() && !current->isOutOfFlowPositioned())) - result = current->firstChildSlow(); - else if (initial) { - result = parent.firstChild(); - initial = false; - } - - if (!result) { - // We hit the end of our inline. (It was empty, e.g., <span></span>.) - if (!oldEndOfInline && current && current->isRenderInline()) { - result = current; - endOfInline = true; - break; - } - - while (current && current != &parent) { - result = current->nextSibling(); - if (result) - break; - current = current->parent(); - if (current && current != &parent && current->isRenderInline()) { - result = current; - endOfInline = true; - break; - } - } - } - - if (!result) - break; - - if (!result->isOutOfFlowPositioned() && (result->isTextOrLineBreak() || result->isFloating() || result->isReplaced() || result->isRenderInline())) - break; - - current = result; - result = nullptr; - } while (current || current == &parent); - // Update our position. - current = result; - return result; -} - -static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit) -{ - if (cssUnit.type() != Auto) - return (cssUnit.isFixed() ? LayoutUnit(cssUnit.value()) : childValue); - return 0; -} - -static LayoutUnit getBorderPaddingMargin(const RenderBoxModelObject& child, bool endOfInline) -{ - const RenderStyle& childStyle = child.style(); - if (endOfInline) { - return getBPMWidth(child.marginEnd(), childStyle.marginEnd()) + - getBPMWidth(child.paddingEnd(), childStyle.paddingEnd()) + - child.borderEnd(); - } - return getBPMWidth(child.marginStart(), childStyle.marginStart()) + - getBPMWidth(child.paddingStart(), childStyle.paddingStart()) + - child.borderStart(); -} - -static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, RenderObject* trailingSpaceChild) -{ - if (is<RenderText>(trailingSpaceChild)) { - // Collapse away the trailing space at the end of a block. - RenderText& renderText = downcast<RenderText>(*trailingSpaceChild); - const UChar space = ' '; - const FontCascade& font = renderText.style().fontCascade(); // FIXME: This ignores first-line. - float spaceWidth = font.width(RenderBlock::constructTextRun(&renderText, font, &space, 1, renderText.style())); - inlineMax -= spaceWidth + font.wordSpacing(); - if (inlineMin > inlineMax) - inlineMin = inlineMax; - } -} - -static inline LayoutUnit preferredWidth(LayoutUnit preferredWidth, float result) -{ - return std::max(preferredWidth, LayoutUnit::fromFloatCeil(result)); -} - -void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - float inlineMax = 0; - float inlineMin = 0; - - const RenderStyle& styleToUse = style(); - RenderBlock* containingBlock = this->containingBlock(); - LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit(); - - // If we are at the start of a line, we want to ignore all white-space. - // Also strip spaces if we previously had text that ended in a trailing space. - bool stripFrontSpaces = true; - RenderObject* trailingSpaceChild = nullptr; - - // Firefox and Opera will allow a table cell to grow to fit an image inside it under - // very specific cirucumstances (in order to match common WinIE renderings). - // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) - bool allowImagesToBreak = !document().inQuirksMode() || !isTableCell() || !styleToUse.logicalWidth().isIntrinsicOrAuto(); - - bool oldAutoWrap = styleToUse.autoWrap(); - - InlineMinMaxIterator childIterator(*this); - - // Only gets added to the max preffered width once. - bool addedTextIndent = false; - // Signals the text indent was more negative than the min preferred width - bool hasRemainingNegativeTextIndent = false; - - LayoutUnit textIndent = minimumValueForLength(styleToUse.textIndent(), cw); - RenderObject* prevFloat = 0; - bool isPrevChildInlineFlow = false; - bool shouldBreakLineAfterText = false; - while (RenderObject* child = childIterator.next()) { - bool autoWrap = child->isReplaced() ? child->parent()->style().autoWrap() : - child->style().autoWrap(); - bool isAnonymousInlineBlock = child->isAnonymousInlineBlock(); - - if (!child->isBR()) { - // Step One: determine whether or not we need to terminate our current line. - // Each discrete chunk can become the new min-width, if it is the widest chunk - // seen so far, and it can also become the max-width. - - // Children fall into three categories: - // (1) An inline flow object. These objects always have a min/max of 0, - // and are included in the iteration solely so that their margins can - // be added in. - // - // (2) An inline non-text non-flow object, e.g., an inline replaced element. - // These objects can always be on a line by themselves, so in this situation - // we need to break the current line, and then add in our own margins and min/max - // width on its own line, and then terminate the line. - // - // (3) A text object. Text runs can have breakable characters at the start, - // the middle or the end. They may also lose whitespace off the front if - // we're already ignoring whitespace. In order to compute accurate min-width - // information, we need three pieces of information. - // (a) the min-width of the first non-breakable run. Should be 0 if the text string - // starts with whitespace. - // (b) the min-width of the last non-breakable run. Should be 0 if the text string - // ends with whitespace. - // (c) the min/max width of the string (trimmed for whitespace). - // - // If the text string starts with whitespace, then we need to terminate our current line - // (unless we're already in a whitespace stripping mode. - // - // If the text string has a breakable character in the middle, but didn't start - // with whitespace, then we add the width of the first non-breakable run and - // then end the current line. We then need to use the intermediate min/max width - // values (if any of them are larger than our current min/max). We then look at - // the width of the last non-breakable run and use that to start a new line - // (unless we end in whitespace). - const RenderStyle& childStyle = child->style(); - float childMin = 0; - float childMax = 0; - - if (!child->isText()) { - if (child->isLineBreakOpportunity()) { - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - continue; - } - // Case (1) and (2). Inline replaced and inline flow elements. - if (is<RenderInline>(*child)) { - // Add in padding/border/margin from the appropriate side of - // the element. - float bpm = getBorderPaddingMargin(downcast<RenderInline>(*child), childIterator.endOfInline); - childMin += bpm; - childMax += bpm; - - inlineMin += childMin; - inlineMax += childMax; - - child->setPreferredLogicalWidthsDirty(false); - } else { - // Inline replaced elts add in their margins to their min/max values. - LayoutUnit margins = 0; - Length startMargin = childStyle.marginStart(); - Length endMargin = childStyle.marginEnd(); - if (startMargin.isFixed()) - margins += LayoutUnit::fromFloatCeil(startMargin.value()); - if (endMargin.isFixed()) - margins += LayoutUnit::fromFloatCeil(endMargin.value()); - childMin += margins.ceilToFloat(); - childMax += margins.ceilToFloat(); - } - } - - if (!is<RenderInline>(*child) && !is<RenderText>(*child)) { - // Case (2). Inline replaced elements and floats. - // Terminate the current line as far as minwidth is concerned. - childMin += child->minPreferredLogicalWidth().ceilToFloat(); - childMax += child->maxPreferredLogicalWidth().ceilToFloat(); - - bool clearPreviousFloat; - if (child->isFloating()) { - clearPreviousFloat = (prevFloat - && ((prevFloat->style().floating() == LeftFloat && (childStyle.clear() & CLEFT)) - || (prevFloat->style().floating() == RightFloat && (childStyle.clear() & CRIGHT)))); - prevFloat = child; - } else - clearPreviousFloat = false; - - bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; - if (((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) || isAnonymousInlineBlock) { - if (child->isAnonymousInlineBlock() && styleToUse.collapseWhiteSpace()) - stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - - // If we're supposed to clear the previous float, then terminate maxwidth as well. - if (clearPreviousFloat || isAnonymousInlineBlock) { - maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax); - inlineMax = 0; - } - - // Add in text-indent. This is added in only once. - if (!addedTextIndent && !child->isFloating() && !isAnonymousInlineBlock) { - LayoutUnit ceiledIndent = textIndent.ceilToFloat(); - childMin += ceiledIndent; - childMax += ceiledIndent; - - if (childMin < 0) - textIndent = LayoutUnit::fromFloatCeil(childMin); - else - addedTextIndent = true; - } - - // Add our width to the max. - inlineMax += std::max<float>(0, childMax); - - if ((!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) && !isAnonymousInlineBlock) { - if (child->isFloating()) - minLogicalWidth = preferredWidth(minLogicalWidth, childMin); - else - inlineMin += childMin; - } else { - // Now check our line. - minLogicalWidth = preferredWidth(minLogicalWidth, childMin); - - // Now start a new line. - inlineMin = 0; - - if (child->isAnonymousInlineBlock()) { - // Terminate max width as well. - maxLogicalWidth = preferredWidth(maxLogicalWidth, childMax); - inlineMax = 0; - } - } - - if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) { - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - - // We are no longer stripping whitespace at the start of a line. - if (!child->isFloating()) { - stripFrontSpaces = false; - trailingSpaceChild = nullptr; - } - } else if (is<RenderText>(*child)) { - // Case (3). Text. - RenderText& renderText = downcast<RenderText>(*child); - - if (renderText.style().hasTextCombine() && renderText.isCombineText()) - downcast<RenderCombineText>(renderText).combineText(); - - // Determine if we have a breakable character. Pass in - // whether or not we should ignore any spaces at the front - // of the string. If those are going to be stripped out, - // then they shouldn't be considered in the breakable char - // check. - bool hasBreakableChar, hasBreak; - float beginMin, endMin; - bool beginWS, endWS; - float beginMax, endMax; - renderText.trimmedPrefWidths(inlineMax, beginMin, beginWS, endMin, endWS, - hasBreakableChar, hasBreak, beginMax, endMax, - childMin, childMax, stripFrontSpaces); - - // This text object will not be rendered, but it may still provide a breaking opportunity. - if (!hasBreak && !childMax) { - if (autoWrap && (beginWS || endWS)) { - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - continue; - } - - if (stripFrontSpaces) - trailingSpaceChild = child; - else - trailingSpaceChild = 0; - - // Add in text-indent. This is added in only once. - float ti = 0; - if (!addedTextIndent || hasRemainingNegativeTextIndent) { - ti = textIndent.ceilToFloat(); - childMin += ti; - beginMin += ti; - - // It the text indent negative and larger than the child minimum, we re-use the remainder - // in future minimum calculations, but using the negative value again on the maximum - // will lead to under-counting the max pref width. - if (!addedTextIndent) { - childMax += ti; - beginMax += ti; - addedTextIndent = true; - } - - if (childMin < 0) { - textIndent = childMin; - hasRemainingNegativeTextIndent = true; - } - } - - // If we have no breakable characters at all, - // then this is the easy case. We add ourselves to the current - // min and max and continue. - if (!hasBreakableChar) - inlineMin += childMin; - else { - // We have a breakable character. Now we need to know if - // we start and end with whitespace. - if (beginWS) { - // End the current line. - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - } else { - inlineMin += beginMin; - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - childMin -= ti; - } - - inlineMin = childMin; - - if (endWS) { - // We end in whitespace, which means we can end our current line. - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - shouldBreakLineAfterText = false; - } else { - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - inlineMin = endMin; - shouldBreakLineAfterText = true; - } - } - - if (hasBreak) { - inlineMax += beginMax; - maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax); - maxLogicalWidth = preferredWidth(maxLogicalWidth, childMax); - inlineMax = endMax; - addedTextIndent = true; - } else - inlineMax += std::max<float>(0, childMax); - } - - // Ignore spaces after a list marker and also after an anonymous inline block. - if (child->isListMarker() || isAnonymousInlineBlock) - stripFrontSpaces = true; - } else { - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax); - inlineMin = inlineMax = 0; - stripFrontSpaces = true; - trailingSpaceChild = 0; - addedTextIndent = true; - } - - if (!child->isText() && child->isRenderInline()) - isPrevChildInlineFlow = true; - else - isPrevChildInlineFlow = false; - - oldAutoWrap = autoWrap; - } - - if (styleToUse.collapseWhiteSpace()) - stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); - - minLogicalWidth = preferredWidth(minLogicalWidth, inlineMin); - maxLogicalWidth = preferredWidth(maxLogicalWidth, inlineMax); -} - } // namespace WebCore |