/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "RenderBlock.h" #include "AXObjectCache.h" #include "ColumnInfo.h" #include "Document.h" #include "Editor.h" #include "Element.h" #include "FloatQuad.h" #include "Frame.h" #include "FrameSelection.h" #include "FrameView.h" #include "GraphicsContext.h" #include "HTMLInputElement.h" #include "HTMLNames.h" #include "HitTestLocation.h" #include "HitTestResult.h" #include "InlineIterator.h" #include "InlineTextBox.h" #include "LayoutRepainter.h" #include "LogicalSelectionOffsetCaches.h" #include "OverflowEvent.h" #include "Page.h" #include "PaintInfo.h" #include "RenderBoxRegionInfo.h" #include "RenderCombineText.h" #include "RenderDeprecatedFlexibleBox.h" #include "RenderFlexibleBox.h" #include "RenderInline.h" #include "RenderLayer.h" #include "RenderMarquee.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" #include "RenderTableCell.h" #include "RenderTextFragment.h" #include "RenderTheme.h" #include "RenderView.h" #include "SVGTextRunRenderingContext.h" #include "Settings.h" #include "ShadowRoot.h" #include "TransformState.h" #include #include #if ENABLE(CSS_SHAPES) #include "ShapeInsideInfo.h" #include "ShapeOutsideInfo.h" #endif using namespace std; using namespace WTF; using namespace Unicode; 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; }; COMPILE_ASSERT(sizeof(RenderBlock::MarginValues) == sizeof(LayoutUnit[4]), MarginValues_should_stay_small); struct SameSizeAsMarginInfo { uint32_t bitfields : 16; LayoutUnit margins[2]; }; typedef WTF::HashMap > ColumnInfoMap; static ColumnInfoMap* gColumnInfoMap = 0; static TrackedDescendantsMap* gPositionedDescendantsMap = 0; static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; static TrackedContainerMap* gPositionedContainerMap = 0; static TrackedContainerMap* gPercentHeightContainerMap = 0; typedef WTF::HashMap > > ContinuationOutlineTableMap; typedef WTF::HashSet DelayedUpdateScrollInfoSet; static int gDelayUpdateScrollInfo = 0; static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0; static bool gColumnFlowSplitEnabled = true; bool RenderBlock::s_canPropagateFloatIntoSibling = false; // 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. class OverflowEventDispatcher { WTF_MAKE_NONCOPYABLE(OverflowEventDispatcher); public: OverflowEventDispatcher(const RenderBlock* block) : m_block(block) , m_hadHorizontalLayoutOverflow(false) , m_hadVerticalLayoutOverflow(false) { 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(); } } ~OverflowEventDispatcher() { if (!m_shouldDispatchEvent) return; bool hasHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow(); bool hasVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow(); 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()); } } private: const RenderBlock* m_block; bool m_shouldDispatchEvent; bool m_hadHorizontalLayoutOverflow; 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(ContainerNode* node) : RenderBox(node) , m_lineHeight(-1) , m_hasMarginBeforeQuirk(false) , m_hasMarginAfterQuirk(false) , m_beingDestroyed(false) , m_hasMarkupTruncation(false) , m_hasBorderOrPaddingLogicalWidthChanged(false) { 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)) { TrackedRendererListHashSet::iterator end = descendantSet->end(); for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { TrackedContainerMap::iterator it = containerMap->find(*descendant); ASSERT(it != containerMap->end()); if (it == containerMap->end()) continue; HashSet* containerSet = it->value.get(); ASSERT(containerSet->contains(block)); containerSet->remove(block); if (containerSet->isEmpty()) containerMap->remove(it); } } } RenderBlock::~RenderBlock() { if (m_floatingObjects) deleteAllValues(m_floatingObjects->set()); if (hasColumns()) gColumnInfoMap->take(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); } 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) { RenderStyle* oldStyle = style(); s_canPropagateFloatIntoSibling = oldStyle ? !isFloatingOrOutOfFlowPositioned() && !avoidsFloats() : false; setReplaced(newStyle->isDisplayInlineType()); 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(); } if (cb->isRenderBlock()) toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock); } if (containsFloats() && !isFloating() && !isOutOfFlowPositioned() && newStyle->hasOutOfFlowPosition()) markAllDescendantsWithFloatsForLayout(); } RenderBox::styleWillChange(diff, newStyle); } static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle) { if (newStyle->isHorizontalWritingMode()) return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth() || oldStyle->borderRightWidth() != newStyle->borderRightWidth() || oldStyle->paddingLeft() != newStyle->paddingLeft() || oldStyle->paddingRight() != newStyle->paddingRight(); return oldStyle->borderTopWidth() != newStyle->borderTopWidth() || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth() || oldStyle->paddingTop() != newStyle->paddingTop() || oldStyle->paddingBottom() != newStyle->paddingBottom(); } void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); RenderStyle* newStyle = style(); #if ENABLE(CSS_SHAPES) updateShapeInsideInfoAfterStyleChange(newStyle->resolvedShapeInside(), oldStyle ? oldStyle->resolvedShapeInside() : 0); #endif if (!isAnonymousBlock()) { // Ensure that all of our continuation blocks pick up the new style. for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { RenderBoxModelObject* nextCont = currCont->continuation(); currCont->setContinuation(0); currCont->setStyle(newStyle); currCont->setContinuation(nextCont); } } 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(); } // 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); } 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) return last; return curr; } nextToLast = last; last = curr; curr = toRenderBlock(curr->continuation()); } if (!beforeChild && !last->firstChild()) return nextToLast; return last; } void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) { RenderBlock* flow = continuationBefore(beforeChild); ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock()); RenderBoxModelObject* beforeChildParent = 0; if (beforeChild) beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); else { RenderBoxModelObject* cont = flow->continuation(); if (cont) beforeChildParent = cont; else beforeChildParent = flow; } if (newChild->isFloatingOrOutOfFlowPositioned()) { beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 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(); if (flow == beforeChildParent) { flow->addChildIgnoringContinuation(newChild, beforeChild); return; } // The goal here is to match up if we can, so that we can coalesce and create the // minimal # of continuations needed for the inline. if (childIsNormal == bcpIsNormal) { beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); return; } if (flowIsNormal == childIsNormal) { flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. return; } 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 { RenderBlock* cloneBlock; if (isAnonymousBlock()) { cloneBlock = createAnonymousBlock(); cloneBlock->setChildrenInline(childrenInline()); } else { RenderObject* cloneRenderer = toElement(node())->createRenderer(renderArena(), style()); cloneBlock = toRenderBlock(cloneRenderer); cloneBlock->setStyle(style()); // This takes care of setting the right value of childrenInline in case // generated content is added to cloneBlock and 'this' does not have // generated content added yet. cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline()); } cloneBlock->setFlowThreadState(flowThreadState()); 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) { 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; } void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) { if (beforeChild && beforeChild->parent() != this) { RenderObject* beforeChildContainer = beforeChild->parent(); while (beforeChildContainer->parent() != this) beforeChildContainer = beforeChildContainer->parent(); ASSERT(beforeChildContainer); 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; if (beforeChildAnonymousContainer->isAnonymousBlock() #if ENABLE(FULLSCREEN_API) // Full screen renderers and full screen placeholders act as anonymous blocks, not tables: || beforeChildAnonymousContainer->isRenderFullScreen() || beforeChildAnonymousContainer->isRenderFullScreenPlaceholder() #endif ) { // Insert the child into the anonymous block box instead of here. if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild) beforeChild->parent()->addChild(newChild, beforeChild); else addChild(newChild, beforeChild->parent()); return; } ASSERT(beforeChildAnonymousContainer->isTable()); if (newChild->isTablePart()) { // Insert into the anonymous table. beforeChildAnonymousContainer->addChild(newChild, beforeChild); return; } beforeChild = splitAnonymousBoxesAroundChild(beforeChild); ASSERT(beforeChild->parent() == this); if (beforeChild->parent() != this) { // We should never reach here. If we do, we need to use the // 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; } } bool madeBoxesNonInline = false; // A block has to either have all of its children inline, or all of its children as blocks. // So, if our children are currently inline and a block child has to be inserted, we move all our // inline children into anonymous block boxes. if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) { // This is a block with inline content. Wrap the inline content in anonymous blocks. makeChildrenNonInline(beforeChild); madeBoxesNonInline = true; if (beforeChild && beforeChild->parent() != this) { beforeChild = beforeChild->parent(); ASSERT(beforeChild->isAnonymousBlock()); ASSERT(beforeChild->parent() == this); } } else if (!childrenInline() && (newChild->isFloatingOrOutOfFlowPositioned() || newChild->isInline())) { // If we're inserting an inline child but all of our children are blocks, then we have to make sure // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise // a new one is created and inserted into our list of children in the appropriate position. RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild(); if (afterChild && afterChild->isAnonymousBlock()) { afterChild->addChild(newChild); return; } if (newChild->isInline()) { // No suitable existing anonymous box - create a new one. RenderBlock* newBox = createAnonymousBlock(); RenderBox::addChild(newBox, beforeChild); newBox->addChild(newChild); return; } } RenderBox::addChild(newChild, beforeChild); // Handle placement of run-ins. placeRunInIfNeeded(newChild); if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) toRenderBlock(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) { // Beginning at |start| we find the largest contiguous run of inlines that // we can. We denote the run with start and end points, |inlineRunStart| // and |inlineRunEnd|. Note that these two values may be the same if // we encounter only one inline. // // We skip any non-inlines we encounter as long as we haven't found any // inlines yet. // // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary| // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered // a non-inline. // Start by skipping as many non-inlines as we can. RenderObject * curr = start; bool sawInline; do { while (curr && !(curr->isInline() || curr->isFloatingOrOutOfFlowPositioned())) curr = curr->nextSibling(); inlineRunStart = inlineRunEnd = curr; if (!curr) return; // No more inline children to be found. sawInline = curr->isInline(); curr = curr->nextSibling(); while (curr && (curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()) && (curr != boundary)) { inlineRunEnd = curr; if (curr->isInline()) sawInline = true; curr = curr->nextSibling(); } } while (!sawInline); } void RenderBlock::deleteLineBoxTree() { 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()) 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) { // makeChildrenNonInline takes a block whose children are *all* inline and it // makes sure that inline children are coalesced under anonymous // blocks. If |insertionPoint| is defined, then it represents the insertion point for // the new block child that is causing us to have to wrap all the inlines. This // means that we cannot coalesce inlines before |insertionPoint| with inlines following // |insertionPoint|, because the new child is going to be inserted in between the inlines, // splitting them. ASSERT(isInlineBlockOrInlineTable() || !isInline()); ASSERT(!insertionPoint || insertionPoint->parent() == this); setChildrenInline(false); 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(); } while (child) { RenderObject *inlineRunStart, *inlineRunEnd; getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd); if (!inlineRunStart) break; child = inlineRunEnd->nextSibling(); RenderBlock* block = createAnonymousBlock(); children()->insertChildNode(this, block, inlineRunStart); moveChildrenTo(block, inlineRunStart, child); } #ifndef NDEBUG for (RenderObject *c = firstChild(); c; c = c->nextSibling()) ASSERT(!c->isInline()); #endif repaint(); } void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) { ASSERT(child->isAnonymousBlock()); ASSERT(!child->childrenInline()); if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock()))) return; RenderObject* firstAnChild = child->m_children.firstChild(); RenderObject* lastAnChild = child->m_children.lastChild(); if (firstAnChild) { RenderObject* o = firstAnChild; while (o) { o->setParent(this); o = o->nextSibling(); } firstAnChild->setPreviousSibling(child->previousSibling()); lastAnChild->setNextSibling(child->nextSibling()); if (child->previousSibling()) child->previousSibling()->setNextSibling(firstAnChild); 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); } else { if (child == m_children.firstChild()) m_children.setFirstChild(child->nextSibling()); if (child == m_children.lastChild()) m_children.setLastChild(child->previousSibling()); if (child->previousSibling()) child->previousSibling()->setNextSibling(child->nextSibling()); if (child->nextSibling()) child->nextSibling()->setPreviousSibling(child->previousSibling()); } child->children()->setFirstChild(0); child->m_next = 0; // Remove all the information in the flow thread associated with the leftover anonymous block. child->removeFromRenderFlowThread(); child->setParent(0); child->setPreviousSibling(0); child->setNextSibling(0); child->destroy(); } static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObject* prev, RenderObject* next) { 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()))) 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()) return false; if ((prev && (prev->isRubyRun() || prev->isRubyBase())) || (next && (next->isRubyRun() || next->isRubyBase()))) 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(); } void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* child) { parent->setNeedsLayoutAndPrefWidthsRecalc(); parent->setChildrenInline(child->childrenInline()); RenderObject* nextSibling = child->nextSibling(); RenderFlowThread* childFlowThread = child->flowThreadContainingBlock(); CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread); RenderBlock* anonBlock = toRenderBlock(parent->children()->removeChildNode(parent, child, child->hasLayer())); anonBlock->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()); } } } 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. if (documentBeingDestroyed()) { RenderBox::removeChild(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(); bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next); if (canMergeAnonymousBlocks && prev && next) { prev->setNeedsLayoutAndPrefWidthsRecalc(); RenderBlock* nextBlock = toRenderBlock(next); RenderBlock* prevBlock = toRenderBlock(prev); if (prev->childrenInline() != next->childrenInline()) { 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); // Cache this value as it might get changed in setStyle() call. bool inlineChildrenBlockHasLayer = inlineChildrenBlock->hasLayer(); inlineChildrenBlock->setStyle(newStyle); children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlockHasLayer); // Now just put the inlineChildrenBlock inside the blockChildrenBlock. blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0, inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer()); 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; else next = 0; } else { // Take all the children out of the |next| block and put them in // the |prev| block. nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); // Delete the now-empty block's lines and nuke it. nextBlock->deleteLineBoxTree(); nextBlock->destroy(); next = 0; } } RenderBox::removeChild(oldChild); RenderObject* child = prev ? prev : next; if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canCollapseAnonymousBlockChild()) { // 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()) { // 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); } } if (!firstChild()) { // If this was our last child be sure to clear out our line boxes. if (childrenInline()) deleteLineBoxTree(); // 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(); while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymousBlock()) containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock(); for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) { if (curr->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); else ASSERT_NOT_REACHED(); break; } setContinuation(0); destroy(); } } } bool RenderBlock::isSelfCollapsingBlock() const { // We are not self-collapsing if we // (a) have a non-zero height according to layout (an optimization to avoid wasting time) // (b) are a table, // (c) have border/padding, // (d) have a min-height // (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) return false; Length logicalHeightLength = style()->logicalHeight(); bool hasAutoHeight = logicalHeightLength.isAuto(); if (logicalHeightLength.isPercent() && !document()->inQuirksMode()) { hasAutoHeight = true; for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); 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; } return false; } void RenderBlock::startDelayUpdateScrollInfo() { if (gDelayUpdateScrollInfo == 0) { ASSERT(!gDelayedUpdateScrollInfoSet); gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet; } ASSERT(gDelayedUpdateScrollInfoSet); ++gDelayUpdateScrollInfo; } void RenderBlock::finishDelayUpdateScrollInfo() { --gDelayUpdateScrollInfo; ASSERT(gDelayUpdateScrollInfo >= 0); if (gDelayUpdateScrollInfo == 0) { ASSERT(gDelayedUpdateScrollInfoSet); OwnPtr infoSet(adoptPtr(gDelayedUpdateScrollInfoSet)); gDelayedUpdateScrollInfoSet = 0; for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) { RenderBlock* block = *it; if (block->hasOverflowClip()) { block->layer()->updateScrollInfoAfterLayout(); block->clearLayoutOverflow(); } } } } 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(); return; } if (gDelayUpdateScrollInfo) gDelayedUpdateScrollInfoSet->add(this); else layer()->updateScrollInfoAfterLayout(); } } void RenderBlock::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; OverflowEventDispatcher dispatcher(this); // Update our first letter info now. updateFirstLetter(); // Table cells call layoutBlock directly, so don't add any logic here. Put code into // layoutBlock(). layoutBlock(false); // 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) clearLayoutOverflow(); invalidateBackgroundObscurationStatus(); } #if ENABLE(CSS_SHAPES) void RenderBlock::updateShapeInsideInfoAfterStyleChange(const ShapeValue* shapeInside, const ShapeValue* oldShapeInside) { // 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(); } } void RenderBlock::markShapeInsideDescendantsForLayout() { 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); } void RenderBlock::computeRegionRangeForBlock(RenderFlowThread* flowThread) { if (flowThread) flowThread->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage()); } bool RenderBlock::updateLogicalWidthAndColumnWidth() { LayoutUnit oldWidth = logicalWidth(); LayoutUnit oldColumnWidth = desiredColumnWidth(); updateLogicalWidth(); calcColumnWidth(); bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged; m_hasBorderOrPaddingLogicalWidthChanged = false; return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged; } void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight) { 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(); } } void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight) { 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); 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); 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); } } } void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats) { m_overflow.clear(); // Add overflow from children. addOverflowFromChildren(); if (!hasColumns() && (recomputeFloats || isRoot() || expandsToEncloseOverhangingFloats() || hasSelfPaintingLayer())) addOverflowFromFloats(); // Add in the overflow from positioned objects. addOverflowFromPositionedObjects(); if (hasOverflowClip()) { // 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 rectToApply; if (isHorizontalWritingMode()) rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, max(0, oldClientAfterEdge - clientRect.y())); else rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 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. addVisualEffectOverflow(); // Add visual overflow from theme. addVisualOverflowFromTheme(); if (isRenderNamedFlowThread()) toRenderNamedFlowThread(this)->computeOversetStateForRegions(oldClientAfterEdge); } void RenderBlock::clearLayoutOverflow() { if (!m_overflow) return; if (visualOverflowRect() == borderBoxRect()) { m_overflow.clear(); return; } m_overflow->setLayoutOverflow(borderBoxRect()); } void RenderBlock::addOverflowFromBlockChildren() { for (RenderBox* 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; // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content. if (positionedObject->style()->position() != FixedPosition) { LayoutUnit x = positionedObject->x(); if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) x -= verticalScrollbarWidth(); addOverflowFromChild(positionedObject, LayoutSize(x, positionedObject->y())); } } } void RenderBlock::addVisualOverflowFromTheme() { if (!style()->hasAppearance()) return; IntRect inflatedRect = pixelSnappedBorderBoxRect(); theme()->adjustRepaintRect(this, inflatedRect); addVisualOverflow(inflatedRect); } bool RenderBlock::expandsToEncloseOverhangingFloats() const { return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated()) || hasColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot(); } void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo) { 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); } } 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(); runIn->destroy(); } void RenderBlock::placeRunInIfNeeded(RenderObject* newChild) { if (newChild->isRunIn()) moveRunInUnderSiblingBlockIfNeeded(newChild); else if (RenderObject* prevSibling = newChild->previousSibling()) { if (prevSibling->isRunIn()) moveRunInUnderSiblingBlockIfNeeded(prevSibling); } } RenderBoxModelObject* RenderBlock::createReplacementRunIn(RenderBoxModelObject* runIn) { 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; } void RenderBlock::moveRunInUnderSiblingBlockIfNeeded(RenderObject* runIn) { ASSERT(runIn->isRunIn()); // 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; // FIXME: We don't handle non-block elements with run-in for now. if (!runIn->isRenderBlock()) 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()) return; // Check if this node is allowed to run-in. E.g.