From 6882a04fb36642862b11efe514251d32070c3d65 Mon Sep 17 00:00:00 2001 From: Konstantin Tokarev Date: Thu, 25 Aug 2016 19:20:41 +0300 Subject: Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443) Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f Reviewed-by: Konstantin Tokarev --- Source/WebCore/rendering/RenderBlock.cpp | 9048 ++++++++---------------------- 1 file changed, 2282 insertions(+), 6766 deletions(-) (limited to 'Source/WebCore/rendering/RenderBlock.cpp') diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp index c1b3fe154..3ffdef97d 100644 --- a/Source/WebCore/rendering/RenderBlock.cpp +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -25,7 +25,6 @@ #include "RenderBlock.h" #include "AXObjectCache.h" -#include "ColumnInfo.h" #include "Document.h" #include "Editor.h" #include "Element.h" @@ -38,6 +37,7 @@ #include "HTMLNames.h" #include "HitTestLocation.h" #include "HitTestResult.h" +#include "InlineElementBox.h" #include "InlineIterator.h" #include "InlineTextBox.h" #include "LayoutRepainter.h" @@ -45,32 +45,41 @@ #include "OverflowEvent.h" #include "Page.h" #include "PaintInfo.h" +#include "RenderBlockFlow.h" #include "RenderBoxRegionInfo.h" +#include "RenderButton.h" #include "RenderCombineText.h" #include "RenderDeprecatedFlexibleBox.h" #include "RenderFlexibleBox.h" #include "RenderInline.h" +#include "RenderIterator.h" #include "RenderLayer.h" -#include "RenderMarquee.h" +#include "RenderListMarker.h" +#include "RenderMenuList.h" +#include "RenderNamedFlowFragment.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" +#include "RenderSVGResourceClipper.h" #include "RenderTableCell.h" #include "RenderTextFragment.h" #include "RenderTheme.h" +#include "RenderTreePosition.h" #include "RenderView.h" #include "SVGTextRunRenderingContext.h" #include "Settings.h" #include "ShadowRoot.h" +#include "TextBreakIterator.h" #include "TransformState.h" + +#include +#include #include #include #if ENABLE(CSS_SHAPES) -#include "ShapeInsideInfo.h" #include "ShapeOutsideInfo.h" #endif -using namespace std; using namespace WTF; using namespace Unicode; @@ -79,46 +88,57 @@ namespace WebCore { using namespace HTMLNames; struct SameSizeAsRenderBlock : public RenderBox { - void* pointers[2]; - RenderObjectChildList children; - RenderLineBoxList lineBoxes; - uint32_t bitfields; }; COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small); -struct SameSizeAsFloatingObject { - void* pointers[2]; - LayoutRect rect; - int paginationStrut; - uint32_t bitfields : 8; -}; +static TrackedDescendantsMap* gPositionedDescendantsMap; +static TrackedDescendantsMap* gPercentHeightDescendantsMap; + +static TrackedContainerMap* gPositionedContainerMap; +static TrackedContainerMap* gPercentHeightContainerMap; + +typedef HashMap>> ContinuationOutlineTableMap; -COMPILE_ASSERT(sizeof(RenderBlock::MarginValues) == sizeof(LayoutUnit[4]), MarginValues_should_stay_small); +struct UpdateScrollInfoAfterLayoutTransaction { + UpdateScrollInfoAfterLayoutTransaction(const RenderView& view) + : nestedCount(0) + , view(&view) + { + } -struct SameSizeAsMarginInfo { - uint32_t bitfields : 16; - LayoutUnit margins[2]; + int nestedCount; + const RenderView* view; + HashSet blocks; }; -typedef WTF::HashMap > ColumnInfoMap; -static ColumnInfoMap* gColumnInfoMap = 0; +typedef Vector DelayedUpdateScrollInfoStack; +static std::unique_ptr& updateScrollInfoAfterLayoutTransactionStack() +{ + static NeverDestroyed> delayedUpdatedScrollInfoStack; + return delayedUpdatedScrollInfoStack; +} -static TrackedDescendantsMap* gPositionedDescendantsMap = 0; -static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; +// Allocated only when some of these fields have non-default values -static TrackedContainerMap* gPositionedContainerMap = 0; -static TrackedContainerMap* gPercentHeightContainerMap = 0; - -typedef WTF::HashMap > > ContinuationOutlineTableMap; +struct RenderBlockRareData { + WTF_MAKE_NONCOPYABLE(RenderBlockRareData); WTF_MAKE_FAST_ALLOCATED; +public: + RenderBlockRareData() + : m_paginationStrut(0) + , m_pageLogicalOffset(0) + , m_flowThreadContainingBlock(Nullopt) + { + } -typedef WTF::HashSet DelayedUpdateScrollInfoSet; -static int gDelayUpdateScrollInfo = 0; -static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0; + LayoutUnit m_paginationStrut; + LayoutUnit m_pageLogicalOffset; -static bool gColumnFlowSplitEnabled = true; + Optional m_flowThreadContainingBlock; +}; -bool RenderBlock::s_canPropagateFloatIntoSibling = false; +typedef HashMap> RenderBlockRareDataMap; +static RenderBlockRareDataMap* gRareDataMap = 0; // This class helps dispatching the 'overflow' event on layout change. overflow can be set on RenderBoxes, yet the existing code // only works on RenderBlocks. If this change, this class should be shared with other RenderBoxes. @@ -130,7 +150,7 @@ public: , m_hadHorizontalLayoutOverflow(false) , m_hadVerticalLayoutOverflow(false) { - m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER); + m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document().hasListenerType(Document::OVERFLOWCHANGED_LISTENER); if (m_shouldDispatchEvent) { m_hadHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow(); m_hadVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow(); @@ -147,10 +167,12 @@ public: bool horizontalLayoutOverflowChanged = hasHorizontalLayoutOverflow != m_hadHorizontalLayoutOverflow; bool verticalLayoutOverflowChanged = hasVerticalLayoutOverflow != m_hadVerticalLayoutOverflow; - if (horizontalLayoutOverflowChanged || verticalLayoutOverflowChanged) { - if (FrameView* frameView = m_block->document()->view()) - frameView->scheduleEvent(OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow), m_block->node()); - } + if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged) + return; + + Ref overflowEvent = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); + overflowEvent->setTarget(m_block->element()); + m_block->document().enqueueOverflowEvent(WTFMove(overflowEvent)); } private: @@ -160,58 +182,19 @@ private: bool m_hadVerticalLayoutOverflow; }; -// Our MarginInfo state used when laying out block children. -RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, LayoutUnit beforeBorderPadding, LayoutUnit afterBorderPadding) - : m_atBeforeSideOfBlock(true) - , m_atAfterSideOfBlock(false) - , m_hasMarginBeforeQuirk(false) - , m_hasMarginAfterQuirk(false) - , m_determinedMarginBeforeQuirk(false) - , m_discardMargin(false) -{ - RenderStyle* blockStyle = block->style(); - ASSERT(block->isRenderView() || block->parent()); - 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; - - // If any height other than auto is specified in CSS, then we don't collapse our bottom - // margins with our children's margins. To do otherwise would be to risk odd visual - // effects when the children overflow out of the parent block and yet still collapse - // with it. We also don't collapse if we have any bottom border/padding. - m_canCollapseMarginAfterWithChildren = m_canCollapseWithChildren && (afterBorderPadding == 0) && - (blockStyle->logicalHeight().isAuto() && !blockStyle->logicalHeight().value()) && blockStyle->marginAfterCollapse() != MSEPARATE; - - m_quirkContainer = block->isTableCell() || block->isBody(); - - m_discardMargin = m_canCollapseMarginBeforeWithChildren && block->mustDiscardMarginBefore(); - - m_positiveMargin = (m_canCollapseMarginBeforeWithChildren && !block->mustDiscardMarginBefore()) ? block->maxPositiveMarginBefore() : LayoutUnit(); - m_negativeMargin = (m_canCollapseMarginBeforeWithChildren && !block->mustDiscardMarginBefore()) ? block->maxNegativeMarginBefore() : LayoutUnit(); +RenderBlock::RenderBlock(Element& element, Ref&& style, BaseTypeFlags baseTypeFlags) + : RenderBox(element, WTFMove(style), baseTypeFlags | RenderBlockFlag) +{ } -// ------------------------------------------------------------------------------------------------------- - -RenderBlock::RenderBlock(ContainerNode* node) - : RenderBox(node) - , m_lineHeight(-1) - , m_hasMarginBeforeQuirk(false) - , m_hasMarginAfterQuirk(false) - , m_beingDestroyed(false) - , m_hasMarkupTruncation(false) - , m_hasBorderOrPaddingLogicalWidthChanged(false) +RenderBlock::RenderBlock(Document& document, Ref&& style, BaseTypeFlags baseTypeFlags) + : RenderBox(document, WTFMove(style), baseTypeFlags | RenderBlockFlag) { - setChildrenInline(true); - COMPILE_ASSERT(sizeof(RenderBlock::FloatingObject) == sizeof(SameSizeAsFloatingObject), FloatingObject_should_stay_small); - COMPILE_ASSERT(sizeof(RenderBlock::MarginInfo) == sizeof(SameSizeAsMarginInfo), MarginInfo_should_stay_small); } static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap) { - if (OwnPtr descendantSet = descendantMap->take(block)) { + if (std::unique_ptr descendantSet = descendantMap->take(block)) { TrackedRendererListHashSet::iterator end = descendantSet->end(); for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { TrackedContainerMap::iterator it = containerMap->find(*descendant); @@ -229,113 +212,70 @@ static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, Tracke RenderBlock::~RenderBlock() { - if (m_floatingObjects) - deleteAllValues(m_floatingObjects->set()); - - if (hasColumns()) - gColumnInfoMap->take(this); + removeFromUpdateScrollInfoAfterLayoutTransaction(); + if (gRareDataMap) + gRareDataMap->remove(this); if (gPercentHeightDescendantsMap) removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); if (gPositionedDescendantsMap) removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap); } -RenderBlock* RenderBlock::createAnonymous(Document* document) -{ - RenderBlock* renderer = new (document->renderArena()) RenderBlock(0); - renderer->setDocumentForAnonymous(document); - return renderer; -} - void RenderBlock::willBeDestroyed() { - // Mark as being destroyed to avoid trouble with merges in removeChild(). - m_beingDestroyed = true; - - 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. - children()->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 (firstLineBox()) { - // 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()) - 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 (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) { - while (InlineBox* childBox = box->firstChild()) - childBox->remove(); - } - } - } else if (parent()) - parent()->dirtyLinesFromChangedChild(this); + if (parent()) + parent()->dirtyLinesFromChangedChild(*this); } - m_lineBoxes.deleteLineBoxes(renderArena()); - - if (lineGridBox()) - lineGridBox()->destroy(renderArena()); - - if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0)) - gDelayedUpdateScrollInfoSet->remove(this); - RenderBox::willBeDestroyed(); } -void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) +bool RenderBlock::hasRareData() const +{ + return gRareDataMap ? gRareDataMap->contains(this) : false; +} + +void RenderBlock::removePositionedObjectsIfNeeded(const RenderStyle& oldStyle, const RenderStyle& newStyle) { - RenderStyle* oldStyle = style(); - s_canPropagateFloatIntoSibling = oldStyle ? !isFloatingOrOutOfFlowPositioned() && !avoidsFloats() : false; + bool hadTransform = oldStyle.hasTransformRelatedProperty(); + bool willHaveTransform = newStyle.hasTransformRelatedProperty(); + if (oldStyle.position() == newStyle.position() && hadTransform == willHaveTransform) + return; - setReplaced(newStyle->isDisplayInlineType()); + // We are no longer a containing block. + if (newStyle.position() == StaticPosition && !willHaveTransform) { + // Clear our positioned objects list. Our absolutely positioned descendants will be + // inserted into our containing block's positioned objects list during layout. + removePositionedObjects(nullptr, NewContainingBlock); + return; + } - if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle->position()) { - if (newStyle->position() == StaticPosition) - // Clear our positioned objects list. Our absolutely positioned descendants will be - // inserted into our containing block's positioned objects list during layout. - removePositionedObjects(0, NewContainingBlock); - else if (oldStyle->position() == StaticPosition) { - // Remove our absolutely positioned descendants from their current containing block. - // They will be inserted into our positioned objects list during layout. - RenderObject* cb = parent(); - while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) { - if (cb->style()->position() == RelativePosition && cb->isInline() && !cb->isReplaced()) { - cb = cb->containingBlock(); - break; - } - cb = cb->parent(); + // We are a new containing block. + if (oldStyle.position() == StaticPosition && !hadTransform) { + // Remove our absolutely positioned descendants from their current containing block. + // They will be inserted into our positioned objects list during layout. + auto* containingBlock = parent(); + while (containingBlock && !is(*containingBlock) + && (containingBlock->style().position() == StaticPosition || (containingBlock->isInline() && !containingBlock->isReplaced()))) { + if (containingBlock->style().position() == RelativePosition && containingBlock->isInline() && !containingBlock->isReplaced()) { + containingBlock = containingBlock->containingBlock(); + break; } - - if (cb->isRenderBlock()) - toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock); + containingBlock = containingBlock->parent(); } - - if (containsFloats() && !isFloating() && !isOutOfFlowPositioned() && newStyle->hasOutOfFlowPosition()) - markAllDescendantsWithFloatsForLayout(); + if (containingBlock && is(*containingBlock)) + downcast(*containingBlock).removePositionedObjects(this, NewContainingBlock); } +} +void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) +{ + const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr; + setReplaced(newStyle.isDisplayInlineType()); + if (oldStyle) + removePositionedObjectsIfNeeded(*oldStyle, newStyle); RenderBox::styleWillChange(diff, newStyle); } @@ -355,13 +295,19 @@ static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, cons void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { + RenderStyle& newStyle = style(); + + bool hadTransform = hasTransform(); + bool flowThreadContainingBlockInvalidated = false; + if (oldStyle && oldStyle->position() != newStyle.position()) { + invalidateFlowThreadContainingBlockIncludingDescendants(); + flowThreadContainingBlockInvalidated = true; + } + RenderBox::styleDidChange(diff, oldStyle); - - RenderStyle* newStyle = style(); - -#if ENABLE(CSS_SHAPES) - updateShapeInsideInfoAfterStyleChange(newStyle->resolvedShapeInside(), oldStyle ? oldStyle->resolvedShapeInside() : 0); -#endif + + if (hadTransform != hasTransform() && !flowThreadContainingBlockInvalidated) + invalidateFlowThreadContainingBlockIncludingDescendants(); if (!isAnonymousBlock()) { // Ensure that all of our continuation blocks pick up the new style. @@ -373,42 +319,11 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty } } - propagateStyleToAnonymousChildren(true); - m_lineHeight = -1; - - // After our style changed, if we lose our ability to propagate floats into next sibling - // blocks, then we need to find the top most parent containing that overhanging float and - // then mark its descendants with floats for layout and clear all floats from its next - // sibling blocks that exist in our floating objects list. See bug 56299 and 62875. - bool canPropagateFloatIntoSibling = !isFloatingOrOutOfFlowPositioned() && !avoidsFloats(); - if (diff == StyleDifferenceLayout && s_canPropagateFloatIntoSibling && !canPropagateFloatIntoSibling && hasOverhangingFloats()) { - RenderBlock* parentBlock = this; - const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - FloatingObjectSetIterator end = floatingObjectSet.end(); - - for (RenderObject* curr = parent(); curr && !curr->isRenderView(); curr = curr->parent()) { - if (curr->isRenderBlock()) { - RenderBlock* currBlock = toRenderBlock(curr); - - if (currBlock->hasOverhangingFloats()) { - for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) { - RenderBox* renderer = (*it)->renderer(); - if (currBlock->hasOverhangingFloat(renderer)) { - parentBlock = currBlock; - break; - } - } - } - } - } - - parentBlock->markAllDescendantsWithFloatsForLayout(); - parentBlock->markSiblingsWithFloatsForLayout(); - } - + propagateStyleToAnonymousChildren(PropagateToBlockChildrenOnly); + // It's possible for our border/padding to change, but for the overall logical width of the block to // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true. - m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff == StyleDifferenceLayout && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, newStyle); + setHasBorderOrPaddingLogicalWidthChanged(oldStyle && diff == StyleDifferenceLayout && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, &newStyle)); } RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) @@ -416,19 +331,17 @@ RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) if (beforeChild && beforeChild->parent() == this) return this; - RenderBlock* curr = toRenderBlock(continuation()); RenderBlock* nextToLast = this; RenderBlock* last = this; - while (curr) { - if (beforeChild && beforeChild->parent() == curr) { - if (curr->firstChild() == beforeChild) + for (auto* current = downcast(continuation()); current; current = downcast(current->continuation())) { + if (beforeChild && beforeChild->parent() == current) { + if (current->firstChild() == beforeChild) return last; - return curr; + return current; } nextToLast = last; - last = curr; - curr = toRenderBlock(curr->continuation()); + last = current; } if (!beforeChild && !last->firstChild()) @@ -439,14 +352,14 @@ RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) { RenderBlock* flow = continuationBefore(beforeChild); - ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock()); - RenderBoxModelObject* beforeChildParent = 0; + ASSERT(!beforeChild || is(*beforeChild->parent())); + RenderBoxModelObject* beforeChildParent = nullptr; if (beforeChild) - beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); + beforeChildParent = downcast(beforeChild->parent()); else { - RenderBoxModelObject* cont = flow->continuation(); - if (cont) - beforeChildParent = cont; + RenderBoxModelObject* continuation = flow->continuation(); + if (continuation) + beforeChildParent = continuation; else beforeChildParent = flow; } @@ -456,11 +369,9 @@ void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* b return; } - // A continuation always consists of two potential candidates: a block or an anonymous - // column span box holding column span children. - bool childIsNormal = newChild->isInline() || !newChild->style()->columnSpan(); - bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style()->columnSpan(); - bool flowIsNormal = flow->isInline() || !flow->style()->columnSpan(); + bool childIsNormal = newChild->isInline() || !newChild->style().columnSpan(); + bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style().columnSpan(); + bool flowIsNormal = flow->isInline() || !flow->style().columnSpan(); if (flow == beforeChildParent) { flow->addChildIgnoringContinuation(newChild, beforeChild); @@ -480,107 +391,16 @@ void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* b beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); } - -void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) -{ - ASSERT(!continuation()); // We don't yet support column spans that aren't immediate children of the multi-column block. - - // The goal is to locate a suitable box in which to place our child. - RenderBlock* beforeChildParent = 0; - if (beforeChild) { - RenderObject* curr = beforeChild; - while (curr && curr->parent() != this) - curr = curr->parent(); - beforeChildParent = toRenderBlock(curr); - ASSERT(beforeChildParent); - ASSERT(beforeChildParent->isAnonymousColumnsBlock() || beforeChildParent->isAnonymousColumnSpanBlock()); - } else - beforeChildParent = toRenderBlock(lastChild()); - - // If the new child is floating or positioned it can just go in that block. - if (newChild->isFloatingOrOutOfFlowPositioned()) { - beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); - return; - } - - // See if the child can be placed in the box. - bool newChildHasColumnSpan = newChild->style()->columnSpan() && !newChild->isInline(); - bool beforeChildParentHoldsColumnSpans = beforeChildParent->isAnonymousColumnSpanBlock(); - - if (newChildHasColumnSpan == beforeChildParentHoldsColumnSpans) { - beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); - return; - } - - if (!beforeChild) { - // Create a new block of the correct type. - RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); - children()->appendChildNode(this, newBox); - newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); - return; - } - - RenderObject* immediateChild = beforeChild; - bool isPreviousBlockViable = true; - while (immediateChild->parent() != this) { - if (isPreviousBlockViable) - isPreviousBlockViable = !immediateChild->previousSibling(); - immediateChild = immediateChild->parent(); - } - if (isPreviousBlockViable && immediateChild->previousSibling()) { - toRenderBlock(immediateChild->previousSibling())->addChildIgnoringAnonymousColumnBlocks(newChild, 0); // Treat like an append. - return; - } - - // Split our anonymous blocks. - RenderObject* newBeforeChild = splitAnonymousBoxesAroundChild(beforeChild); - - - // Create a new anonymous box of the appropriate type. - RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); - children()->insertChildNode(this, newBox, newBeforeChild); - newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); - return; -} - -RenderBlock* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock) -{ - RenderBlock* firstChildIgnoringAnonymousWrappers = 0; - for (RenderObject* curr = this; curr; curr = curr->parent()) { - if (!curr->isRenderBlock() || curr->isFloatingOrOutOfFlowPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip() - || curr->isInlineBlockOrInlineTable()) - return 0; - - // FIXME: Tables, RenderButtons, and RenderListItems all do special management - // of their children that breaks when the flow is split through them. Disabling - // multi-column for them to avoid this problem. - if (curr->isTable() || curr->isRenderButton() || curr->isListItem()) - return 0; - - RenderBlock* currBlock = toRenderBlock(curr); - if (!currBlock->createsAnonymousWrapper()) - firstChildIgnoringAnonymousWrappers = currBlock; - - if (currBlock->style()->specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock())) - return firstChildIgnoringAnonymousWrappers; - - if (currBlock->isAnonymousColumnSpanBlock()) - return 0; - } - return 0; -} - -RenderBlock* RenderBlock::clone() const +RenderPtr RenderBlock::clone() const { - RenderBlock* cloneBlock; + RenderPtr cloneBlock; if (isAnonymousBlock()) { - cloneBlock = createAnonymousBlock(); + cloneBlock = RenderPtr(createAnonymousBlock()); cloneBlock->setChildrenInline(childrenInline()); - } - else { - RenderObject* cloneRenderer = toElement(node())->createRenderer(renderArena(), style()); - cloneBlock = toRenderBlock(cloneRenderer); - cloneBlock->setStyle(style()); + } else { + RenderTreePosition insertionPosition(*parent()); + cloneBlock = static_pointer_cast(element()->createElementRenderer(style(), insertionPosition)); + cloneBlock->initializeStyle(); // This takes care of setting the right value of childrenInline in case // generated content is added to cloneBlock and 'this' does not have @@ -591,220 +411,18 @@ RenderBlock* RenderBlock::clone() const return cloneBlock; } -void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, - RenderBlock* middleBlock, - RenderObject* beforeChild, RenderBoxModelObject* oldCont) -{ - // Create a clone of this inline. - RenderBlock* cloneBlock = clone(); - if (!isAnonymousBlock()) - cloneBlock->setContinuation(oldCont); - - if (!beforeChild && isAfterContent(lastChild())) - beforeChild = lastChild(); - - // If we are moving inline children from |this| to cloneBlock, then we need - // to clear our line box tree. - if (beforeChild && childrenInline()) - deleteLineBoxTree(); - - // Now take all of the children from beforeChild to the end and remove - // them from |this| and place them in the clone. - moveChildrenTo(cloneBlock, beforeChild, 0, true); - - // Hook |clone| up as the continuation of the middle block. - if (!cloneBlock->isAnonymousBlock()) - middleBlock->setContinuation(cloneBlock); - - // We have been reparented and are now under the fromBlock. We need - // to walk up our block parent chain until we hit the containing anonymous columns block. - // Once we hit the anonymous columns block we're done. - RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); - RenderBoxModelObject* currChild = this; - RenderObject* currChildNextSibling = currChild->nextSibling(); - - while (curr && curr != fromBlock) { - ASSERT_WITH_SECURITY_IMPLICATION(curr->isRenderBlock()); - - RenderBlock* blockCurr = toRenderBlock(curr); - - // Create a new clone. - RenderBlock* cloneChild = cloneBlock; - cloneBlock = blockCurr->clone(); - - // Insert our child clone as the first child. - cloneBlock->addChildIgnoringContinuation(cloneChild, 0); - - // Hook the clone up as a continuation of |curr|. Note we do encounter - // anonymous blocks possibly as we walk up the block chain. When we split an - // anonymous block, there's no need to do any continuation hookup, since we haven't - // actually split a real element. - if (!blockCurr->isAnonymousBlock()) { - oldCont = blockCurr->continuation(); - blockCurr->setContinuation(cloneBlock); - cloneBlock->setContinuation(oldCont); - } - - // Now we need to take all of the children starting from the first child - // *after* currChild and append them all to the clone. - blockCurr->moveChildrenTo(cloneBlock, currChildNextSibling, 0, true); - - // Keep walking up the chain. - currChild = curr; - currChildNextSibling = currChild->nextSibling(); - curr = toRenderBoxModelObject(curr->parent()); - } - - // Now we are at the columns block level. We need to put the clone into the toBlock. - toBlock->children()->appendChildNode(toBlock, cloneBlock); - - // Now take all the children after currChild and remove them from the fromBlock - // and put them in the toBlock. - fromBlock->moveChildrenTo(toBlock, currChildNextSibling, 0, true); -} - -void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, - RenderObject* newChild, RenderBoxModelObject* oldCont) -{ - RenderBlock* pre = 0; - RenderBlock* block = containingColumnsBlock(); - - // Delete our line boxes before we do the inline split into continuations. - block->deleteLineBoxTree(); - - bool madeNewBeforeBlock = false; - if (block->isAnonymousColumnsBlock()) { - // We can reuse this block and make it the preBlock of the next continuation. - pre = block; - pre->removePositionedObjects(0); - pre->removeFloatingObjects(); - block = toRenderBlock(block->parent()); - } else { - // No anonymous block available for use. Make one. - pre = block->createAnonymousColumnsBlock(); - pre->setChildrenInline(false); - madeNewBeforeBlock = true; - } - - RenderBlock* post = block->createAnonymousColumnsBlock(); - post->setChildrenInline(false); - - RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); - if (madeNewBeforeBlock) - block->children()->insertChildNode(block, pre, boxFirst); - block->children()->insertChildNode(block, newBlockBox, boxFirst); - block->children()->insertChildNode(block, post, boxFirst); - block->setChildrenInline(false); - - if (madeNewBeforeBlock) - block->moveChildrenTo(pre, boxFirst, 0, true); - - splitBlocks(pre, post, newBlockBox, beforeChild, oldCont); - - // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting - // time in makeChildrenNonInline by just setting this explicitly up front. - newBlockBox->setChildrenInline(false); - - // We delayed adding the newChild until now so that the |newBlockBox| would be fully - // connected, thus allowing newChild access to a renderArena should it need - // to wrap itself in additional boxes (e.g., table construction). - newBlockBox->addChild(newChild); - - // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) - // get deleted properly. Because objects moves from the pre block into the post block, we want to - // make new line boxes instead of leaving the old line boxes around. - pre->setNeedsLayoutAndPrefWidthsRecalc(); - block->setNeedsLayoutAndPrefWidthsRecalc(); - post->setNeedsLayoutAndPrefWidthsRecalc(); -} - -void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild) +void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) { - RenderBlock* pre = 0; - RenderBlock* post = 0; - RenderBlock* block = this; // Eventually block will not just be |this|, but will also be a block nested inside |this|. Assign to a variable - // so that we don't have to patch all of the rest of the code later on. - - // Delete the block's line boxes before we do the split. - block->deleteLineBoxTree(); - - if (beforeChild && beforeChild->parent() != this) - beforeChild = splitAnonymousBoxesAroundChild(beforeChild); - - if (beforeChild != firstChild()) { - pre = block->createAnonymousColumnsBlock(); - pre->setChildrenInline(block->childrenInline()); - } - - if (beforeChild) { - post = block->createAnonymousColumnsBlock(); - post->setChildrenInline(block->childrenInline()); - } - - RenderObject* boxFirst = block->firstChild(); - if (pre) - block->children()->insertChildNode(block, pre, boxFirst); - block->children()->insertChildNode(block, newBlockBox, boxFirst); - if (post) - block->children()->insertChildNode(block, post, boxFirst); - block->setChildrenInline(false); - - // The pre/post blocks always have layers, so we know to always do a full insert/remove (so we pass true as the last argument). - block->moveChildrenTo(pre, boxFirst, beforeChild, true); - block->moveChildrenTo(post, beforeChild, 0, true); - - // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting - // time in makeChildrenNonInline by just setting this explicitly up front. - newBlockBox->setChildrenInline(false); - - // We delayed adding the newChild until now so that the |newBlockBox| would be fully - // connected, thus allowing newChild access to a renderArena should it need - // to wrap itself in additional boxes (e.g., table construction). - newBlockBox->addChild(newChild); - - // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) - // get deleted properly. Because objects moved from the pre block into the post block, we want to - // make new line boxes instead of leaving the old line boxes around. - if (pre) - pre->setNeedsLayoutAndPrefWidthsRecalc(); - block->setNeedsLayoutAndPrefWidthsRecalc(); - if (post) - post->setNeedsLayoutAndPrefWidthsRecalc(); -} - -RenderBlock* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) -{ - // FIXME: This function is the gateway for the addition of column-span support. It will - // be added to in three stages: - // (1) Immediate children of a multi-column block can span. - // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span. - // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we - // cross the streams and have to cope with both types of continuations mixed together). - // This function currently supports (1) and (2). - RenderBlock* columnsBlockAncestor = 0; - if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isBeforeOrAfterContent() - && !newChild->isFloatingOrOutOfFlowPositioned() && !newChild->isInline() && !isAnonymousColumnSpanBlock()) { - columnsBlockAncestor = containingColumnsBlock(false); - if (columnsBlockAncestor) { - // Make sure that none of the parent ancestors have a continuation. - // If yes, we do not want split the block into continuations. - RenderObject* curr = this; - while (curr && curr != columnsBlockAncestor) { - if (curr->isRenderBlock() && toRenderBlock(curr)->continuation()) { - columnsBlockAncestor = 0; - break; - } - curr = curr->parent(); - } - } - } - return columnsBlockAncestor; + if (continuation() && !isAnonymousBlock()) + addChildToContinuation(newChild, beforeChild); + else + addChildIgnoringContinuation(newChild, beforeChild); } -void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) +void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) { if (beforeChild && beforeChild->parent() != this) { - RenderObject* beforeChildContainer = beforeChild->parent(); + RenderElement* beforeChildContainer = beforeChild->parent(); while (beforeChildContainer->parent() != this) beforeChildContainer = beforeChildContainer->parent(); ASSERT(beforeChildContainer); @@ -812,7 +430,7 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, if (beforeChildContainer->isAnonymous()) { // If the requested beforeChild is not one of our children, then this is because // there is an anonymous container within this object that contains the beforeChild. - RenderObject* beforeChildAnonymousContainer = beforeChildContainer; + RenderElement* beforeChildAnonymousContainer = beforeChildContainer; if (beforeChildAnonymousContainer->isAnonymousBlock() #if ENABLE(FULLSCREEN_API) // Full screen renderers and full screen placeholders act as anonymous blocks, not tables: @@ -843,47 +461,6 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, // safe fallback to use the topmost beforeChild container. beforeChild = beforeChildContainer; } - } else { - // We will reach here when beforeChild is a run-in element. - // If run-in element precedes a block-level element, it becomes the - // the first inline child of that block level element. The insertion - // point will be before that block-level element. - ASSERT(beforeChild->isRunIn()); - beforeChild = beforeChildContainer; - } - } - - // Nothing goes before the intruded run-in. - if (beforeChild && beforeChild->isRunIn() && runInIsPlacedIntoSiblingBlock(beforeChild)) - beforeChild = beforeChild->nextSibling(); - - // Check for a spanning element in columns. - if (gColumnFlowSplitEnabled) { - RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); - if (columnsBlockAncestor) { - TemporaryChange columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); - // We are placing a column-span element inside a block. - RenderBlock* newBox = createAnonymousColumnSpanBlock(); - - if (columnsBlockAncestor != this && !isRenderFlowThread()) { - // We are nested inside a multi-column element and are being split by the span. We have to break up - // our block into continuations. - RenderBoxModelObject* oldContinuation = continuation(); - - // When we split an anonymous block, there's no need to do any continuation hookup, - // since we haven't actually split a real element. - if (!isAnonymousBlock()) - setContinuation(newBox); - - splitFlow(beforeChild, newBox, newChild, oldContinuation); - return; - } - - // We have to perform a split of this block's children. This involves creating an anonymous block box to hold - // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into - // one anonymous columns block, and all of the children after |newChild| go into another anonymous block. - makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild); - return; } } @@ -909,7 +486,7 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild(); if (afterChild && afterChild->isAnonymousBlock()) { - afterChild->addChild(newChild); + downcast(*afterChild).addChild(newChild); return; } @@ -922,32 +499,15 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, } } + invalidateLineLayoutPath(); + RenderBox::addChild(newChild, beforeChild); - // Handle placement of run-ins. - placeRunInIfNeeded(newChild); - - if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) - toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); + if (madeBoxesNonInline && is(parent()) && isAnonymousBlock()) + downcast(*parent()).removeLeftoverAnonymousBlock(this); // this object may be dead here } -void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - if (continuation() && !isAnonymousBlock()) - addChildToContinuation(newChild, beforeChild); - else - addChildIgnoringContinuation(newChild, beforeChild); -} - -void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) -{ - if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock())) - addChildToAnonymousColumnBlocks(newChild, beforeChild); - else - addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); -} - static void getInlineRun(RenderObject* start, RenderObject* boundary, RenderObject*& inlineRunStart, RenderObject*& inlineRunEnd) @@ -988,42 +548,13 @@ static void getInlineRun(RenderObject* start, RenderObject* boundary, } while (!sawInline); } -void RenderBlock::deleteLineBoxTree() +void RenderBlock::deleteLines() { - if (containsFloats()) { - // Clear references to originating lines, since the lines are being deleted - const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - FloatingObjectSetIterator end = floatingObjectSet.end(); - for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) { - ASSERT(!((*it)->m_originatingLine) || (*it)->m_originatingLine->renderer() == this); - (*it)->m_originatingLine = 0; - } - } - m_lineBoxes.deleteLineBoxTree(renderArena()); - - if (AXObjectCache* cache = document()->existingAXObjectCache()) + if (AXObjectCache* cache = document().existingAXObjectCache()) cache->recomputeIsIgnored(this); } -RootInlineBox* RenderBlock::createRootInlineBox() -{ - return new (renderArena()) RootInlineBox(this); -} - -RootInlineBox* RenderBlock::createAndAppendRootInlineBox() -{ - RootInlineBox* rootBox = createRootInlineBox(); - m_lineBoxes.appendLineBox(rootBox); - - if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox) { - if (AXObjectCache* cache = document()->existingAXObjectCache()) - cache->recomputeIsIgnored(this); - } - - return rootBox; -} - -void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) +void RenderBlock::makeChildrenNonInline(RenderObject* insertionPoint) { // makeChildrenNonInline takes a block whose children are *all* inline and it // makes sure that inline children are coalesced under anonymous @@ -1037,21 +568,15 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) setChildrenInline(false); - RenderObject *child = firstChild(); + RenderObject* child = firstChild(); if (!child) return; - deleteLineBoxTree(); - - // Since we are going to have block children, we have to move - // back the run-in to its original place. - if (child->isRunIn()) { - moveRunInToOriginalPosition(child); - child = firstChild(); - } + deleteLines(); while (child) { - RenderObject *inlineRunStart, *inlineRunEnd; + RenderObject* inlineRunStart; + RenderObject* inlineRunEnd; getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd); if (!inlineRunStart) @@ -1060,12 +585,12 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) child = inlineRunEnd->nextSibling(); RenderBlock* block = createAnonymousBlock(); - children()->insertChildNode(this, block, inlineRunStart); + insertChildInternal(block, inlineRunStart, NotifyChildren); moveChildrenTo(block, inlineRunStart, child); } #ifndef NDEBUG - for (RenderObject *c = firstChild(); c; c = c->nextSibling()) + for (RenderObject* c = firstChild(); c; c = c->nextSibling()) ASSERT(!c->isInline()); #endif @@ -1077,11 +602,11 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) ASSERT(child->isAnonymousBlock()); ASSERT(!child->childrenInline()); - if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock()))) + if (child->continuation()) return; - RenderObject* firstAnChild = child->m_children.firstChild(); - RenderObject* lastAnChild = child->m_children.lastChild(); + RenderObject* firstAnChild = child->firstChild(); + RenderObject* lastAnChild = child->lastChild(); if (firstAnChild) { RenderObject* o = firstAnChild; while (o) { @@ -1095,15 +620,15 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) if (child->nextSibling()) child->nextSibling()->setPreviousSibling(lastAnChild); - if (child == m_children.firstChild()) - m_children.setFirstChild(firstAnChild); - if (child == m_children.lastChild()) - m_children.setLastChild(lastAnChild); + if (child == firstChild()) + setFirstChild(firstAnChild); + if (child == lastChild()) + setLastChild(lastAnChild); } else { - if (child == m_children.firstChild()) - m_children.setFirstChild(child->nextSibling()); - if (child == m_children.lastChild()) - m_children.setLastChild(child->previousSibling()); + if (child == firstChild()) + setFirstChild(child->nextSibling()); + if (child == lastChild()) + setLastChild(child->previousSibling()); if (child->previousSibling()) child->previousSibling()->setNextSibling(child->nextSibling()); @@ -1111,7 +636,7 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) child->nextSibling()->setPreviousSibling(child->previousSibling()); } - child->children()->setFirstChild(0); + child->setFirstChild(0); child->m_next = 0; // Remove all the information in the flow thread associated with the leftover anonymous block. @@ -1124,90 +649,53 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) child->destroy(); } -static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObject* prev, RenderObject* next) +static bool canDropAnonymousBlock(const RenderBlock& anonymousBlock) { - if (oldChild->documentBeingDestroyed() || oldChild->isInline() || oldChild->virtualContinuation()) - return false; - - if ((prev && (!prev->isAnonymousBlock() || toRenderBlock(prev)->continuation() || toRenderBlock(prev)->beingDestroyed())) - || (next && (!next->isAnonymousBlock() || toRenderBlock(next)->continuation() || toRenderBlock(next)->beingDestroyed()))) + if (anonymousBlock.beingDestroyed() || anonymousBlock.continuation()) return false; - - // FIXME: This check isn't required when inline run-ins can't be split into continuations. - if (prev && prev->firstChild() && prev->firstChild()->isInline() && prev->firstChild()->isRunIn()) + if (anonymousBlock.isRubyRun() || anonymousBlock.isRubyBase()) return false; + return true; +} - if ((prev && (prev->isRubyRun() || prev->isRubyBase())) - || (next && (next->isRubyRun() || next->isRubyBase()))) +static bool canMergeContiguousAnonymousBlocks(RenderObject& oldChild, RenderObject* previous, RenderObject* next) +{ + if (oldChild.documentBeingDestroyed() || oldChild.isInline() || oldChild.virtualContinuation()) return false; - if (!prev || !next) - return true; - - // Make sure the types of the anonymous blocks match up. - return prev->isAnonymousColumnsBlock() == next->isAnonymousColumnsBlock() - && prev->isAnonymousColumnSpanBlock() == next->isAnonymousColumnSpanBlock(); + if (previous) { + if (!previous->isAnonymousBlock()) + return false; + RenderBlock& previousAnonymousBlock = downcast(*previous); + if (!canDropAnonymousBlock(previousAnonymousBlock)) + return false; + } + if (next) { + if (!next->isAnonymousBlock()) + return false; + RenderBlock& nextAnonymousBlock = downcast(*next); + if (!canDropAnonymousBlock(nextAnonymousBlock)) + return false; + } + return true; } -void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* child) +void RenderBlock::dropAnonymousBoxChild(RenderBlock& parent, RenderBlock& child) { - parent->setNeedsLayoutAndPrefWidthsRecalc(); - parent->setChildrenInline(child->childrenInline()); - RenderObject* nextSibling = child->nextSibling(); + parent.setNeedsLayoutAndPrefWidthsRecalc(); + parent.setChildrenInline(child.childrenInline()); + if (auto* childFlowThread = child.flowThreadContainingBlock()) + childFlowThread->removeFlowChildInfo(&child); - RenderFlowThread* childFlowThread = child->flowThreadContainingBlock(); - CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread); - - RenderBlock* anonBlock = toRenderBlock(parent->children()->removeChildNode(parent, child, child->hasLayer())); - anonBlock->moveAllChildrenTo(parent, nextSibling, child->hasLayer()); + RenderObject* nextSibling = child.nextSibling(); + parent.removeChildInternal(child, child.hasLayer() ? NotifyChildren : DontNotifyChildren); + child.moveAllChildrenTo(&parent, nextSibling, child.hasLayer()); // Delete the now-empty block's lines and nuke it. - anonBlock->deleteLineBoxTree(); - if (childFlowThread && childFlowThread->isRenderNamedFlowThread()) - toRenderNamedFlowThread(childFlowThread)->removeFlowChildInfo(anonBlock); - anonBlock->destroy(); -} - -void RenderBlock::moveAllChildrenIncludingFloatsTo(RenderBlock* toBlock, bool fullRemoveInsert) -{ - moveAllChildrenTo(toBlock, 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. - // It can happen that the later block (this) contains floats that the - // previous block (toBlock) did not contain, and thus are not in the - // floating objects list for toBlock. This can result in toBlock containing - // floats that are not in it's floating objects list, but are in the - // floating objects lists of siblings and parents. This can cause problems - // when the float itself is deleted, since the deletion code assumes that - // if a float is not in it's containing block's floating objects list, it - // isn't in any floating objects list. In order to preserve this condition - // (removing it has serious performance implications), we need to copy the - // floating objects from the old block (this) to the new block (toBlock). - // The float's metrics will likely all be wrong, but since toBlock is - // already marked for layout, this will get fixed before anything gets - // displayed. - // See bug https://bugs.webkit.org/show_bug.cgi?id=115566 - if (m_floatingObjects) { - if (!toBlock->m_floatingObjects) - toBlock->createFloatingObjects(); - - const FloatingObjectSet& fromFloatingObjectSet = m_floatingObjects->set(); - FloatingObjectSetIterator end = fromFloatingObjectSet.end(); - - for (FloatingObjectSetIterator it = fromFloatingObjectSet.begin(); it != end; ++it) { - FloatingObject* floatingObject = *it; - - // Don't insert the object again if it's already in the list - if (toBlock->containsFloat(floatingObject->renderer())) - continue; - - toBlock->m_floatingObjects->add(floatingObject->clone()); - } - } + child.deleteLines(); + child.destroy(); } -void RenderBlock::removeChild(RenderObject* oldChild) +void RenderBlock::removeChild(RenderObject& oldChild) { // No need to waste time in merging or removing empty anonymous blocks. // We can just bail out if our document is getting destroyed. @@ -1216,111 +704,128 @@ void RenderBlock::removeChild(RenderObject* oldChild) return; } - // This protects against column split flows when anonymous blocks are getting merged. - TemporaryChange columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); - - // If this child is a block, and if our previous and next siblings are - // both anonymous blocks with inline content, then we can go ahead and - // fold the inline content back together. - RenderObject* prev = oldChild->previousSibling(); - RenderObject* next = oldChild->nextSibling(); + // If this child is a block, and if our previous and next siblings are both anonymous blocks + // with inline content, then we can fold the inline content back together. + RenderObject* prev = oldChild.previousSibling(); + RenderObject* next = oldChild.nextSibling(); bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next); if (canMergeAnonymousBlocks && prev && next) { prev->setNeedsLayoutAndPrefWidthsRecalc(); - RenderBlock* nextBlock = toRenderBlock(next); - RenderBlock* prevBlock = toRenderBlock(prev); + RenderBlock& nextBlock = downcast(*next); + RenderBlock& prevBlock = downcast(*prev); if (prev->childrenInline() != next->childrenInline()) { - RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; - RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock; + RenderBlock& inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; + RenderBlock& blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock; // Place the inline children block inside of the block children block instead of deleting it. // In order to reuse it, we have to reset it to just be a generic anonymous block. Make sure // to clear out inherited column properties by just making a new style, and to also clear the // column span flag if it is set. - ASSERT(!inlineChildrenBlock->continuation()); - RefPtr newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK); + ASSERT(!inlineChildrenBlock.continuation()); // Cache this value as it might get changed in setStyle() call. - bool inlineChildrenBlockHasLayer = inlineChildrenBlock->hasLayer(); - inlineChildrenBlock->setStyle(newStyle); - children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlockHasLayer); + bool inlineChildrenBlockHasLayer = inlineChildrenBlock.hasLayer(); + inlineChildrenBlock.setStyle(RenderStyle::createAnonymousStyleWithDisplay(&style(), BLOCK)); + removeChildInternal(inlineChildrenBlock, inlineChildrenBlockHasLayer ? NotifyChildren : DontNotifyChildren); // Now just put the inlineChildrenBlock inside the blockChildrenBlock. - blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0, - inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer()); + RenderObject* beforeChild = prev == &inlineChildrenBlock ? blockChildrenBlock.firstChild() : nullptr; + blockChildrenBlock.insertChildInternal(&inlineChildrenBlock, beforeChild, + (inlineChildrenBlockHasLayer || blockChildrenBlock.hasLayer()) ? NotifyChildren : DontNotifyChildren); next->setNeedsLayoutAndPrefWidthsRecalc(); // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child // of "this". we null out prev or next so that is not used later in the function. - if (inlineChildrenBlock == prevBlock) - prev = 0; + if (&inlineChildrenBlock == &prevBlock) + prev = nullptr; else - next = 0; + next = nullptr; } else { // Take all the children out of the |next| block and put them in // the |prev| block. - nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); + nextBlock.moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock.hasLayer() || prevBlock.hasLayer()); // Delete the now-empty block's lines and nuke it. - nextBlock->deleteLineBoxTree(); - nextBlock->destroy(); - next = 0; + nextBlock.deleteLines(); + nextBlock.destroy(); + next = nullptr; } } + invalidateLineLayoutPath(); + RenderBox::removeChild(oldChild); RenderObject* child = prev ? prev : next; - if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canCollapseAnonymousBlockChild()) { + if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canDropAnonymousBlockChild()) { // The removal has knocked us down to containing only a single anonymous - // box. We can go ahead and pull the content right back up into our - // box. - collapseAnonymousBoxChild(this, child); - } else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canCollapseAnonymousBlockChild()) { + // box. We can pull the content right back up into our box. + dropAnonymousBoxChild(*this, downcast(*child)); + } else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canDropAnonymousBlockChild()) { // It's possible that the removal has knocked us down to a single anonymous - // block with pseudo-style element siblings (e.g. first-letter). If these - // are floating, then we need to pull the content up also. - RenderBlock* anonBlock = toRenderBlock((prev && prev->isAnonymousBlock()) ? prev : next); - if ((anonBlock->previousSibling() || anonBlock->nextSibling()) - && (!anonBlock->previousSibling() || (anonBlock->previousSibling()->style()->styleType() != NOPSEUDO && anonBlock->previousSibling()->isFloating() && !anonBlock->previousSibling()->previousSibling())) - && (!anonBlock->nextSibling() || (anonBlock->nextSibling()->style()->styleType() != NOPSEUDO && anonBlock->nextSibling()->isFloating() && !anonBlock->nextSibling()->nextSibling()))) { - collapseAnonymousBoxChild(this, anonBlock); + // block with floating siblings. + RenderBlock& anonBlock = downcast((prev && prev->isAnonymousBlock()) ? *prev : *next); + if (canDropAnonymousBlock(anonBlock)) { + bool dropAnonymousBlock = true; + for (auto& sibling : childrenOfType(*this)) { + if (&sibling == &anonBlock) + continue; + if (!sibling.isFloating()) { + dropAnonymousBlock = false; + break; + } + } + if (dropAnonymousBlock) + dropAnonymousBoxChild(*this, anonBlock); } } if (!firstChild()) { // If this was our last child be sure to clear out our line boxes. if (childrenInline()) - deleteLineBoxTree(); + deleteLines(); // If we are an empty anonymous block in the continuation chain, // we need to remove ourself and fix the continuation chain. - if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild->isListMarker()) { - RenderObject* containingBlockIgnoringAnonymous = containingBlock(); + if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild.isListMarker()) { + auto containingBlockIgnoringAnonymous = containingBlock(); while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymousBlock()) containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock(); - for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) { - if (curr->virtualContinuation() != this) + for (RenderObject* current = this; current; current = current->previousInPreOrder(containingBlockIgnoringAnonymous)) { + if (current->virtualContinuation() != this) continue; // Found our previous continuation. We just need to point it to // |this|'s next continuation. RenderBoxModelObject* nextContinuation = continuation(); - if (curr->isRenderInline()) - toRenderInline(curr)->setContinuation(nextContinuation); - else if (curr->isRenderBlock()) - toRenderBlock(curr)->setContinuation(nextContinuation); + if (is(*current)) + downcast(*current).setContinuation(nextContinuation); + else if (is(*current)) + downcast(*current).setContinuation(nextContinuation); else ASSERT_NOT_REACHED(); break; } - setContinuation(0); + setContinuation(nullptr); destroy(); } } } +bool RenderBlock::childrenPreventSelfCollapsing() const +{ + // Whether or not we collapse is dependent on whether all our normal flow children + // are also self-collapsing. + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->isFloatingOrOutOfFlowPositioned()) + continue; + if (!child->isSelfCollapsingBlock()) + return true; + } + return false; +} + bool RenderBlock::isSelfCollapsingBlock() const { // We are not self-collapsing if we @@ -1331,88 +836,98 @@ bool RenderBlock::isSelfCollapsingBlock() const // (e) have specified that one of our margins can't collapse using a CSS extension if (logicalHeight() > 0 || isTable() || borderAndPaddingLogicalHeight() - || style()->logicalMinHeight().isPositive() - || style()->marginBeforeCollapse() == MSEPARATE || style()->marginAfterCollapse() == MSEPARATE) + || style().logicalMinHeight().isPositive() + || style().marginBeforeCollapse() == MSEPARATE || style().marginAfterCollapse() == MSEPARATE) return false; - Length logicalHeightLength = style()->logicalHeight(); + Length logicalHeightLength = style().logicalHeight(); bool hasAutoHeight = logicalHeightLength.isAuto(); - if (logicalHeightLength.isPercent() && !document()->inQuirksMode()) { + if (logicalHeightLength.isPercentOrCalculated() && !document().inQuirksMode()) { hasAutoHeight = true; - for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { - if (cb->style()->logicalHeight().isFixed() || cb->isTableCell()) + for (RenderBlock* cb = containingBlock(); cb && !is(*cb); cb = cb->containingBlock()) { + if (cb->style().logicalHeight().isFixed() || cb->isTableCell()) hasAutoHeight = false; } } // If the height is 0 or auto, then whether or not we are a self-collapsing block depends // on whether we have content that is all self-collapsing or not. - if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) { - // If the block has inline children, see if we generated any line boxes. If we have any - // line boxes, then we can't be self-collapsing, since we have content. - if (childrenInline()) - return !firstLineBox(); - - // Whether or not we collapse is dependent on whether all our normal flow children - // are also self-collapsing. - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isFloatingOrOutOfFlowPositioned()) - continue; - if (!child->isSelfCollapsingBlock()) - return false; - } - return true; - } + if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercentOrCalculated()) && logicalHeightLength.isZero())) + return !childrenPreventSelfCollapsing(); + return false; } -void RenderBlock::startDelayUpdateScrollInfo() +static inline UpdateScrollInfoAfterLayoutTransaction* currentUpdateScrollInfoAfterLayoutTransaction() { - if (gDelayUpdateScrollInfo == 0) { - ASSERT(!gDelayedUpdateScrollInfoSet); - gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet; - } - ASSERT(gDelayedUpdateScrollInfoSet); - ++gDelayUpdateScrollInfo; + if (!updateScrollInfoAfterLayoutTransactionStack()) + return nullptr; + return &updateScrollInfoAfterLayoutTransactionStack()->last(); +} + +void RenderBlock::beginUpdateScrollInfoAfterLayoutTransaction() +{ + if (!updateScrollInfoAfterLayoutTransactionStack()) + updateScrollInfoAfterLayoutTransactionStack() = std::make_unique(); + if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty() || currentUpdateScrollInfoAfterLayoutTransaction()->view != &view()) + updateScrollInfoAfterLayoutTransactionStack()->append(UpdateScrollInfoAfterLayoutTransaction(view())); + ++currentUpdateScrollInfoAfterLayoutTransaction()->nestedCount; } -void RenderBlock::finishDelayUpdateScrollInfo() +void RenderBlock::endAndCommitUpdateScrollInfoAfterLayoutTransaction() { - --gDelayUpdateScrollInfo; - ASSERT(gDelayUpdateScrollInfo >= 0); - if (gDelayUpdateScrollInfo == 0) { - ASSERT(gDelayedUpdateScrollInfoSet); + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + ASSERT(transaction); + ASSERT(transaction->view == &view()); + if (--transaction->nestedCount) + return; - OwnPtr infoSet(adoptPtr(gDelayedUpdateScrollInfoSet)); - gDelayedUpdateScrollInfoSet = 0; + // Calling RenderLayer::updateScrollInfoAfterLayout() may cause its associated block to layout again and + // updates its scroll info (i.e. call RenderBlock::updateScrollInfoAfterLayout()). We remove |transaction| + // from the transaction stack to ensure that all subsequent calls to RenderBlock::updateScrollInfoAfterLayout() + // are dispatched immediately. That is, to ensure that such subsequent calls aren't added to |transaction| + // while we are processing it. + Vector blocksToUpdate; + copyToVector(transaction->blocks, blocksToUpdate); + updateScrollInfoAfterLayoutTransactionStack()->removeLast(); + if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty()) + updateScrollInfoAfterLayoutTransactionStack() = nullptr; - for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) { - RenderBlock* block = *it; - if (block->hasOverflowClip()) { - block->layer()->updateScrollInfoAfterLayout(); - block->clearLayoutOverflow(); - } - } + for (auto* block : blocksToUpdate) { + ASSERT(block->hasOverflowClip()); + block->layer()->updateScrollInfoAfterLayout(); + block->clearLayoutOverflow(); + } +} + +void RenderBlock::removeFromUpdateScrollInfoAfterLayoutTransaction() +{ + if (UNLIKELY(updateScrollInfoAfterLayoutTransactionStack().get() != 0)) { + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + ASSERT(transaction); + if (transaction->view == &view()) + transaction->blocks.remove(this); } } void RenderBlock::updateScrollInfoAfterLayout() { - if (hasOverflowClip()) { - if (style()->isFlippedBlocksWritingMode()) { - // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937 - // Workaround for now. We cannot delay the scroll info for overflow - // for items with opposite writing directions, as the contents needs - // to overflow in that direction - layer()->updateScrollInfoAfterLayout(); + if (!hasOverflowClip()) + return; + + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937 + // Workaround for now. We cannot delay the scroll info for overflow + // for items with opposite writing directions, as the contents needs + // to overflow in that direction + if (!style().isFlippedBlocksWritingMode()) { + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + if (transaction && transaction->view == &view()) { + transaction->blocks.add(this); return; } - - if (gDelayUpdateScrollInfo) - gDelayedUpdateScrollInfoSet->add(this); - else - layer()->updateScrollInfoAfterLayout(); } + if (layer()) + layer()->updateScrollInfoAfterLayout(); } void RenderBlock::layout() @@ -1429,361 +944,76 @@ void RenderBlock::layout() // It's safe to check for control clip here, since controls can never be table cells. // If we have a lightweight clip, there can never be any overflow from children. - if (hasControlClip() && m_overflow && !gDelayUpdateScrollInfo) + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + bool isDelayingUpdateScrollInfoAfterLayoutInView = transaction && transaction->view == &view(); + if (hasControlClip() && m_overflow && !isDelayingUpdateScrollInfoAfterLayoutInView) clearLayoutOverflow(); invalidateBackgroundObscurationStatus(); } -#if ENABLE(CSS_SHAPES) -void RenderBlock::updateShapeInsideInfoAfterStyleChange(const ShapeValue* shapeInside, const ShapeValue* oldShapeInside) +static RenderBlockRareData* getBlockRareData(const RenderBlock* block) { - // FIXME: A future optimization would do a deep comparison for equality. - if (shapeInside == oldShapeInside) - return; - - if (shapeInside) { - ShapeInsideInfo* shapeInsideInfo = ensureShapeInsideInfo(); - shapeInsideInfo->dirtyShapeSize(); - } else { - setShapeInsideInfo(nullptr); - markShapeInsideDescendantsForLayout(); - } + return gRareDataMap ? gRareDataMap->get(block) : nullptr; } -void RenderBlock::markShapeInsideDescendantsForLayout() +static RenderBlockRareData& ensureBlockRareData(const RenderBlock* block) { - if (!everHadLayout()) - return; - if (childrenInline()) { - setNeedsLayout(true); - return; - } - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (!child->isRenderBlock()) - continue; - RenderBlock* childBlock = toRenderBlock(child); - childBlock->markShapeInsideDescendantsForLayout(); - } -} -#endif - -static inline bool shapeInfoRequiresRelayout(const RenderBlock* block) -{ -#if !ENABLE(CSS_SHAPES) - return false; -#else - ShapeInsideInfo* info = block->shapeInsideInfo(); - if (info) - info->setNeedsLayout(info->shapeSizeDirty()); - else - info = block->layoutShapeInsideInfo(); - return info && info->needsLayout(); -#endif -} - -bool RenderBlock::updateRegionsAndShapesBeforeChildLayout(RenderFlowThread* flowThread) -{ -#if ENABLE(CSS_SHAPES) - if (!flowThread && !shapeInsideInfo()) -#else - if (!flowThread) -#endif - return shapeInfoRequiresRelayout(this); - - LayoutUnit oldHeight = logicalHeight(); - LayoutUnit oldTop = logicalTop(); - - // Compute the maximum logical height content may cause this block to expand to - // FIXME: These should eventually use the const computeLogicalHeight rather than updateLogicalHeight - setLogicalHeight(RenderFlowThread::maxLogicalHeight()); - updateLogicalHeight(); - -#if ENABLE(CSS_SHAPES) - computeShapeSize(); -#endif - - // Set our start and end regions. No regions above or below us will be considered by our children. They are - // effectively clamped to our region range. - computeRegionRangeForBlock(flowThread); - - setLogicalHeight(oldHeight); - setLogicalTop(oldTop); - - return shapeInfoRequiresRelayout(this); -} - -#if ENABLE(CSS_SHAPES) -void RenderBlock::computeShapeSize() -{ - ShapeInsideInfo* shapeInsideInfo = this->shapeInsideInfo(); - if (shapeInsideInfo) { - bool percentageLogicalHeightResolvable = percentageLogicalHeightIsResolvableFromBlock(this, false); - shapeInsideInfo->setShapeSize(logicalWidth(), percentageLogicalHeightResolvable ? logicalHeight() : LayoutUnit()); - } -} -#endif - -void RenderBlock::updateRegionsAndShapesAfterChildLayout(RenderFlowThread* flowThread, bool heightChanged) -{ -#if ENABLE(CSS_SHAPES) - // A previous sibling has changed dimension, so we need to relayout the shape with the content - ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); - if (heightChanged && shapeInsideInfo) - shapeInsideInfo->dirtyShapeSize(); -#else - UNUSED_PARAM(heightChanged); -#endif - computeRegionRangeForBlock(flowThread); + if (!gRareDataMap) + gRareDataMap = new RenderBlockRareDataMap; + + auto& rareData = gRareDataMap->add(block, nullptr).iterator->value; + if (!rareData) + rareData = std::make_unique(); + return *rareData.get(); } -void RenderBlock::computeRegionRangeForBlock(RenderFlowThread* flowThread) +void RenderBlock::preparePaginationBeforeBlockLayout(bool& relayoutChildren) { + // Regions changing widths can force us to relayout our children. + RenderFlowThread* flowThread = flowThreadContainingBlock(); if (flowThread) - flowThread->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage()); + flowThread->logicalWidthChangedInRegionsForBlock(this, relayoutChildren); } -bool RenderBlock::updateLogicalWidthAndColumnWidth() +bool RenderBlock::recomputeLogicalWidth() { LayoutUnit oldWidth = logicalWidth(); - LayoutUnit oldColumnWidth = desiredColumnWidth(); - + updateLogicalWidth(); - calcColumnWidth(); - - bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged; - m_hasBorderOrPaddingLogicalWidthChanged = false; + + bool hasBorderOrPaddingLogicalWidthChanged = this->hasBorderOrPaddingLogicalWidthChanged(); + setHasBorderOrPaddingLogicalWidthChanged(false); - return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged; + return oldWidth != logicalWidth() || hasBorderOrPaddingLogicalWidthChanged; } -void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight) +void RenderBlock::layoutBlock(bool, LayoutUnit) { - ColumnInfo* colInfo = columnInfo(); - if (hasColumns()) { - 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); - } - - if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) - pageLogicalHeightChanged = true; - - 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(); - } + ASSERT_NOT_REACHED(); + clearNeedsLayout(); } -void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight) +void RenderBlock::addOverflowFromChildren() { - ASSERT(needsLayout()); - - if (isInline() && !isInlineBlockOrInlineTable()) // Inline
s inside various table elements can - return; // cause us to come in here. Just bail. - - if (!relayoutChildren && simplifiedLayout()) - return; - - LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); - - if (updateLogicalWidthAndColumnWidth()) - relayoutChildren = true; - - clearFloats(); - - LayoutUnit previousHeight = logicalHeight(); - // FIXME: should this start out as borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(), - // for consistency with other render classes? - setLogicalHeight(0); - - bool pageLogicalHeightChanged = false; - bool hasSpecifiedPageLogicalHeight = false; - checkForPaginationLogicalHeightChange(pageLogicalHeight, pageLogicalHeightChanged, hasSpecifiedPageLogicalHeight); - - RenderView* renderView = view(); - RenderStyle* styleToUse = style(); - LayoutStateMaintainer statePusher(renderView, this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || styleToUse->isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged, columnInfo()); - - // Regions changing widths can force us to relayout our children. - RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (logicalWidthChangedInRegions(flowThread)) - relayoutChildren = true; - if (updateRegionsAndShapesBeforeChildLayout(flowThread)) - relayoutChildren = true; - - // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track - // our current maximal positive and negative margins. These values are used when we - // are collapsed with adjacent blocks, so for example, if you have block A and B - // collapsing together, then you'd take the maximal positive margin from both A and B - // and subtract it from the maximal negative margin from both A and B to get the - // true collapsed margin. This algorithm is recursive, so when we finish layout() - // our block knows its current maximal positive/negative values. - // - // Start out by setting our margin values to our current margins. Table cells have - // no margins, so we don't fill in the values for table cells. - bool isCell = isTableCell(); - if (!isCell) { - initMaxMarginValues(); - - setHasMarginBeforeQuirk(styleToUse->hasMarginBeforeQuirk()); - setHasMarginAfterQuirk(styleToUse->hasMarginAfterQuirk()); - setPaginationStrut(0); - } - - LayoutUnit repaintLogicalTop = 0; - LayoutUnit repaintLogicalBottom = 0; - LayoutUnit maxFloatLogicalBottom = 0; - if (!firstChild() && !isAnonymousBlock()) - setChildrenInline(true); if (childrenInline()) - layoutInlineChildren(relayoutChildren, repaintLogicalTop, repaintLogicalBottom); + addOverflowFromInlineChildren(); else - layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom); - - // Expand our intrinsic height to encompass floats. - LayoutUnit toAdd = borderAndPaddingAfter() + scrollbarLogicalHeight(); - if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats()) - setLogicalHeight(lowestFloatLogicalBottom() + toAdd); - - if (relayoutForPagination(hasSpecifiedPageLogicalHeight, pageLogicalHeight, statePusher)) - return; - - // Calculate our new height. - LayoutUnit oldHeight = logicalHeight(); - LayoutUnit oldClientAfterEdge = clientLogicalBottom(); - - // 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 (isRenderFlowThread()) - toRenderFlowThread(this)->applyBreakAfterContent(oldClientAfterEdge); - - updateLogicalHeight(); - LayoutUnit newHeight = logicalHeight(); - if (oldHeight != newHeight) { - if (oldHeight > newHeight && maxFloatLogicalBottom > newHeight && !childrenInline()) { - // One of our children's floats may have become an overhanging float for us. We need to look for it. - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (child->isBlockFlow() && !child->isFloatingOrOutOfFlowPositioned()) { - RenderBlock* block = toRenderBlock(child); - if (block->lowestFloatLogicalBottom() + block->logicalTop() > newHeight) - addOverhangingFloats(block, false); - } - } - } - } - - bool heightChanged = (previousHeight != newHeight); - if (heightChanged) - relayoutChildren = true; - - layoutPositionedObjects(relayoutChildren || isRoot()); - - updateRegionsAndShapesAfterChildLayout(flowThread, heightChanged); - - // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway). - computeOverflow(oldClientAfterEdge); + addOverflowFromBlockChildren(); - statePusher.pop(); - - fitBorderToLinesIfNeeded(); - - if (renderView->layoutState()->m_pageLogicalHeight) - setPageLogicalOffset(renderView->layoutState()->pageLogicalOffset(this, logicalTop())); - - updateLayerTransform(); - - // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if - // we overflow or not. - updateScrollInfoAfterLayout(); - - // FIXME: This repaint logic should be moved into a separate helper function! - // Repaint with our new bounds if they are different from our old bounds. - bool didFullRepaint = repainter.repaintAfterLayout(); - if (!didFullRepaint && repaintLogicalTop != repaintLogicalBottom && (styleToUse->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { - // FIXME: We could tighten up the left and right invalidation points if we let layoutInlineChildren fill them in based off the particular lines - // it had to lay out. We wouldn't need the hasOverflowClip() hack in that case either. - LayoutUnit repaintLogicalLeft = logicalLeftVisualOverflow(); - LayoutUnit repaintLogicalRight = logicalRightVisualOverflow(); - if (hasOverflowClip()) { - // If we have clipped overflow, we should use layout overflow as well, since visual overflow from lines didn't propagate to our block's overflow. - // Note the old code did this as well but even for overflow:visible. The addition of hasOverflowClip() at least tightens up the hack a bit. - // layoutInlineChildren should be patched to compute the entire repaint rect. - repaintLogicalLeft = min(repaintLogicalLeft, logicalLeftLayoutOverflow()); - repaintLogicalRight = max(repaintLogicalRight, logicalRightLayoutOverflow()); - } - - LayoutRect repaintRect; - if (isHorizontalWritingMode()) - repaintRect = LayoutRect(repaintLogicalLeft, repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop); - 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()); - - // Don't allow this rect to spill out of our overflow box. - repaintRect.intersect(LayoutRect(LayoutPoint(), size())); - } - - // Make sure the rect is still non-empty after intersecting for overflow above - if (!repaintRect.isEmpty()) { - repaintRectangle(repaintRect); // We need to do a partial repaint of our content. - if (hasReflection()) - repaintRectangle(reflectedRect(repaintRect)); - } - } - - setNeedsLayout(false); -} - -void RenderBlock::addOverflowFromChildren() -{ - if (!hasColumns()) { - if (childrenInline()) - addOverflowFromInlineChildren(); - else - addOverflowFromBlockChildren(); - } else { - ColumnInfo* colInfo = columnInfo(); - if (columnCount(colInfo)) { - LayoutRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1); - addLayoutOverflow(lastRect); - if (!hasOverflowClip()) - addVisualOverflow(lastRect); - } + // If this block is flowed inside a flow thread, make sure its overflow is propagated to the containing regions. + if (m_overflow) { + if (RenderFlowThread* containingFlowThread = flowThreadContainingBlock()) + containingFlowThread->addRegionsVisualOverflow(this, m_overflow->visualOverflowRect()); } } -void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats) +void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) { - m_overflow.clear(); - + clearOverflow(); // Add overflow from children. addOverflowFromChildren(); - if (!hasColumns() && (recomputeFloats || isRoot() || expandsToEncloseOverhangingFloats() || hasSelfPaintingLayer())) - addOverflowFromFloats(); - // Add in the overflow from positioned objects. addOverflowFromPositionedObjects(); @@ -1791,33 +1021,22 @@ void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeF // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always // be considered reachable. - LayoutRect clientRect(clientBoxRect()); + LayoutRect clientRect(flippedClientBoxRect()); LayoutRect rectToApply; if (isHorizontalWritingMode()) - rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, max(0, oldClientAfterEdge - clientRect.y())); + rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, std::max(0, oldClientAfterEdge - clientRect.y())); else - rectToApply = LayoutRect(clientRect.x(), clientRect.y(), max(0, oldClientAfterEdge - clientRect.x()), 1); + rectToApply = LayoutRect(clientRect.x(), clientRect.y(), std::max(0, oldClientAfterEdge - clientRect.x()), 1); addLayoutOverflow(rectToApply); if (hasRenderOverflow()) m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); } - // Allow our overflow to catch cases where the caret in an empty editable element with negative text indent needs to get painted. - LayoutUnit textIndent = textIndentOffset(); - if (textIndent < 0) { - LayoutRect clientRect(clientBoxRect()); - LayoutRect rectToApply = LayoutRect(clientRect.x() + min(0, textIndent), clientRect.y(), clientRect.width() - min(0, textIndent), clientRect.height()); - addVisualOverflow(rectToApply); - } - - // Add visual overflow from box-shadow and border-image-outset. + // Add visual overflow from box-shadow, border-image-outset and outline. addVisualEffectOverflow(); // Add visual overflow from theme. addVisualOverflowFromTheme(); - - if (isRenderNamedFlowThread()) - toRenderNamedFlowThread(this)->computeOversetStateForRegions(oldClientAfterEdge); } void RenderBlock::clearLayoutOverflow() @@ -1826,7 +1045,8 @@ void RenderBlock::clearLayoutOverflow() return; if (visualOverflowRect() == borderBoxRect()) { - m_overflow.clear(); + // FIXME: Implement complete solution for regions overflow. + clearOverflow(); return; } @@ -1835,41 +1055,25 @@ void RenderBlock::clearLayoutOverflow() void RenderBlock::addOverflowFromBlockChildren() { - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) { if (!child->isFloatingOrOutOfFlowPositioned()) addOverflowFromChild(child); } } -void RenderBlock::addOverflowFromFloats() -{ - if (!m_floatingObjects) - return; - - const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - FloatingObjectSetIterator end = floatingObjectSet.end(); - for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) { - FloatingObject* r = *it; - if (r->isDescendant()) - addOverflowFromChild(r->m_renderer, IntSize(xPositionForFloatIncludingMargin(r), yPositionForFloatIncludingMargin(r))); - } -} - void RenderBlock::addOverflowFromPositionedObjects() { TrackedRendererListHashSet* positionedDescendants = positionedObjects(); if (!positionedDescendants) return; - RenderBox* positionedObject; - TrackedRendererListHashSet::iterator end = positionedDescendants->end(); - for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { - positionedObject = *it; + for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) { + RenderBox* positionedObject = *it; // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content. - if (positionedObject->style()->position() != FixedPosition) { + if (positionedObject->style().position() != FixedPosition) { LayoutUnit x = positionedObject->x(); - if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) + if (style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) x -= verticalScrollbarWidth(); addOverflowFromChild(positionedObject, LayoutSize(x, positionedObject->y())); } @@ -1878,4068 +1082,1601 @@ void RenderBlock::addOverflowFromPositionedObjects() void RenderBlock::addVisualOverflowFromTheme() { - if (!style()->hasAppearance()) + if (!style().hasAppearance()) return; - IntRect inflatedRect = pixelSnappedBorderBoxRect(); - theme()->adjustRepaintRect(this, inflatedRect); - addVisualOverflow(inflatedRect); -} + FloatRect inflatedRect = borderBoxRect(); + theme().adjustRepaintRect(*this, inflatedRect); + addVisualOverflow(snappedIntRect(LayoutRect(inflatedRect))); -bool RenderBlock::expandsToEncloseOverhangingFloats() const -{ - return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated()) - || hasColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot(); + if (RenderFlowThread* flowThread = flowThreadContainingBlock()) + flowThread->addRegionsVisualOverflowFromTheme(this); } -void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo) +LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox& child, LayoutUnit childMarginStart, RenderRegion* region) { - bool isHorizontal = isHorizontalWritingMode(); - bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontal); - - LayoutUnit logicalTop = logicalHeight(); - updateStaticInlinePositionForChild(child, logicalTop); - - if (!marginInfo.canCollapseWithMarginBefore()) { - // Positioned blocks don't collapse margins, so add the margin provided by - // the container now. The child's own margin is added later when calculating its logical top. - LayoutUnit collapsedBeforePos = marginInfo.positiveMargin(); - LayoutUnit collapsedBeforeNeg = marginInfo.negativeMargin(); - logicalTop += collapsedBeforePos - collapsedBeforeNeg; - } - - RenderLayer* childLayer = child->layer(); - if (childLayer->staticBlockPosition() != logicalTop) { - childLayer->setStaticBlockPosition(logicalTop); - if (hasStaticBlockPosition) - child->setChildNeedsLayout(true, MarkOnlyThis); - } -} + LayoutUnit startPosition = startOffsetForContent(region); -void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo) -{ - // The float should be positioned taking into account the bottom margin - // of the previous flow. We add that margin into the height, get the - // float positioned properly, and then subtract the margin out of the - // height again. In the case of self-collapsing blocks, we always just - // use the top margins, since the self-collapsing block collapsed its - // own bottom margin into its top margin. - // - // Note also that the previous flow may collapse its margin into the top of - // our block. If this is the case, then we do not add the margin in to our - // height when computing the position of the float. This condition can be tested - // for by simply calling canCollapseWithMarginBefore. See - // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for - // an example of this scenario. - LayoutUnit marginOffset = marginInfo.canCollapseWithMarginBefore() ? LayoutUnit() : marginInfo.margin(); - setLogicalHeight(logicalHeight() + marginOffset); - positionNewFloats(); - setLogicalHeight(logicalHeight() - marginOffset); -} - -static void destroyRunIn(RenderBoxModelObject* runIn) -{ - ASSERT(runIn->isRunIn()); - ASSERT(!runIn->firstChild()); - - // Delete our line box tree. This is needed as our children got moved - // and our line box tree is no longer valid. - if (runIn->isRenderBlock()) - toRenderBlock(runIn)->deleteLineBoxTree(); - else if (runIn->isRenderInline()) - toRenderInline(runIn)->deleteLineBoxTree(); - else - ASSERT_NOT_REACHED(); + // Add in our start margin. + LayoutUnit oldPosition = startPosition + childMarginStart; + LayoutUnit newPosition = oldPosition; + + LayoutUnit blockOffset = logicalTopForChild(child); + if (region) + blockOffset = std::max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage())); + + LayoutUnit startOff = startOffsetForLineInRegion(blockOffset, DoNotIndentText, region, logicalHeightForChild(child)); + + if (style().textAlign() != WEBKIT_CENTER && !child.style().marginStartUsing(&style()).isAuto()) { + if (childMarginStart < 0) + startOff += childMarginStart; + newPosition = std::max(newPosition, startOff); // Let the float sit in the child's margin if it can fit. + } else if (startOff != startPosition) + newPosition = startOff + childMarginStart; - runIn->destroy(); + return newPosition - oldPosition; } -void RenderBlock::placeRunInIfNeeded(RenderObject* newChild) +void RenderBlock::setLogicalLeftForChild(RenderBox& child, LayoutUnit logicalLeft, ApplyLayoutDeltaMode applyDelta) { - if (newChild->isRunIn()) - moveRunInUnderSiblingBlockIfNeeded(newChild); - else if (RenderObject* prevSibling = newChild->previousSibling()) { - if (prevSibling->isRunIn()) - moveRunInUnderSiblingBlockIfNeeded(prevSibling); + if (isHorizontalWritingMode()) { + if (applyDelta == ApplyLayoutDelta) + view().addLayoutDelta(LayoutSize(child.x() - logicalLeft, 0)); + child.setX(logicalLeft); + } else { + if (applyDelta == ApplyLayoutDelta) + view().addLayoutDelta(LayoutSize(0, child.y() - logicalLeft)); + child.setY(logicalLeft); } } -RenderBoxModelObject* RenderBlock::createReplacementRunIn(RenderBoxModelObject* runIn) +void RenderBlock::setLogicalTopForChild(RenderBox& child, LayoutUnit logicalTop, ApplyLayoutDeltaMode applyDelta) { - ASSERT(runIn->isRunIn()); - ASSERT(runIn->node()); - - RenderBoxModelObject* newRunIn = 0; - if (!runIn->isRenderBlock()) - newRunIn = new (renderArena()) RenderBlock(runIn->node()); - else - newRunIn = new (renderArena()) RenderInline(toElement(runIn->node())); - - runIn->node()->setRenderer(newRunIn); - newRunIn->setStyle(runIn->style()); - - runIn->moveAllChildrenTo(newRunIn, true); - - return newRunIn; + if (isHorizontalWritingMode()) { + if (applyDelta == ApplyLayoutDelta) + view().addLayoutDelta(LayoutSize(0, child.y() - logicalTop)); + child.setY(logicalTop); + } else { + if (applyDelta == ApplyLayoutDelta) + view().addLayoutDelta(LayoutSize(child.x() - logicalTop, 0)); + child.setX(logicalTop); + } } -void RenderBlock::moveRunInUnderSiblingBlockIfNeeded(RenderObject* runIn) +void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox& child) { - ASSERT(runIn->isRunIn()); + // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into + // an auto value. Add a method to determine this, so that we can avoid the relayout. + if (relayoutChildren || (child.hasRelativeLogicalHeight() && !isRenderView())) + child.setChildNeedsLayout(MarkOnlyThis); - // See if we have inline children. If the children aren't inline, - // then just treat the run-in as a normal block. - if (!runIn->childrenInline()) - return; + // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. + if (relayoutChildren && child.needsPreferredWidthsRecalculation()) + child.setPreferredLogicalWidthsDirty(true, MarkOnlyThis); +} - // FIXME: We don't handle non-block elements with run-in for now. - if (!runIn->isRenderBlock()) +void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants() +{ + if (!gPercentHeightDescendantsMap) return; - // FIXME: We don't support run-ins with or as part of a continuation - // as it makes the back-and-forth placing complex. - if (runIn->isElementContinuation() || runIn->virtualContinuation()) + TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this); + if (!descendants) return; - // Check if this node is allowed to run-in. E.g.