diff options
author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2013-09-13 12:51:20 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 20:50:05 +0200 |
commit | d441d6f39bb846989d95bcf5caf387b42414718d (patch) | |
tree | e367e64a75991c554930278175d403c072de6bb8 /Source/WebCore/rendering/RenderBlock.cpp | |
parent | 0060b2994c07842f4c59de64b5e3e430525c4b90 (diff) | |
download | qtwebkit-d441d6f39bb846989d95bcf5caf387b42414718d.tar.gz |
Import Qt5x2 branch of QtWebkit for Qt 5.2
Importing a new snapshot of webkit.
Change-Id: I2d01ad12cdc8af8cb015387641120a9d7ea5f10c
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'Source/WebCore/rendering/RenderBlock.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderBlock.cpp | 2107 |
1 files changed, 1321 insertions, 786 deletions
diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp index 058e90431..c1b3fe154 100644 --- a/Source/WebCore/rendering/RenderBlock.cpp +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -27,43 +27,47 @@ #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 "HTMLFormElement.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 "PODFreeListArena.h" #include "Page.h" #include "PaintInfo.h" #include "RenderBoxRegionInfo.h" #include "RenderCombineText.h" #include "RenderDeprecatedFlexibleBox.h" -#include "RenderImage.h" +#include "RenderFlexibleBox.h" #include "RenderInline.h" #include "RenderLayer.h" #include "RenderMarquee.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" -#include "RenderReplica.h" #include "RenderTableCell.h" #include "RenderTextFragment.h" #include "RenderTheme.h" #include "RenderView.h" -#include "Settings.h" #include "SVGTextRunRenderingContext.h" +#include "Settings.h" #include "ShadowRoot.h" #include "TransformState.h" -#include <wtf/StdLibExtras.h> -#if ENABLE(CSS_EXCLUSIONS) -#include "ExclusionShapeInsideInfo.h" +#include <wtf/StackStats.h> +#include <wtf/TemporaryChange.h> + +#if ENABLE(CSS_SHAPES) +#include "ShapeInsideInfo.h" +#include "ShapeOutsideInfo.h" #endif using namespace std; @@ -97,7 +101,7 @@ struct SameSizeAsMarginInfo { LayoutUnit margins[2]; }; -typedef WTF::HashMap<const RenderBox*, ColumnInfo*> ColumnInfoMap; +typedef WTF::HashMap<const RenderBox*, OwnPtr<ColumnInfo> > ColumnInfoMap; static ColumnInfoMap* gColumnInfoMap = 0; static TrackedDescendantsMap* gPositionedDescendantsMap = 0; @@ -106,12 +110,14 @@ static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; static TrackedContainerMap* gPositionedContainerMap = 0; static TrackedContainerMap* gPercentHeightContainerMap = 0; -typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderInline*>*> ContinuationOutlineTableMap; +typedef WTF::HashMap<RenderBlock*, OwnPtr<ListHashSet<RenderInline*> > > ContinuationOutlineTableMap; typedef WTF::HashSet<RenderBlock*> 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 @@ -158,16 +164,17 @@ private: RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, LayoutUnit beforeBorderPadding, LayoutUnit afterBorderPadding) : m_atBeforeSideOfBlock(true) , m_atAfterSideOfBlock(false) - , m_marginBeforeQuirk(false) - , m_marginAfterQuirk(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->isWritingModeRoot() && !block->parent()->isFlexibleBox() && blockStyle->hasAutoColumnCount() && blockStyle->hasAutoColumnWidth() - && !blockStyle->columnSpan(); + && !block->isRenderFlowThread() && !block->isWritingModeRoot() && !block->parent()->isFlexibleBox() + && blockStyle->hasAutoColumnCount() && blockStyle->hasAutoColumnWidth() && !blockStyle->columnSpan(); m_canCollapseMarginBeforeWithChildren = m_canCollapseWithChildren && !beforeBorderPadding && blockStyle->marginBeforeCollapse() != MSEPARATE; @@ -178,20 +185,24 @@ RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, LayoutUnit beforeBorderP m_canCollapseMarginAfterWithChildren = m_canCollapseWithChildren && (afterBorderPadding == 0) && (blockStyle->logicalHeight().isAuto() && !blockStyle->logicalHeight().value()) && blockStyle->marginAfterCollapse() != MSEPARATE; - m_quirkContainer = block->isTableCell() || block->isBody() || blockStyle->marginBeforeCollapse() == MDISCARD - || blockStyle->marginAfterCollapse() == MDISCARD; + m_quirkContainer = block->isTableCell() || block->isBody(); + + m_discardMargin = m_canCollapseMarginBeforeWithChildren && block->mustDiscardMarginBefore(); - m_positiveMargin = m_canCollapseMarginBeforeWithChildren ? block->maxPositiveMarginBefore() : LayoutUnit(); - m_negativeMargin = m_canCollapseMarginBeforeWithChildren ? block->maxNegativeMarginBefore() : LayoutUnit(); + m_positiveMargin = (m_canCollapseMarginBeforeWithChildren && !block->mustDiscardMarginBefore()) ? block->maxPositiveMarginBefore() : LayoutUnit(); + m_negativeMargin = (m_canCollapseMarginBeforeWithChildren && !block->mustDiscardMarginBefore()) ? block->maxNegativeMarginBefore() : LayoutUnit(); } // ------------------------------------------------------------------------------------------------------- -RenderBlock::RenderBlock(Node* node) - : RenderBox(node) - , m_lineHeight(-1) - , m_beingDestroyed(false) - , m_hasMarkupTruncation(false) +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); @@ -200,21 +211,19 @@ RenderBlock::RenderBlock(Node* node) static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap) { - if (TrackedRendererListHashSet* descendantSet = descendantMap->take(block)) { + if (OwnPtr<TrackedRendererListHashSet> descendantSet = descendantMap->take(block)) { TrackedRendererListHashSet::iterator end = descendantSet->end(); for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { - HashSet<RenderBlock*>* containerSet = containerMap->get(*descendant); - ASSERT(containerSet); - if (!containerSet) + TrackedContainerMap::iterator it = containerMap->find(*descendant); + ASSERT(it != containerMap->end()); + if (it == containerMap->end()) continue; + HashSet<RenderBlock*>* containerSet = it->value.get(); ASSERT(containerSet->contains(block)); containerSet->remove(block); - if (containerSet->isEmpty()) { - containerMap->remove(*descendant); - delete containerSet; - } + if (containerSet->isEmpty()) + containerMap->remove(it); } - delete descendantSet; } } @@ -224,7 +233,7 @@ RenderBlock::~RenderBlock() deleteAllValues(m_floatingObjects->set()); if (hasColumns()) - delete gColumnInfoMap->take(this); + gColumnInfoMap->take(this); if (gPercentHeightDescendantsMap) removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); @@ -232,11 +241,23 @@ RenderBlock::~RenderBlock() 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(); @@ -277,10 +298,6 @@ void RenderBlock::willBeDestroyed() if (lineGridBox()) lineGridBox()->destroy(renderArena()); -#if ENABLE(CSS_EXCLUSIONS) - ExclusionShapeInsideInfo::removeExclusionShapeInsideInfoForRenderBlock(this); -#endif - if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0)) gDelayedUpdateScrollInfoSet->remove(this); @@ -322,14 +339,28 @@ void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newSt 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); - -#if ENABLE(CSS_EXCLUSIONS) - // FIXME: Bug 89993: Style changes should affect the ExclusionShapeInsideInfos for other render blocks that - // share the same ExclusionShapeInsideInfo - updateExclusionShapeInsideInfoAfterStyleChange(style()->shapeInside(), oldStyle ? oldStyle->shapeInside() : 0); + + RenderStyle* newStyle = style(); + +#if ENABLE(CSS_SHAPES) + updateShapeInsideInfoAfterStyleChange(newStyle->resolvedShapeInside(), oldStyle ? oldStyle->resolvedShapeInside() : 0); #endif if (!isAnonymousBlock()) { @@ -337,7 +368,7 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { RenderBoxModelObject* nextCont = currCont->continuation(); currCont->setContinuation(0); - currCont->setStyle(style()); + currCont->setStyle(newStyle); currCont->setContinuation(nextCont); } } @@ -345,12 +376,6 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty propagateStyleToAnonymousChildren(true); m_lineHeight = -1; - // Update pseudos for :before and :after now. - if (!isAnonymous() && document()->styleSheetCollection()->usesBeforeAfterRules() && canHaveGeneratedChildren()) { - updateBeforeAfterContent(BEFORE); - updateBeforeAfterContent(AFTER); - } - // 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 @@ -380,14 +405,10 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty parentBlock->markAllDescendantsWithFloatsForLayout(); parentBlock->markSiblingsWithFloatsForLayout(); } -} - -void RenderBlock::updateBeforeAfterContent(PseudoId pseudoId) -{ - // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. - if (parent() && parent()->createsAnonymousWrapper()) - return; - children()->updateBeforeAfterContent(this, pseudoId); + + // 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) @@ -566,7 +587,7 @@ RenderBlock* RenderBlock::clone() const // generated content added yet. cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline()); } - cloneBlock->setInRenderFlowThread(inRenderFlowThread()); + cloneBlock->setFlowThreadState(flowThreadState()); return cloneBlock; } @@ -601,15 +622,9 @@ void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); RenderBoxModelObject* currChild = this; RenderObject* currChildNextSibling = currChild->nextSibling(); - bool documentUsesBeforeAfterRules = document()->styleSheetCollection()->usesBeforeAfterRules(); - // Note: |this| can be destroyed inside this loop if it is an empty anonymous - // block and we try to call updateBeforeAfterContent inside which removes the - // generated content and additionally cleans up |this| empty anonymous block. - // See RenderBlock::removeChild(). DO NOT reference any local variables to |this| - // after this point. while (curr && curr != fromBlock) { - ASSERT(curr->isRenderBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(curr->isRenderBlock()); RenderBlock* blockCurr = toRenderBlock(curr); @@ -630,16 +645,6 @@ void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, cloneBlock->setContinuation(oldCont); } - // Someone may have indirectly caused a <q> to split. When this happens, the :after content - // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after - // content gets properly destroyed. - bool isLastChild = (currChildNextSibling == blockCurr->lastChild()); - if (documentUsesBeforeAfterRules) - blockCurr->children()->updateBeforeAfterContent(blockCurr, AFTER); - if (isLastChild && currChildNextSibling != blockCurr->lastChild()) - currChildNextSibling = 0; // We destroyed the last child, so now we need to update - // the value of currChildNextSibling. - // 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); @@ -798,10 +803,6 @@ RenderBlock* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) { - // Make sure we don't append things after :after-generated content if we have it. - if (!beforeChild) - beforeChild = afterPseudoElementRenderer(); - if (beforeChild && beforeChild->parent() != this) { RenderObject* beforeChildContainer = beforeChild->parent(); while (beforeChildContainer->parent() != this) @@ -857,47 +858,33 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, beforeChild = beforeChild->nextSibling(); // Check for a spanning element in columns. - RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); - if (columnsBlockAncestor) { - // We are placing a column-span element inside a block. - RenderBlock* newBox = createAnonymousColumnSpanBlock(); + if (gColumnFlowSplitEnabled) { + RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); + if (columnsBlockAncestor) { + TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); + // We are placing a column-span element inside a block. + RenderBlock* newBox = createAnonymousColumnSpanBlock(); - if (columnsBlockAncestor != this) { - // 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); - - // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content - // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after - // content gets properly destroyed. - bool isFirstChild = (beforeChild == firstChild()); - bool isLastChild = (beforeChild == lastChild()); - if (document()->styleSheetCollection()->usesBeforeAfterRules()) - children()->updateBeforeAfterContent(this, AFTER); - if (isLastChild && beforeChild != lastChild()) { - // We destroyed the last child, so now we need to update our insertion - // point to be 0. It's just a straight append now. - beforeChild = 0; - } else if (isFirstChild && beforeChild != firstChild()) { - // If beforeChild was the last anonymous block that collapsed, - // then we need to update its value. - beforeChild = firstChild(); + 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; } - splitFlow(beforeChild, newBox, newChild, oldContinuation); + // 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; } - - // 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; @@ -938,7 +925,7 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderBox::addChild(newChild, beforeChild); // Handle placement of run-ins. - placeRunInIfNeeded(newChild, DoNotPlaceGeneratedRunIn); + placeRunInIfNeeded(newChild); if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); @@ -1013,11 +1000,12 @@ void RenderBlock::deleteLineBoxTree() } } m_lineBoxes.deleteLineBoxTree(renderArena()); - if (UNLIKELY(AXObjectCache::accessibilityEnabled())) - document()->axObjectCache()->recomputeIsIgnored(this); + + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->recomputeIsIgnored(this); } -RootInlineBox* RenderBlock::createRootInlineBox() +RootInlineBox* RenderBlock::createRootInlineBox() { return new (renderArena()) RootInlineBox(this); } @@ -1027,8 +1015,10 @@ RootInlineBox* RenderBlock::createAndAppendRootInlineBox() RootInlineBox* rootBox = createRootInlineBox(); m_lineBoxes.appendLineBox(rootBox); - if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox) - document()->axObjectCache()->recomputeIsIgnored(this); + if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox) { + if (AXObjectCache* cache = document()->existingAXObjectCache()) + cache->recomputeIsIgnored(this); + } return rootBox; } @@ -1120,12 +1110,16 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) 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->children()->setFirstChild(0); - child->m_next = 0; child->destroy(); } @@ -1161,7 +1155,7 @@ void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* c parent->setChildrenInline(child->childrenInline()); RenderObject* nextSibling = child->nextSibling(); - RenderFlowThread* childFlowThread = child->enclosingRenderFlowThread(); + RenderFlowThread* childFlowThread = child->flowThreadContainingBlock(); CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread); RenderBlock* anonBlock = toRenderBlock(parent->children()->removeChildNode(parent, child, child->hasLayer())); @@ -1173,6 +1167,46 @@ void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderObject* c 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. @@ -1182,6 +1216,9 @@ void RenderBlock::removeChild(RenderObject* oldChild) return; } + // This protects against column split flows when anonymous blocks are getting merged. + TemporaryChange<bool> 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. @@ -1222,7 +1259,7 @@ void RenderBlock::removeChild(RenderObject* oldChild) } else { // Take all the children out of the |next| block and put them in // the |prev| block. - nextBlock->moveAllChildrenTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); + nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); // Delete the now-empty block's lines and nuke it. nextBlock->deleteLineBoxTree(); @@ -1353,6 +1390,7 @@ void RenderBlock::finishDelayUpdateScrollInfo() RenderBlock* block = *it; if (block->hasOverflowClip()) { block->layer()->updateScrollInfoAfterLayout(); + block->clearLayoutOverflow(); } } } @@ -1391,69 +1429,118 @@ 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) + if (hasControlClip() && m_overflow && !gDelayUpdateScrollInfo) clearLayoutOverflow(); + + invalidateBackgroundObscurationStatus(); } -#if ENABLE(CSS_EXCLUSIONS) -void RenderBlock::updateExclusionShapeInsideInfoAfterStyleChange(const ExclusionShapeValue* shapeInside, const ExclusionShapeValue* oldShapeInside) +#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) { - ExclusionShapeInsideInfo* exclusionShapeInsideInfo = ExclusionShapeInsideInfo::ensureExclusionShapeInsideInfoForRenderBlock(this); - exclusionShapeInsideInfo->dirtyShapeSize(); - } else - ExclusionShapeInsideInfo::removeExclusionShapeInsideInfoForRenderBlock(this); + 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 -void RenderBlock::updateRegionsAndExclusionsLogicalSize() +static inline bool shapeInfoRequiresRelayout(const RenderBlock* block) { -#if ENABLE(CSS_EXCLUSIONS) - if (!inRenderFlowThread() && !exclusionShapeInsideInfo()) +#if !ENABLE(CSS_SHAPES) + return false; #else - if (!inRenderFlowThread()) + ShapeInsideInfo* info = block->shapeInsideInfo(); + if (info) + info->setNeedsLayout(info->shapeSizeDirty()); + else + info = block->layoutShapeInsideInfo(); + return info && info->needsLayout(); #endif - return; +} + +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(LayoutUnit::max() / 2); + setLogicalHeight(RenderFlowThread::maxLogicalHeight()); updateLogicalHeight(); -#if ENABLE(CSS_EXCLUSIONS) - computeExclusionShapeSize(); +#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(); + computeRegionRangeForBlock(flowThread); setLogicalHeight(oldHeight); setLogicalTop(oldTop); + + return shapeInfoRequiresRelayout(this); } -#if ENABLE(CSS_EXCLUSIONS) -void RenderBlock::computeExclusionShapeSize() +#if ENABLE(CSS_SHAPES) +void RenderBlock::computeShapeSize() { - ExclusionShapeInsideInfo* exclusionShapeInsideInfo = this->exclusionShapeInsideInfo(); - if (exclusionShapeInsideInfo) { + ShapeInsideInfo* shapeInsideInfo = this->shapeInsideInfo(); + if (shapeInsideInfo) { bool percentageLogicalHeightResolvable = percentageLogicalHeightIsResolvableFromBlock(this, false); - exclusionShapeInsideInfo->computeShapeSize(logicalWidth(), percentageLogicalHeightResolvable ? logicalHeight() : LayoutUnit()); + shapeInsideInfo->setShapeSize(logicalWidth(), percentageLogicalHeightResolvable ? logicalHeight() : LayoutUnit()); } } #endif -void RenderBlock::computeRegionRangeForBlock() +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 (inRenderFlowThread()) - enclosingRenderFlowThread()->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage()); + if (flowThread) + flowThread->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage()); } bool RenderBlock::updateLogicalWidthAndColumnWidth() @@ -1464,7 +1551,10 @@ bool RenderBlock::updateLogicalWidthAndColumnWidth() updateLogicalWidth(); calcColumnWidth(); - return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth(); + bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged; + m_hasBorderOrPaddingLogicalWidthChanged = false; + + return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged; } void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight) @@ -1475,17 +1565,18 @@ void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalH // 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 = contentLogicalHeight(); + LayoutUnit columnHeight = isRenderView() ? view()->pageOrViewLogicalHeight() : contentLogicalHeight(); if (columnHeight > 0) { pageLogicalHeight = columnHeight; hasSpecifiedPageLogicalHeight = true; } setLogicalHeight(0); } - if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) { - colInfo->setColumnHeight(pageLogicalHeight); + + if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) pageLogicalHeightChanged = true; - } + + colInfo->setColumnHeight(pageLogicalHeight); if (!hasSpecifiedPageLogicalHeight && !pageLogicalHeight) colInfo->clearForcedBreaks(); @@ -1493,7 +1584,7 @@ void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalH 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)->pageLogicalHeightChanged(); + pageLogicalHeightChanged = toRenderFlowThread(this)->pageLogicalSizeChanged(); } } @@ -1512,11 +1603,11 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh if (updateLogicalWidthAndColumnWidth()) relayoutChildren = true; - m_overflow.clear(); - clearFloats(); LayoutUnit previousHeight = logicalHeight(); + // FIXME: should this start out as borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(), + // for consistency with other render classes? setLogicalHeight(0); bool pageLogicalHeightChanged = false; @@ -1527,12 +1618,12 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh RenderStyle* styleToUse = style(); LayoutStateMaintainer statePusher(renderView, this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || styleToUse->isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged, columnInfo()); - if (inRenderFlowThread()) { - // Regions changing widths can force us to relayout our children. - if (logicalWidthChangedInRegions()) - relayoutChildren = true; - } - updateRegionsAndExclusionsLogicalSize(); + // 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 @@ -1548,8 +1639,8 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh if (!isCell) { initMaxMarginValues(); - setMarginBeforeQuirk(styleToUse->marginBefore().quirk()); - setMarginAfterQuirk(styleToUse->marginAfter().quirk()); + setHasMarginBeforeQuirk(styleToUse->hasMarginBeforeQuirk()); + setHasMarginAfterQuirk(styleToUse->hasMarginAfterQuirk()); setPaginationStrut(0); } @@ -1564,16 +1655,22 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom); // Expand our intrinsic height to encompass floats. - LayoutUnit toAdd = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + 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) { @@ -1589,18 +1686,21 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh } } - if (previousHeight != newHeight) + bool heightChanged = (previousHeight != newHeight); + if (heightChanged) relayoutChildren = true; layoutPositionedObjects(relayoutChildren || isRoot()); - computeRegionRangeForBlock(); + 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())); @@ -1652,7 +1752,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeigh repaintRectangle(reflectedRect(repaintRect)); } } - + setNeedsLayout(false); } @@ -1676,6 +1776,8 @@ void RenderBlock::addOverflowFromChildren() void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeFloats) { + m_overflow.clear(); + // Add overflow from children. addOverflowFromChildren(); @@ -1696,16 +1798,39 @@ void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool recomputeF else rectToApply = LayoutRect(clientRect.x(), clientRect.y(), max<LayoutUnit>(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<LayoutUnit>(0, textIndent), clientRect.y(), clientRect.width() - min<LayoutUnit>(0, textIndent), clientRect.height()); + addVisualOverflow(rectToApply); + } + // Add visual overflow from box-shadow and border-image-outset. addVisualEffectOverflow(); // Add visual overflow from theme. addVisualOverflowFromTheme(); - if (isRenderFlowThread()) - enclosingRenderFlowThread()->computeOverflowStateForRegions(oldClientAfterEdge); + 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() @@ -1728,7 +1853,6 @@ void RenderBlock::addOverflowFromFloats() if (r->isDescendant()) addOverflowFromChild(r->m_renderer, IntSize(xPositionForFloatIncludingMargin(r), yPositionForFloatIncludingMargin(r))); } - return; } void RenderBlock::addOverflowFromPositionedObjects() @@ -1764,7 +1888,7 @@ void RenderBlock::addVisualOverflowFromTheme() bool RenderBlock::expandsToEncloseOverhangingFloats() const { - return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isDeprecatedFlexibleBox()) + return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated()) || hasColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot(); } @@ -1774,7 +1898,7 @@ void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marg bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontal); LayoutUnit logicalTop = logicalHeight(); - setStaticInlinePositionForChild(child, logicalTop, startOffsetForContent(logicalTop)); + updateStaticInlinePositionForChild(child, logicalTop); if (!marginInfo.canCollapseWithMarginBefore()) { // Positioned blocks don't collapse margins, so add the margin provided by @@ -1813,34 +1937,6 @@ void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo) setLogicalHeight(logicalHeight() - marginOffset); } -bool RenderBlock::handleSpecialChild(RenderBox* child, const MarginInfo& marginInfo) -{ - // Handle in the given order - return handlePositionedChild(child, marginInfo) - || handleFloatingChild(child, marginInfo); -} - - -bool RenderBlock::handlePositionedChild(RenderBox* child, const MarginInfo& marginInfo) -{ - if (child->isOutOfFlowPositioned()) { - child->containingBlock()->insertPositionedObject(child); - adjustPositionedBlock(child, marginInfo); - return true; - } - return false; -} - -bool RenderBlock::handleFloatingChild(RenderBox* child, const MarginInfo& marginInfo) -{ - if (child->isFloating()) { - insertFloatingObject(child); - adjustFloatingBlock(marginInfo); - return true; - } - return false; -} - static void destroyRunIn(RenderBoxModelObject* runIn) { ASSERT(runIn->isRunIn()); @@ -1858,12 +1954,12 @@ static void destroyRunIn(RenderBoxModelObject* runIn) runIn->destroy(); } -void RenderBlock::placeRunInIfNeeded(RenderObject* newChild, PlaceGeneratedRunInFlag flag) +void RenderBlock::placeRunInIfNeeded(RenderObject* newChild) { - if (newChild->isRunIn() && (flag == PlaceGeneratedRunIn || !newChild->isBeforeOrAfterContent())) + if (newChild->isRunIn()) moveRunInUnderSiblingBlockIfNeeded(newChild); else if (RenderObject* prevSibling = newChild->previousSibling()) { - if (prevSibling->isRunIn() && (flag == PlaceGeneratedRunIn || !newChild->isBeforeOrAfterContent())) + if (prevSibling->isRunIn()) moveRunInUnderSiblingBlockIfNeeded(prevSibling); } } @@ -1871,31 +1967,18 @@ void RenderBlock::placeRunInIfNeeded(RenderObject* newChild, PlaceGeneratedRunIn RenderBoxModelObject* RenderBlock::createReplacementRunIn(RenderBoxModelObject* runIn) { ASSERT(runIn->isRunIn()); + ASSERT(runIn->node()); - // First we destroy any :before/:after content. It will be regenerated by the new run-in. - // Exception is if the run-in itself is generated. - if (runIn->style()->styleType() != BEFORE && runIn->style()->styleType() != AFTER) { - RenderObject* generatedContent; - if (runIn->getCachedPseudoStyle(BEFORE) && (generatedContent = runIn->beforePseudoElementRenderer())) - generatedContent->destroy(); - if (runIn->getCachedPseudoStyle(AFTER) && (generatedContent = runIn->afterPseudoElementRenderer())) - generatedContent->destroy(); - } - - bool newRunInShouldBeBlock = !runIn->isRenderBlock(); - Node* runInNode = runIn->node(); RenderBoxModelObject* newRunIn = 0; - if (newRunInShouldBeBlock) - newRunIn = new (renderArena()) RenderBlock(runInNode ? runInNode : document()); + if (!runIn->isRenderBlock()) + newRunIn = new (renderArena()) RenderBlock(runIn->node()); else - newRunIn = new (renderArena()) RenderInline(runInNode ? runInNode : document()); + newRunIn = new (renderArena()) RenderInline(toElement(runIn->node())); + + runIn->node()->setRenderer(newRunIn); newRunIn->setStyle(runIn->style()); - - runIn->moveAllChildrenTo(newRunIn, true); - // If the run-in had an element, we need to set the new renderer. - if (runInNode) - runInNode->setRenderer(newRunIn); + runIn->moveAllChildrenTo(newRunIn, true); return newRunIn; } @@ -1927,6 +2010,9 @@ void RenderBlock::moveRunInUnderSiblingBlockIfNeeded(RenderObject* runIn) if (!curr || !curr->isRenderBlock() || !curr->childrenInline()) return; + if (toRenderBlock(curr)->beingDestroyed()) + return; + // Per CSS3, "A run-in cannot run in to a block that already starts with a // run-in or that itself is a run-in". if (curr->isRunIn() || (curr->firstChild() && curr->firstChild()->isRunIn())) @@ -1991,6 +2077,13 @@ void RenderBlock::moveRunInToOriginalPosition(RenderObject* runIn) LayoutUnit RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) { + bool childDiscardMarginBefore = mustDiscardMarginBeforeForChild(child); + bool childDiscardMarginAfter = mustDiscardMarginAfterForChild(child); + bool childIsSelfCollapsing = child->isSelfCollapsingBlock(); + + // The child discards the before margin when the the after margin has discard in the case of a self collapsing block. + childDiscardMarginBefore = childDiscardMarginBefore || (childDiscardMarginAfter && childIsSelfCollapsing); + // Get the four margin values for the child and cache them. const MarginValues childMargins = marginValuesForChild(child); @@ -2000,84 +2093,106 @@ LayoutUnit RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo // For self-collapsing blocks, collapse our bottom margins into our // top to get new posTop and negTop values. - if (child->isSelfCollapsingBlock()) { + if (childIsSelfCollapsing) { posTop = max(posTop, childMargins.positiveMarginAfter()); negTop = max(negTop, childMargins.negativeMarginAfter()); } // See if the top margin is quirky. We only care if this child has // margins that will collapse with us. - bool topQuirk = child->isMarginBeforeQuirk() || style()->marginBeforeCollapse() == MDISCARD; + bool topQuirk = hasMarginBeforeQuirk(child); if (marginInfo.canCollapseWithMarginBefore()) { - // This child is collapsing with the top of the - // block. If it has larger margin values, then we need to update - // our own maximal values. - if (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !topQuirk) - setMaxMarginBeforeValues(max(posTop, maxPositiveMarginBefore()), max(negTop, maxNegativeMarginBefore())); - - // The minute any of the margins involved isn't a quirk, don't - // collapse it away, even if the margin is smaller (www.webreference.com - // has an example of this, a <dt> with 0.8em author-specified inside - // a <dl> inside a <td>. - if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) { - setMarginBeforeQuirk(false); - marginInfo.setDeterminedMarginBeforeQuirk(true); - } - - if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && !marginBefore()) - // We have no top margin and our top child has a quirky margin. - // We will pick up this quirky margin and pass it through. - // This deals with the <td><div><p> case. - // Don't do this for a block that split two inlines though. You do - // still apply margins in this case. - setMarginBeforeQuirk(true); + if (!childDiscardMarginBefore && !marginInfo.discardMargin()) { + // This child is collapsing with the top of the + // block. If it has larger margin values, then we need to update + // our own maximal values. + if (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !topQuirk) + setMaxMarginBeforeValues(max(posTop, maxPositiveMarginBefore()), max(negTop, maxNegativeMarginBefore())); + + // The minute any of the margins involved isn't a quirk, don't + // collapse it away, even if the margin is smaller (www.webreference.com + // has an example of this, a <dt> with 0.8em author-specified inside + // a <dl> inside a <td>. + if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) { + setHasMarginBeforeQuirk(false); + marginInfo.setDeterminedMarginBeforeQuirk(true); + } + + if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && !marginBefore()) + // We have no top margin and our top child has a quirky margin. + // We will pick up this quirky margin and pass it through. + // This deals with the <td><div><p> case. + // Don't do this for a block that split two inlines though. You do + // still apply margins in this case. + setHasMarginBeforeQuirk(true); + } else + // The before margin of the container will also discard all the margins it is collapsing with. + setMustDiscardMarginBefore(); + } + + // Once we find a child with discardMarginBefore all the margins collapsing with us must also discard. + if (childDiscardMarginBefore) { + marginInfo.setDiscardMargin(true); + marginInfo.clearMargin(); } if (marginInfo.quirkContainer() && marginInfo.atBeforeSideOfBlock() && (posTop - negTop)) - marginInfo.setMarginBeforeQuirk(topQuirk); + marginInfo.setHasMarginBeforeQuirk(topQuirk); LayoutUnit beforeCollapseLogicalTop = logicalHeight(); LayoutUnit logicalTop = beforeCollapseLogicalTop; - if (child->isSelfCollapsingBlock()) { - // This child has no height. We need to compute our - // position before we collapse the child's margins together, - // so that we can get an accurate position for the zero-height block. - LayoutUnit collapsedBeforePos = max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore()); - LayoutUnit collapsedBeforeNeg = max(marginInfo.negativeMargin(), childMargins.negativeMarginBefore()); - marginInfo.setMargin(collapsedBeforePos, collapsedBeforeNeg); - - // Now collapse the child's margins together, which means examining our - // bottom margin values as well. - marginInfo.setPositiveMarginIfLarger(childMargins.positiveMarginAfter()); - marginInfo.setNegativeMarginIfLarger(childMargins.negativeMarginAfter()); - - if (!marginInfo.canCollapseWithMarginBefore()) - // We need to make sure that the position of the self-collapsing block - // is correct, since it could have overflowing content - // that needs to be positioned correctly (e.g., a block that - // had a specified height of 0 but that actually had subcontent). - logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg; - } - else { - if (child->style()->marginBeforeCollapse() == MSEPARATE) { - setLogicalHeight(logicalHeight() + marginInfo.margin() + marginBeforeForChild(child)); - logicalTop = logicalHeight(); + if (childIsSelfCollapsing) { + // For a self collapsing block both the before and after margins get discarded. The block doesn't contribute anything to the height of the block. + // Also, the child's top position equals the logical height of the container. + if (!childDiscardMarginBefore && !marginInfo.discardMargin()) { + // This child has no height. We need to compute our + // position before we collapse the child's margins together, + // so that we can get an accurate position for the zero-height block. + LayoutUnit collapsedBeforePos = max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore()); + LayoutUnit collapsedBeforeNeg = max(marginInfo.negativeMargin(), childMargins.negativeMarginBefore()); + marginInfo.setMargin(collapsedBeforePos, collapsedBeforeNeg); + + // Now collapse the child's margins together, which means examining our + // bottom margin values as well. + marginInfo.setPositiveMarginIfLarger(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMarginIfLarger(childMargins.negativeMarginAfter()); + + if (!marginInfo.canCollapseWithMarginBefore()) + // We need to make sure that the position of the self-collapsing block + // is correct, since it could have overflowing content + // that needs to be positioned correctly (e.g., a block that + // had a specified height of 0 but that actually had subcontent). + logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg; } - else if (!marginInfo.atBeforeSideOfBlock() || - (!marginInfo.canCollapseMarginBeforeWithChildren() - && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginBeforeQuirk()))) { + } else { + if (mustSeparateMarginBeforeForChild(child)) { + ASSERT(!marginInfo.discardMargin() || (marginInfo.discardMargin() && !marginInfo.margin())); + // If we are at the before side of the block and we collapse, ignore the computed margin + // and just add the child margin to the container height. This will correctly position + // the child inside the container. + LayoutUnit separateMargin = !marginInfo.canCollapseWithMarginBefore() ? marginInfo.margin() : LayoutUnit(0); + setLogicalHeight(logicalHeight() + separateMargin + marginBeforeForChild(child)); + logicalTop = logicalHeight(); + } else if (!marginInfo.discardMargin() && (!marginInfo.atBeforeSideOfBlock() + || (!marginInfo.canCollapseMarginBeforeWithChildren() + && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.hasMarginBeforeQuirk())))) { // We're collapsing with a previous sibling's margins and not // with the top of the block. setLogicalHeight(logicalHeight() + max(marginInfo.positiveMargin(), posTop) - max(marginInfo.negativeMargin(), negTop)); logicalTop = logicalHeight(); } - marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); - marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); + marginInfo.setDiscardMargin(childDiscardMarginAfter); + + if (!marginInfo.discardMargin()) { + marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); + } else + marginInfo.clearMargin(); if (marginInfo.margin()) - marginInfo.setMarginAfterQuirk(child->isMarginAfterQuirk() || style()->marginAfterCollapse() == MDISCARD); + marginInfo.setHasMarginAfterQuirk(hasMarginAfterQuirk(child)); } // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins @@ -2093,12 +2208,15 @@ LayoutUnit RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo // If we have collapsed into a previous sibling and so reduced the height of the parent, ensure any floats that now // overhang from the previous sibling are added to our parent. If the child's previous sibling itself is a float the child will avoid // or clear it anyway, so don't worry about any floating children it may contain. + LayoutUnit oldLogicalHeight = logicalHeight(); + setLogicalHeight(logicalTop); RenderObject* prev = child->previousSibling(); if (prev && prev->isBlockFlow() && !prev->isFloatingOrOutOfFlowPositioned()) { RenderBlock* block = toRenderBlock(prev); if (block->containsFloats() && !block->avoidsFloats() && (block->logicalTop() + block->lowestFloatLogicalBottom()) > logicalTop) addOverhangingFloats(block, false); } + setLogicalHeight(oldLogicalHeight); return logicalTop; } @@ -2110,12 +2228,19 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin return yPos; if (child->isSelfCollapsingBlock()) { + bool childDiscardMargin = mustDiscardMarginBeforeForChild(child) || mustDiscardMarginAfterForChild(child); + // For self-collapsing blocks that clear, they can still collapse their // margins with following siblings. Reset the current margins to represent // the self-collapsing block's margins only. - MarginValues childMargins = marginValuesForChild(child); - marginInfo.setPositiveMargin(max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter())); - marginInfo.setNegativeMargin(max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter())); + // If DISCARD is specified for -webkit-margin-collapse, reset the margin values. + if (!childDiscardMargin) { + MarginValues childMargins = marginValuesForChild(child); + marginInfo.setPositiveMargin(max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter())); + marginInfo.setNegativeMargin(max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter())); + } else + marginInfo.clearMargin(); + marginInfo.setDiscardMargin(childDiscardMargin); // CSS2.1 states: // "If the top and bottom margins of an element with clearance are adjoining, its margins collapse with @@ -2134,7 +2259,8 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin // Move the top of the child box to the bottom of the float ignoring the child's top margin. LayoutUnit collapsedMargin = collapsedMarginBeforeForChild(child); setLogicalHeight(child->logicalTop() - collapsedMargin); - heightIncrease -= collapsedMargin; + // A negative collapsed margin-top value cancels itself out as it has already been factored into |yPos| above. + heightIncrease -= max(LayoutUnit(), collapsedMargin); } else // Increase our height by the amount we had to clear. setLogicalHeight(logicalHeight() + heightIncrease); @@ -2147,6 +2273,9 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin // margins involved. setMaxMarginBeforeValues(oldTopPosMargin, oldTopNegMargin); marginInfo.setAtBeforeSideOfBlock(false); + + // In case the child discarded the before margin of the block we need to reset the mustDiscardMarginBefore flag to the initial value. + setMustDiscardMarginBefore(style()->marginBeforeCollapse() == MDISCARD); } LayoutUnit logicalTop = yPos + heightIncrease; @@ -2158,12 +2287,22 @@ LayoutUnit RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& margin return logicalTop; } -void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& positiveMarginBefore, LayoutUnit& negativeMarginBefore) const +void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& positiveMarginBefore, LayoutUnit& negativeMarginBefore, bool& discardMarginBefore) const { - // FIXME: We should deal with the margin-collapse-* style extensions that prevent collapsing and that discard margins. // Give up if in quirks mode and we're a body/table cell and the top margin of the child box is quirky. - if (document()->inQuirksMode() && child->isMarginBeforeQuirk() && (isTableCell() || isBody())) + // Give up if the child specified -webkit-margin-collapse: separate that prevents collapsing. + // FIXME: Use writing mode independent accessor for marginBeforeCollapse. + if ((document()->inQuirksMode() && hasMarginAfterQuirk(child) && (isTableCell() || isBody())) || child->style()->marginBeforeCollapse() == MSEPARATE) + return; + + // The margins are discarded by a child that specified -webkit-margin-collapse: discard. + // FIXME: Use writing mode independent accessor for marginBeforeCollapse. + if (child->style()->marginBeforeCollapse() == MDISCARD) { + positiveMarginBefore = 0; + negativeMarginBefore = 0; + discardMarginBefore = true; return; + } LayoutUnit beforeChildMargin = marginBeforeForChild(child); positiveMarginBefore = max(positiveMarginBefore, beforeChildMargin); @@ -2176,7 +2315,7 @@ void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& pos if (childBlock->childrenInline() || childBlock->isWritingModeRoot()) return; - MarginInfo childMarginInfo(childBlock, childBlock->borderBefore() + childBlock->paddingBefore(), childBlock->borderAfter() + childBlock->paddingAfter()); + MarginInfo childMarginInfo(childBlock, childBlock->borderAndPaddingBefore(), childBlock->borderAndPaddingAfter()); if (!childMarginInfo.canCollapseMarginBeforeWithChildren()) return; @@ -2193,12 +2332,15 @@ void RenderBlock::marginBeforeEstimateForChild(RenderBox* child, LayoutUnit& pos // Make sure to update the block margins now for the grandchild box so that we're looking at current values. if (grandchildBox->needsLayout()) { grandchildBox->computeAndSetBlockDirectionMargins(this); - grandchildBox->setMarginBeforeQuirk(grandchildBox->style()->marginBefore().quirk()); - grandchildBox->setMarginAfterQuirk(grandchildBox->style()->marginAfter().quirk()); + if (grandchildBox->isRenderBlock()) { + RenderBlock* grandchildBlock = toRenderBlock(grandchildBox); + grandchildBlock->setHasMarginBeforeQuirk(grandchildBox->style()->hasMarginBeforeQuirk()); + grandchildBlock->setHasMarginAfterQuirk(grandchildBox->style()->hasMarginAfterQuirk()); + } } // Collapse the margin of the grandchild box with our own to produce an estimate. - childBlock->marginBeforeEstimateForChild(grandchildBox, positiveMarginBefore, negativeMarginBefore); + childBlock->marginBeforeEstimateForChild(grandchildBox, positiveMarginBefore, negativeMarginBefore, discardMarginBefore); } LayoutUnit RenderBlock::estimateLogicalTopPosition(RenderBox* child, const MarginInfo& marginInfo, LayoutUnit& estimateWithoutPagination) @@ -2209,19 +2351,22 @@ LayoutUnit RenderBlock::estimateLogicalTopPosition(RenderBox* child, const Margi if (!marginInfo.canCollapseWithMarginBefore()) { LayoutUnit positiveMarginBefore = 0; LayoutUnit negativeMarginBefore = 0; + bool discardMarginBefore = false; if (child->selfNeedsLayout()) { // Try to do a basic estimation of how the collapse is going to go. - marginBeforeEstimateForChild(child, positiveMarginBefore, negativeMarginBefore); + marginBeforeEstimateForChild(child, positiveMarginBefore, negativeMarginBefore, discardMarginBefore); } else { // Use the cached collapsed margin values from a previous layout. Most of the time they // will be right. MarginValues marginValues = marginValuesForChild(child); positiveMarginBefore = max(positiveMarginBefore, marginValues.positiveMarginBefore()); negativeMarginBefore = max(negativeMarginBefore, marginValues.negativeMarginBefore()); + discardMarginBefore = mustDiscardMarginBeforeForChild(child); } // Collapse the result with our current margins. - logicalTopEstimate += max(marginInfo.positiveMargin(), positiveMarginBefore) - max(marginInfo.negativeMargin(), negativeMarginBefore); + if (!discardMarginBefore) + logicalTopEstimate += max(marginInfo.positiveMargin(), positiveMarginBefore) - max(marginInfo.negativeMargin(), negativeMarginBefore); } // Adjust logicalTopEstimate down to the next page if the margins are so large that we don't fit on the current @@ -2249,10 +2394,9 @@ LayoutUnit RenderBlock::estimateLogicalTopPosition(RenderBox* child, const Margi return logicalTopEstimate; } -LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, - RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) +LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, RenderRegion* region) { - LayoutUnit startPosition = startOffsetForContent(region, offsetFromLogicalTopOfFirstPage); + LayoutUnit startPosition = startOffsetForContent(region); // Add in our start margin. LayoutUnit oldPosition = startPosition + childMarginStart; @@ -2260,9 +2404,9 @@ LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const Re LayoutUnit blockOffset = logicalTopForChild(child); if (region) - blockOffset = max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage)); + blockOffset = max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage())); - LayoutUnit startOff = startOffsetForLine(blockOffset, false, region, offsetFromLogicalTopOfFirstPage, logicalHeightForChild(child)); + LayoutUnit startOff = startOffsetForLine(blockOffset, false, region, logicalHeightForChild(child)); if (style()->textAlign() != WEBKIT_CENTER && !child->style()->marginStartUsing(style()).isAuto()) { if (childMarginStart < 0) @@ -2274,7 +2418,7 @@ LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const Re return newPosition - oldPosition; } -void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child) +void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child, ApplyLayoutDeltaMode applyDelta) { LayoutUnit startPosition = borderStart() + paddingStart(); if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) @@ -2287,27 +2431,34 @@ void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child) // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need // to shift over as necessary to dodge any floats that might get in the way. - if (child->avoidsFloats() && containsFloats() && !inRenderFlowThread()) + if (child->avoidsFloats() && containsFloats() && !flowThreadContainingBlock()) newPosition += computeStartPositionDeltaForChildAvoidingFloats(child, marginStartForChild(child)); - setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), ApplyLayoutDelta); + setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), applyDelta); } void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) { if (marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()) { + // Update the after side margin of the container to discard if the after margin of the last child also discards and we collapse with it. + // Don't update the max margin values because we won't need them anyway. + if (marginInfo.discardMargin()) { + setMustDiscardMarginAfter(); + return; + } + // Update our max pos/neg bottom margins, since we collapsed our bottom margins // with our children. setMaxMarginAfterValues(max(maxPositiveMarginAfter(), marginInfo.positiveMargin()), max(maxNegativeMarginAfter(), marginInfo.negativeMargin())); - if (!marginInfo.marginAfterQuirk()) - setMarginAfterQuirk(false); + if (!marginInfo.hasMarginAfterQuirk()) + setHasMarginAfterQuirk(false); - if (marginInfo.marginAfterQuirk() && marginAfter() == 0) + if (marginInfo.hasMarginAfterQuirk() && !marginAfter()) // We have no bottom margin and our last child has a quirky margin. // We will pick up this quirky margin and pass it through. // This deals with the <td><div><p> case. - setMarginAfterQuirk(true); + setHasMarginAfterQuirk(true); } } @@ -2316,11 +2467,8 @@ void RenderBlock::handleAfterSideOfBlock(LayoutUnit beforeSide, LayoutUnit after marginInfo.setAtAfterSideOfBlock(true); // If we can't collapse with children then go ahead and add in the bottom margin. - // Don't do this for ordinary anonymous blocks as only the enclosing box should add in - // its margin. - if (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore() - && (!isAnonymousBlock() || isAnonymousColumnsBlock() || isAnonymousColumnSpanBlock()) - && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginAfterQuirk())) + if (!marginInfo.discardMargin() && (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore() + && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.hasMarginAfterQuirk()))) setLogicalHeight(logicalHeight() + marginInfo.margin()); // Now add in our bottom border/padding. @@ -2360,28 +2508,55 @@ void RenderBlock::setLogicalTopForChild(RenderBox* child, LayoutUnit logicalTop, } } -void RenderBlock::layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloatLogicalBottom) +void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox* child) { - if (gPercentHeightDescendantsMap) { - if (TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this)) { - TrackedRendererListHashSet::iterator end = descendants->end(); - for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) { - RenderBox* box = *it; - while (box != this) { - if (box->normalChildNeedsLayout()) - break; - box->setChildNeedsLayout(true, MarkOnlyThis); - box = box->containingBlock(); - ASSERT(box); - if (!box) - break; - } - } + // 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->hasViewportPercentageLogicalHeight()) + child->setChildNeedsLayout(true, MarkOnlyThis); + + // 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); +} + +void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants() +{ + if (!gPercentHeightDescendantsMap) + return; + + TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this); + if (!descendants) + return; + + TrackedRendererListHashSet::iterator end = descendants->end(); + for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) { + RenderBox* box = *it; + while (box != this) { + if (box->normalChildNeedsLayout()) + break; + box->setChildNeedsLayout(true, MarkOnlyThis); + + // If the width of an image is affected by the height of a child (e.g., an image with an aspect ratio), + // then we have to dirty preferred widths, since even enclosing blocks can become dirty as a result. + // (A horizontal flexbox that contains an inline image wrapped in an anonymous block for example.) + if (box->hasAspectRatio()) + box->setPreferredLogicalWidthsDirty(true); + + box = box->containingBlock(); + ASSERT(box); + if (!box) + break; } } +} + +void RenderBlock::layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloatLogicalBottom) +{ + dirtyForLayoutFromPercentageHeightDescendants(); - LayoutUnit beforeEdge = borderBefore() + paddingBefore(); - LayoutUnit afterEdge = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + LayoutUnit beforeEdge = borderAndPaddingBefore(); + LayoutUnit afterEdge = borderAndPaddingAfter() + scrollbarLogicalHeight(); setLogicalHeight(beforeEdge); @@ -2409,20 +2584,18 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, LayoutUnit& maxFloa if (childToExclude == child) continue; // Skip this child, since it will be positioned by the specialized subclass (fieldsets and ruby runs). - // Make sure we layout children if they need it. - // 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(true, MarkOnlyThis); - - // 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); + updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child); - // Handle the four types of special elements first. These include positioned content, floating content, compacts and - // run-ins. When we encounter these four types of objects, we don't actually lay them out as normal flow blocks. - if (handleSpecialChild(child, marginInfo)) + if (child->isOutOfFlowPositioned()) { + child->containingBlock()->insertPositionedObject(child); + adjustPositionedBlock(child, marginInfo); continue; + } + if (child->isFloating()) { + insertFloatingObject(child); + adjustFloatingBlock(marginInfo); + continue; + } // Lay out the child. layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom); @@ -2441,13 +2614,6 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay // The child is a normal flow object. Compute the margins we will use for collapsing now. child->computeAndSetBlockDirectionMargins(this); - // Do not allow a collapse if the margin-before-collapse style is set to SEPARATE. - RenderStyle* childStyle = child->style(); - if (childStyle->marginBeforeCollapse() == MSEPARATE) { - marginInfo.setAtBeforeSideOfBlock(false); - marginInfo.clearMargin(); - } - // Try to guess our correct logical top position. In most cases this guess will // be correct. Only if we're wrong (when we compute the real logical top position) // will we have to potentially relayout. @@ -2468,6 +2634,14 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay bool markDescendantsWithFloats = false; if (logicalTopEstimate != oldLogicalTop && !child->avoidsFloats() && childRenderBlock && childRenderBlock->containsFloats()) markDescendantsWithFloats = true; +#if ENABLE(SUBPIXEL_LAYOUT) + else if (UNLIKELY(logicalTopEstimate.mightBeSaturated())) + // logicalTopEstimate, returned by estimateLogicalTopPosition, might be saturated for + // very large elements. If it does the comparison with oldLogicalTop might yield a + // false negative as adding and removing margins, borders etc from a saturated number + // might yield incorrect results. If this is the case always mark for layout. + markDescendantsWithFloats = true; +#endif else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { // If an element might be affected by the presence of floats, then always mark it for // layout. @@ -2511,7 +2685,7 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay // Now we have a final top position. See if it really does end up being different from our estimate. // clearFloatsIfNeeded can also mark the child as needing a layout even though we didn't move. This happens // when collapseMargins dynamically adds overhanging floats because of a child with negative margins. - if (logicalTopAfterClear != logicalTopEstimate || child->needsLayout()) { + if (logicalTopAfterClear != logicalTopEstimate || child->needsLayout() || (paginated && childRenderBlock && childRenderBlock->shouldBreakAtLineToAvoidWidow())) { if (child->shrinkToAvoidFloats()) { // The child's width depends on the line width. // When the child shifts to clear an item, its width can @@ -2537,11 +2711,11 @@ void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, Lay marginInfo.setAtBeforeSideOfBlock(false); // Now place the child in the correct left position - determineLogicalLeftPositionForChild(child); + determineLogicalLeftPositionForChild(child, ApplyLayoutDelta); // Update our height now that the child has been placed in the correct position. setLogicalHeight(logicalHeight() + logicalHeightForChild(child)); - if (childStyle->marginAfterCollapse() == MSEPARATE) { + if (mustSeparateMarginAfterForChild(child)) { setLogicalHeight(logicalHeight() + marginAfterForChild(child)); marginInfo.clearMargin(); } @@ -2621,16 +2795,23 @@ bool RenderBlock::simplifiedLayout() simplifiedNormalFlowLayout(); // Lay out our positioned objects if our positioned child bit is set. - if (posChildNeedsLayout()) - layoutPositionedObjects(false); + // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position + // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the + // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them + // are statically positioned and thus need to move with their absolute ancestors. + bool canContainFixedPosObjects = canContainFixedPositionObjects(); + if (posChildNeedsLayout() || canContainFixedPosObjects) + layoutPositionedObjects(false, !posChildNeedsLayout() && canContainFixedPosObjects); // Recompute our overflow information. // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only // updating our overflow if we either used to have overflow or if the new temporary object has overflow. // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and // lowestPosition on every relayout so it's not a regression. - m_overflow.clear(); - computeOverflow(clientLogicalBottom(), true); + // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during + // simplifiedLayout, we cache the value in m_overflow. + LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); + computeOverflow(oldClientAfterEdge, true); statePusher.pop(); @@ -2642,7 +2823,38 @@ bool RenderBlock::simplifiedLayout() return true; } -void RenderBlock::layoutPositionedObjects(bool relayoutChildren) +void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject* child) +{ + if (child->style()->position() != FixedPosition) + return; + + bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontalWritingMode()); + bool hasStaticInlinePosition = child->style()->hasStaticInlinePosition(isHorizontalWritingMode()); + if (!hasStaticBlockPosition && !hasStaticInlinePosition) + return; + + RenderObject* o = child->parent(); + while (o && !o->isRenderView() && o->style()->position() != AbsolutePosition) + o = o->parent(); + if (o->style()->position() != AbsolutePosition) + return; + + RenderBox* box = toRenderBox(child); + if (hasStaticInlinePosition) { + LogicalExtentComputedValues computedValues; + box->computeLogicalWidthInRegion(computedValues); + LayoutUnit newLeft = computedValues.m_position; + if (newLeft != box->logicalLeft()) + child->setChildNeedsLayout(true, MarkOnlyThis); + } else if (hasStaticBlockPosition) { + LayoutUnit oldTop = box->logicalTop(); + box->updateLogicalHeight(); + if (box->logicalTop() != oldTop) + child->setChildNeedsLayout(true, MarkOnlyThis); + } +} + +void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly) { TrackedRendererListHashSet* positionedDescendants = positionedObjects(); if (!positionedDescendants) @@ -2655,6 +2867,16 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren) TrackedRendererListHashSet::iterator end = positionedDescendants->end(); for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { r = *it; + + // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So + // if this is a fixed position element, mark it for layout if it has an abspos ancestor and needs to move with that ancestor, i.e. + // it has static position. + markFixedPositionObjectForLayoutIfNeeded(r); + if (fixedPositionObjectsOnly) { + r->layoutIfNeeded(); + continue; + } + // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is @@ -2718,7 +2940,7 @@ void RenderBlock::markForPaginationRelayoutIfNeeded() if (needsLayout()) return; - if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset())) + if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset()) || shouldBreakAtLineToAvoidWidow()) setChildNeedsLayout(true, MarkOnlyThis); } @@ -2741,7 +2963,7 @@ void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) // condition is replaced with being a descendant of us. if (logicalBottomForFloat(r) > logicalHeight() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->shouldPaint()) && !r->m_renderer->hasSelfPaintingLayer()) { r->m_renderer->repaint(); - r->m_renderer->repaintOverhangingFloats(); + r->m_renderer->repaintOverhangingFloats(false); } } } @@ -2772,7 +2994,7 @@ void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with // z-index. We paint after we painted the background/border, so that the scrollbars will // sit above the background/border. - if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this)) + if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this) && !paintInfo.paintRootBackgroundOnly()) layer()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect); } @@ -2786,7 +3008,7 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain EBorderStyle ruleStyle = style()->columnRuleStyle(); LayoutUnit ruleThickness = style()->columnRuleWidth(); LayoutUnit colGap = columnGap(); - bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent && ruleThickness <= colGap; + bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; if (!renderRule) return; @@ -2831,10 +3053,10 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain bool topToBottom = !style()->isFlippedBlocksWritingMode() ^ colInfo->progressionIsReversed(); LayoutUnit ruleLeft = isHorizontalWritingMode() ? borderLeft() + paddingLeft() - : colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter()); + : colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderAndPaddingBefore() : borderAndPaddingAfter()); LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness; LayoutUnit ruleTop = isHorizontalWritingMode() - ? colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter()) + ? colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderAndPaddingBefore() : borderAndPaddingAfter()) : borderStart() + paddingStart(); LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight(); LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); @@ -2864,6 +3086,36 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain } } +LayoutUnit RenderBlock::initialBlockOffsetForPainting() const +{ + ColumnInfo* colInfo = columnInfo(); + LayoutUnit result = 0; + if (colInfo->progressionAxis() == ColumnInfo::BlockAxis && colInfo->progressionIsReversed()) { + LayoutRect colRect = columnRectAt(colInfo, 0); + result = isHorizontalWritingMode() ? colRect.y() : colRect.x(); + result -= borderAndPaddingBefore(); + if (style()->isFlippedBlocksWritingMode()) + result = -result; + } + return result; +} + +LayoutUnit RenderBlock::blockDeltaForPaintingNextColumn() const +{ + ColumnInfo* colInfo = columnInfo(); + LayoutUnit blockDelta = -colInfo->columnHeight(); + LayoutUnit colGap = columnGap(); + if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) { + if (!colInfo->progressionIsReversed()) + blockDelta = colGap; + else + blockDelta -= (colInfo->columnHeight() + colGap); + } + if (style()->isFlippedBlocksWritingMode()) + blockDelta = -blockDelta; + return blockDelta; +} + void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats) { // We need to do multiple passes, breaking up our child painting into strips. @@ -2872,20 +3124,16 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& p unsigned colCount = columnCount(colInfo); if (!colCount) return; - LayoutUnit currLogicalTopOffset = 0; LayoutUnit colGap = columnGap(); + LayoutUnit currLogicalTopOffset = initialBlockOffsetForPainting(); + LayoutUnit blockDelta = blockDeltaForPaintingNextColumn(); for (unsigned i = 0; i < colCount; i++) { // For each rect, we clip to the rect, and then we adjust our coords. LayoutRect colRect = columnRectAt(colInfo, i); flipForWritingMode(colRect); + LayoutUnit logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent(); LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset); - if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) { - if (isHorizontalWritingMode()) - offset.expand(0, colRect.y() - borderTop() - paddingTop()); - else - offset.expand(colRect.x() - borderLeft() - paddingLeft(), 0); - } colRect.moveBy(paintOffset); PaintInfo info(paintInfo); info.rect.intersect(pixelSnappedIntRect(colRect)); @@ -2913,12 +3161,7 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& p else paintContents(info, adjustedPaintOffset); } - - LayoutUnit blockDelta = (isHorizontalWritingMode() ? colRect.height() : colRect.width()); - if (style()->isFlippedBlocksWritingMode()) - currLogicalTopOffset += blockDelta; - else - currLogicalTopOffset -= blockDelta; + currLogicalTopOffset += blockDelta; } } @@ -2939,7 +3182,7 @@ void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOf // We don't paint our own background, but we do let the kids paint their backgrounds. PaintInfo paintInfoForChild(paintInfo); paintInfoForChild.phase = newPhase; - paintInfoForChild.updatePaintingRootForChildren(this); + paintInfoForChild.updateSubtreePaintRootForChildren(this); // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit // NSViews. Do not add any more code for this. @@ -3026,7 +3269,7 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) { if (hasBoxDecorations()) paintBoxDecorations(paintInfo, paintOffset); - if (hasColumns()) + if (hasColumns() && !paintInfo.paintRootBackgroundOnly()) paintColumnRules(paintInfo, paintOffset); } @@ -3036,7 +3279,7 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs } // We're done. We don't bother painting any children. - if (paintPhase == PaintPhaseBlockBackground) + if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly()) return; // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div). @@ -3068,15 +3311,12 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs // 5. paint outline. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) - paintOutline(paintInfo.context, LayoutRect(paintOffset, size())); + paintOutline(paintInfo, LayoutRect(paintOffset, size())); // 6. paint continuation outlines. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { RenderInline* inlineCont = inlineElementContinuation(); - // FIXME: For now, do not add continuations for outline painting by our containing block if we are a relative positioned - // anonymous block (i.e. have our own layer). This is because a block depends on renderers in its continuation table being - // in the same layer. - if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE && !hasLayer()) { + if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE) { RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer()); RenderBlock* cb = containingBlock(); @@ -3088,10 +3328,13 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs } } - if (!inlineEnclosedInSelfPaintingLayer) + // Do not add continuations for outline painting by our containing block if we are a relative positioned + // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being + // in the same layer. + if (!inlineEnclosedInSelfPaintingLayer && !hasLayer()) cb->addContinuationWithOutline(inlineRenderer); - else if (!inlineRenderer->firstLineBox()) - inlineRenderer->paintOutline(paintInfo.context, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location()); + else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer())) + inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location()); } paintContinuationOutlines(paintInfo, paintOffset); } @@ -3115,7 +3358,7 @@ LayoutPoint RenderBlock::flipFloatForWritingModeForChild(const FloatingObject* c // case. if (isHorizontalWritingMode()) return LayoutPoint(point.x(), point.y() + height() - child->renderer()->height() - 2 * yPositionForFloatIncludingMargin(child)); - return LayoutPoint(point.x() + width() - child->width() - 2 * xPositionForFloatIncludingMargin(child), point.y()); + return LayoutPoint(point.x() + width() - child->renderer()->width() - 2 * xPositionForFloatIncludingMargin(child), point.y()); } void RenderBlock::paintFloats(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool preservePhase) @@ -3180,7 +3423,7 @@ void RenderBlock::addContinuationWithOutline(RenderInline* flow) ListHashSet<RenderInline*>* continuations = table->get(this); if (!continuations) { continuations = new ListHashSet<RenderInline*>; - table->set(this, continuations); + table->set(this, adoptPtr(continuations)); } continuations->add(flow); @@ -3205,7 +3448,7 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& if (table->isEmpty()) return; - ListHashSet<RenderInline*>* continuations = table->get(this); + OwnPtr<ListHashSet<RenderInline*> > continuations = table->take(this); if (!continuations) return; @@ -3219,12 +3462,8 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& for ( ; block && block != this; block = block->containingBlock()) accumulatedPaintOffset.moveBy(block->location()); ASSERT(block); - flow->paintOutline(info.context, accumulatedPaintOffset); + flow->paintOutline(info, accumulatedPaintOffset); } - - // Delete - delete continuations; - table->remove(this); } bool RenderBlock::shouldPaintSelectionGaps() const @@ -3234,8 +3473,9 @@ bool RenderBlock::shouldPaintSelectionGaps() const bool RenderBlock::isSelectionRoot() const { - if (!node()) + if (isPseudoElement()) return false; + ASSERT(node() || isAnonymous()); // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. if (isTable()) @@ -3264,30 +3504,31 @@ GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* if (!shouldPaintSelectionGaps()) return GapRects(); - // FIXME: this is broken with transforms TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); - mapLocalToContainer(repaintContainer, transformState); + mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip | UseTransforms); LayoutPoint offsetFromRepaintContainer = roundedLayoutPoint(transformState.mappedPoint()); if (hasOverflowClip()) offsetFromRepaintContainer -= scrolledContentOffset(); + LogicalSelectionOffsetCaches cache(this); LayoutUnit lastTop = 0; - LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); - LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); + LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop, cache); + LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop, cache); - return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight); + return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight, cache); } void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) { + LogicalSelectionOffsetCaches cache(this); LayoutUnit lastTop = 0; - LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); - LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); + LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop, cache); + LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop, cache); GraphicsContextStateSaver stateSaver(*paintInfo.context); - LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo); + LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, cache, &paintInfo); if (!gapRectsBounds.isEmpty()) { if (RenderLayer* layer = enclosingLayer()) { gapRectsBounds.moveBy(-paintOffset); @@ -3339,7 +3580,7 @@ LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPh } GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore. // Clip out floating and positioned objects when painting selection gaps. @@ -3376,25 +3617,27 @@ GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& r if (hasColumns() || hasTransform() || style()->columnSpan()) { // FIXME: We should learn how to gap fill multiple columns and transforms eventually. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache); return result; } if (childrenInline()) - result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); else - result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. - if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - logicalHeight(), paintInfo)); + if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) { + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, + lastLogicalTop, lastLogicalLeft, lastLogicalRight, logicalHeight(), cache, paintInfo)); + } + return result; } GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { GapRects result; @@ -3405,8 +3648,8 @@ GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPo // Go ahead and update our lastLogicalTop to be the bottom of the block. <hr>s or empty blocks with height can trip this // case. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache); } return result; } @@ -3422,15 +3665,14 @@ GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPo if (!containsStart && !lastSelectedLine && selectionState() != SelectionStart && selectionState() != SelectionBoth) - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - selTop, paintInfo)); + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, selTop, cache, paintInfo)); LayoutRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight); logicalRect.move(isHorizontalWritingMode() ? offsetFromRootBlock : offsetFromRootBlock.transposedSize()); LayoutRect physicalRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); if (!paintInfo || (isHorizontalWritingMode() && physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y()) || (!isHorizontalWritingMode() && physicalRect.x() < paintInfo->rect.maxX() && physicalRect.maxX() > paintInfo->rect.x())) - result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, paintInfo)); + result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, cache, paintInfo)); lastSelectedLine = curr; } @@ -3442,20 +3684,25 @@ GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPo if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { // Go ahead and update our lastY to be the bottom of the last selected line. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + lastSelectedLine->selectionBottom(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom(), cache); } return result; } GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) + LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { GapRects result; // Go ahead and jump right to the first block child that contains some selected objects. RenderBox* curr; for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { } + + if (!curr) + return result; + + LogicalSelectionOffsetCaches childCache(this, cache); for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) { SelectionState childState = curr->selectionState(); @@ -3477,10 +3724,11 @@ GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoi bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); if (fillBlockGaps) { // We need to fill the vertical gap above this object. - if (childState == SelectionEnd || childState == SelectionInside) + if (childState == SelectionEnd || childState == SelectionInside) { // Fill the gap above the object. - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - curr->logicalTop(), paintInfo)); + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, + lastLogicalTop, lastLogicalLeft, lastLogicalRight, curr->logicalTop(), cache, paintInfo)); + } // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past* // our object. We know this if the selection did not end inside our object. @@ -3492,26 +3740,27 @@ GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoi getSelectionGapInfo(childState, leftGap, rightGap); if (leftGap) - result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); + result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo)); if (rightGap) - result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); + result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo)); // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as // they can without bumping into floating or positioned objects. Ideally they will go right up // to the border of the root selection block. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom()); - } else if (childState != SelectionNone) + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom(), cache); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom(), cache); + } else if (childState != SelectionNone) { // We must be a block that has some selected object inside it. Go ahead and recur. result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), - lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo)); + lastLogicalTop, lastLogicalLeft, lastLogicalRight, childCache, paintInfo)); + } } return result; } LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo) + LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit logicalTop = lastLogicalTop; LayoutUnit logicalHeight = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalBottom - logicalTop; @@ -3519,8 +3768,8 @@ LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPo return LayoutRect(); // Get the selection offsets for the bottom of the gap - LayoutUnit logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom)); - LayoutUnit logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom)); + LayoutUnit logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom, cache)); + LayoutUnit logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom, cache)); LayoutUnit logicalWidth = logicalRight - logicalLeft; if (logicalWidth <= 0) return LayoutRect(); @@ -3532,11 +3781,12 @@ LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPo } LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) + RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; - LayoutUnit rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)); - LayoutUnit rootBlockLogicalRight = min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalLeft), min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight))); + LayoutUnit rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); + LayoutUnit rootBlockLogicalRight = min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalLeft), + min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; if (rootBlockLogicalWidth <= 0) return LayoutRect(); @@ -3548,11 +3798,12 @@ LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const La } LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) + RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; - LayoutUnit rootBlockLogicalLeft = max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalRight), max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight))); - LayoutUnit rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)); + LayoutUnit rootBlockLogicalLeft = max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalRight), + max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); + LayoutUnit rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; if (rootBlockLogicalWidth <= 0) return LayoutRect(); @@ -3574,37 +3825,45 @@ void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& (state == RenderObject::SelectionEnd && !ltr); } -LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) +LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) { LayoutUnit logicalLeft = logicalLeftOffsetForLine(position, false); if (logicalLeft == logicalLeftOffsetForContent()) { - if (rootBlock != this) - // The border can potentially be further extended by our containingBlock(). - return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop()); + if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). + return cache.containingBlockInfo(this).logicalLeftSelectionOffset(rootBlock, position + logicalTop()); return logicalLeft; } else { RenderBlock* cb = this; + const LogicalSelectionOffsetCaches* currentCache = &cache; while (cb != rootBlock) { logicalLeft += cb->logicalLeft(); - cb = cb->containingBlock(); + + ASSERT(currentCache); + const LogicalSelectionOffsetCaches::ContainingBlockInfo& info = currentCache->containingBlockInfo(cb); + cb = info.block(); + currentCache = info.cache(); } } return logicalLeft; } -LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) +LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) { LayoutUnit logicalRight = logicalRightOffsetForLine(position, false); if (logicalRight == logicalRightOffsetForContent()) { - if (rootBlock != this) - // The border can potentially be further extended by our containingBlock(). - return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop()); + if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). + return cache.containingBlockInfo(this).logicalRightSelectionOffset(rootBlock, position + logicalTop()); return logicalRight; } else { RenderBlock* cb = this; + const LogicalSelectionOffsetCaches* currentCache = &cache; while (cb != rootBlock) { logicalRight += cb->logicalLeft(); - cb = cb->containingBlock(); + + ASSERT(currentCache); + const LogicalSelectionOffsetCaches::ContainingBlockInfo& info = currentCache->containingBlockInfo(cb); + cb = info.block(); + currentCache = info.cache(); } } return logicalRight; @@ -3652,7 +3911,7 @@ void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDe TrackedRendererListHashSet* descendantSet = descendantsMap->get(this); if (!descendantSet) { descendantSet = new TrackedRendererListHashSet; - descendantsMap->set(this, descendantSet); + descendantsMap->set(this, adoptPtr(descendantSet)); } bool added = descendantSet->add(descendant).isNewEntry; if (!added) { @@ -3664,7 +3923,7 @@ void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDe HashSet<RenderBlock*>* containerSet = containerMap->get(descendant); if (!containerSet) { containerSet = new HashSet<RenderBlock*>; - containerMap->set(descendant, containerSet); + containerMap->set(descendant, adoptPtr(containerSet)); } ASSERT(!containerSet->contains(this)); containerSet->add(this); @@ -3675,7 +3934,7 @@ void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDe if (!descendantsMap) return; - HashSet<RenderBlock*>* containerSet = containerMap->take(descendant); + OwnPtr<HashSet<RenderBlock*> > containerSet = containerMap->take(descendant); if (!containerSet) return; @@ -3688,19 +3947,16 @@ void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDe // their ancestor chain before being moved. See webkit bug 93766. // ASSERT(descendant->isDescendantOf(container)); - TrackedRendererListHashSet* descendantSet = descendantsMap->get(container); - ASSERT(descendantSet); - if (!descendantSet) + TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container); + ASSERT(descendantsMapIterator != descendantsMap->end()); + if (descendantsMapIterator == descendantsMap->end()) continue; + TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get(); ASSERT(descendantSet->contains(descendant)); descendantSet->remove(descendant); - if (descendantSet->isEmpty()) { - descendantsMap->remove(container); - delete descendantSet; - } + if (descendantSet->isEmpty()) + descendantsMap->remove(descendantsMapIterator); } - - delete containerSet; } TrackedRendererListHashSet* RenderBlock::positionedObjects() const @@ -3774,7 +4030,7 @@ RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o) // Create the list of special objects if we don't aleady have one if (!m_floatingObjects) - m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); + createFloatingObjects(); else { // Don't insert the object again if it's already in the list const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); @@ -3800,8 +4056,14 @@ RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o) o->updateLogicalWidth(); o->computeAndSetBlockDirectionMargins(this); } + setLogicalWidthForFloat(newObj, logicalWidthForChild(o) + marginStartForChild(o) + marginEndForChild(o)); +#if ENABLE(CSS_SHAPES) + if (ShapeOutsideInfo* shapeOutside = o->shapeOutsideInfo()) + shapeOutside->setShapeSize(logicalWidthForChild(o), logicalHeightForChild(o)); +#endif + newObj->setShouldPaint(!o->hasSelfPaintingLayer()); // If a layer exists, the float will paint itself. Otherwise someone else will. newObj->setIsDescendant(true); newObj->m_renderer = o; @@ -3851,7 +4113,7 @@ void RenderBlock::removeFloatingObject(RenderBox* o) void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int logicalOffset) { - if (!m_floatingObjects) + if (!containsFloats()) return; const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); @@ -3869,21 +4131,37 @@ void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int logi LayoutPoint RenderBlock::computeLogicalLocationForFloat(const FloatingObject* floatingObject, LayoutUnit logicalTopOffset) const { RenderBox* childBox = floatingObject->renderer(); - LayoutUnit logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); // Constant part of right offset. LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent(logicalTopOffset); // Constant part of left offset. + LayoutUnit logicalRightOffset; // Constant part of right offset. +#if ENABLE(CSS_SHAPES) + // FIXME Bug 102948: This only works for shape outside directly set on this block. + ShapeInsideInfo* shapeInsideInfo = this->shapeInsideInfo(); + // FIXME Bug 102846: Take into account the height of the content. The offset should be + // equal to the maximum segment length. + if (shapeInsideInfo && shapeInsideInfo->hasSegments() && shapeInsideInfo->segments().size() == 1) { + // FIXME Bug 102949: Add support for shapes with multipe segments. + + // The segment offsets are relative to the content box. + logicalRightOffset = logicalLeftOffset + shapeInsideInfo->segments()[0].logicalRight; + logicalLeftOffset += shapeInsideInfo->segments()[0].logicalLeft; + } else +#endif + logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); + LayoutUnit floatLogicalWidth = min(logicalWidthForFloat(floatingObject), logicalRightOffset - logicalLeftOffset); // The width we look for. LayoutUnit floatLogicalLeft; + bool insideFlowThread = flowThreadContainingBlock(); + if (childBox->style()->floating() == LeftFloat) { LayoutUnit heightRemainingLeft = 1; LayoutUnit heightRemainingRight = 1; - floatLogicalLeft = logicalLeftOffsetForLine(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); - // FIXME: LayoutUnit::epsilon is probably only necessary here due to lost precision elsewhere https://bugs.webkit.org/show_bug.cgi?id=94000 - while (logicalRightOffsetForLine(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight) - floatLogicalLeft + LayoutUnit::epsilon() < floatLogicalWidth) { + floatLogicalLeft = logicalLeftOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); + while (logicalRightOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight) - floatLogicalLeft < floatLogicalWidth) { logicalTopOffset += min(heightRemainingLeft, heightRemainingRight); - floatLogicalLeft = logicalLeftOffsetForLine(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); - if (inRenderFlowThread()) { + floatLogicalLeft = logicalLeftOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft); + if (insideFlowThread) { // Have to re-evaluate all of our offsets, since they may have changed. logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); // Constant part of right offset. logicalLeftOffset = logicalLeftOffsetForContent(logicalTopOffset); // Constant part of left offset. @@ -3894,12 +4172,11 @@ LayoutPoint RenderBlock::computeLogicalLocationForFloat(const FloatingObject* fl } else { LayoutUnit heightRemainingLeft = 1; LayoutUnit heightRemainingRight = 1; - floatLogicalLeft = logicalRightOffsetForLine(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); - // FIXME: LayoutUnit::epsilon is probably only necessary here due to lost precision elsewhere https://bugs.webkit.org/show_bug.cgi?id=94000 - while (floatLogicalLeft - logicalLeftOffsetForLine(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft) + LayoutUnit::epsilon() < floatLogicalWidth) { + floatLogicalLeft = logicalRightOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); + while (floatLogicalLeft - logicalLeftOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalLeftOffset, false, &heightRemainingLeft) < floatLogicalWidth) { logicalTopOffset += min(heightRemainingLeft, heightRemainingRight); - floatLogicalLeft = logicalRightOffsetForLine(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); - if (inRenderFlowThread()) { + floatLogicalLeft = logicalRightOffsetForLineIgnoringShapeOutside(logicalTopOffset, logicalRightOffset, false, &heightRemainingRight); + if (insideFlowThread) { // Have to re-evaluate all of our offsets, since they may have changed. logicalRightOffset = logicalRightOffsetForContent(logicalTopOffset); // Constant part of right offset. logicalLeftOffset = logicalLeftOffsetForContent(logicalTopOffset); // Constant part of left offset. @@ -3971,6 +4248,7 @@ bool RenderBlock::positionNewFloats() LayoutPoint floatLogicalLocation = computeLogicalLocationForFloat(floatingObject, logicalTop); setLogicalLeftForFloat(floatingObject, floatLogicalLocation.x()); + setLogicalLeftForChild(childBox, floatLogicalLocation.x() + childLogicalLeftMargin); setLogicalTopForChild(childBox, floatLogicalLocation.y() + marginBeforeForChild(childBox)); @@ -4000,6 +4278,7 @@ bool RenderBlock::positionNewFloats() floatLogicalLocation = computeLogicalLocationForFloat(floatingObject, newLogicalTop); setLogicalLeftForFloat(floatingObject, floatLogicalLocation.x()); + setLogicalLeftForChild(childBox, floatLogicalLocation.x() + childLogicalLeftMargin); setLogicalTopForChild(childBox, floatLogicalLocation.y() + marginBeforeForChild(childBox)); @@ -4010,6 +4289,7 @@ bool RenderBlock::positionNewFloats() } setLogicalTopForFloat(floatingObject, floatLogicalLocation.y()); + setLogicalHeightForFloat(floatingObject, logicalHeightForChild(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox)); m_floatingObjects->addPlacedObject(floatingObject); @@ -4129,7 +4409,7 @@ inline void RenderBlock::FloatIntervalSearchAdapter<FloatTypeValue>::collectIfNe return; // All the objects returned from the tree should be already placed. - ASSERT(r->isPlaced() && rangesIntersect(m_renderer->pixelSnappedLogicalTopForFloat(r), m_renderer->pixelSnappedLogicalBottomForFloat(r), m_lowValue, m_highValue)); + ASSERT(r->isPlaced() && rangesIntersect(m_renderer->logicalTopForFloat(r), m_renderer->logicalBottomForFloat(r), m_lowValue, m_highValue)); if (FloatTypeValue == FloatingObject::FloatLeft && m_renderer->logicalRightForFloat(r) > m_offset) { @@ -4144,6 +4424,10 @@ inline void RenderBlock::FloatIntervalSearchAdapter<FloatTypeValue>::collectIfNe if (m_heightRemaining) *m_heightRemaining = m_renderer->logicalBottomForFloat(r) - m_lowValue; } + +#if ENABLE(CSS_SHAPES) + m_last = r; +#endif } LayoutUnit RenderBlock::textIndentOffset() const @@ -4157,27 +4441,30 @@ LayoutUnit RenderBlock::textIndentOffset() const return minimumValueForLength(style()->textIndent(), cw, renderView); } -LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region) const { LayoutUnit logicalLeftOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); - if (!inRenderFlowThread()) + if (!region) return logicalLeftOffset; - LayoutRect boxRect = borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage); + LayoutRect boxRect = borderBoxRectInRegion(region); return logicalLeftOffset + (isHorizontalWritingMode() ? boxRect.x() : boxRect.y()); } -LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region, LayoutUnit offsetFromLogicalTopOfFirstPage) const +LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region) const { LayoutUnit logicalRightOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); logicalRightOffset += availableLogicalWidth(); - if (!inRenderFlowThread()) + if (!region) return logicalRightOffset; - LayoutRect boxRect = borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage); + LayoutRect boxRect = borderBoxRectInRegion(region); return logicalRightOffset - (logicalWidth() - (isHorizontalWritingMode() ? boxRect.maxX() : boxRect.maxY())); } -LayoutUnit RenderBlock::logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining, LayoutUnit logicalHeight) const +LayoutUnit RenderBlock::logicalLeftFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode offsetMode) const { +#if !ENABLE(CSS_SHAPES) + UNUSED_PARAM(offsetMode); +#endif LayoutUnit left = fixedOffset; if (m_floatingObjects && m_floatingObjects->hasLeftObjects()) { if (heightRemaining) @@ -4185,8 +4472,25 @@ LayoutUnit RenderBlock::logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUn FloatIntervalSearchAdapter<FloatingObject::FloatLeft> adapter(this, roundToInt(logicalTop), roundToInt(logicalTop + logicalHeight), left, heightRemaining); m_floatingObjects->placedFloatsTree().allOverlapsWithAdapter(adapter); + +#if ENABLE(CSS_SHAPES) + const FloatingObject* lastFloat = adapter.lastFloat(); + if (offsetMode == ShapeOutsideFloatShapeOffset && lastFloat) { + if (ShapeOutsideInfo* shapeOutside = lastFloat->renderer()->shapeOutsideInfo()) { + shapeOutside->computeSegmentsForContainingBlockLine(logicalTop, logicalTopForFloat(lastFloat), logicalHeight); + left += shapeOutside->rightSegmentMarginBoxDelta(); + } + } +#endif } + return left; +} + +LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const +{ + LayoutUnit left = offsetFromFloats; + if (applyTextIndent && style()->isLeftToRightDirection()) left += textIndentOffset(); @@ -4223,8 +4527,11 @@ LayoutUnit RenderBlock::logicalLeftOffsetForLine(LayoutUnit logicalTop, LayoutUn return left; } -LayoutUnit RenderBlock::logicalRightOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, bool applyTextIndent, LayoutUnit* heightRemaining, LayoutUnit logicalHeight) const +LayoutUnit RenderBlock::logicalRightFloatOffsetForLine(LayoutUnit logicalTop, LayoutUnit fixedOffset, LayoutUnit* heightRemaining, LayoutUnit logicalHeight, ShapeOutsideFloatOffsetMode offsetMode) const { +#if !ENABLE(CSS_SHAPES) + UNUSED_PARAM(offsetMode); +#endif LayoutUnit right = fixedOffset; if (m_floatingObjects && m_floatingObjects->hasRightObjects()) { if (heightRemaining) @@ -4233,8 +4540,26 @@ LayoutUnit RenderBlock::logicalRightOffsetForLine(LayoutUnit logicalTop, LayoutU LayoutUnit rightFloatOffset = fixedOffset; FloatIntervalSearchAdapter<FloatingObject::FloatRight> adapter(this, roundToInt(logicalTop), roundToInt(logicalTop + logicalHeight), rightFloatOffset, heightRemaining); m_floatingObjects->placedFloatsTree().allOverlapsWithAdapter(adapter); + +#if ENABLE(CSS_SHAPES) + const FloatingObject* lastFloat = adapter.lastFloat(); + if (offsetMode == ShapeOutsideFloatShapeOffset && lastFloat) { + if (ShapeOutsideInfo* shapeOutside = lastFloat->renderer()->shapeOutsideInfo()) { + shapeOutside->computeSegmentsForContainingBlockLine(logicalTop, logicalTopForFloat(lastFloat), logicalHeight); + rightFloatOffset += shapeOutside->leftSegmentMarginBoxDelta(); + } + } +#endif + right = min(right, rightFloatOffset); } + + return right; +} + +LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const +{ + LayoutUnit right = offsetFromFloats; if (applyTextIndent && !style()->isLeftToRightDirection()) right -= textIndentOffset(); @@ -4509,8 +4834,7 @@ LayoutUnit RenderBlock::addOverhangingFloats(RenderBlock* child, bool makeChildP // We create the floating object list lazily. if (!m_floatingObjects) - m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); - + createFloatingObjects(); m_floatingObjects->add(floatingObj); } } else { @@ -4582,7 +4906,7 @@ void RenderBlock::addIntrudingFloats(RenderBlock* prev, LayoutUnit logicalLeftOf // We create the floating object list lazily. if (!m_floatingObjects) - m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); + createFloatingObjects(); m_floatingObjects->add(floatingObj); } } @@ -4602,7 +4926,7 @@ bool RenderBlock::containsFloat(RenderBox* renderer) const void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove, bool inLayout) { - if (!everHadLayout()) + if (!everHadLayout() && !containsFloats()) return; MarkingBehavior markParents = inLayout ? MarkOnlyThis : MarkContainingBlockChain; @@ -4679,7 +5003,7 @@ LayoutUnit RenderBlock::getClearDelta(RenderBox* child, LayoutUnit logicalTop) return newLogicalTop - logicalTop; RenderRegion* region = regionAtBlockOffset(logicalTopForChild(child)); - LayoutRect borderBox = child->borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage() + logicalTopForChild(child), DoNotCacheRenderBoxRegionInfo); + LayoutRect borderBox = child->borderBoxRectInRegion(region, DoNotCacheRenderBoxRegionInfo); LayoutUnit childLogicalWidthAtOldLogicalTopOffset = isHorizontalWritingMode() ? borderBox.width() : borderBox.height(); // FIXME: None of this is right for perpendicular writing-mode children. @@ -4691,7 +5015,7 @@ LayoutUnit RenderBlock::getClearDelta(RenderBox* child, LayoutUnit logicalTop) child->setLogicalTop(newLogicalTop); child->updateLogicalWidth(); region = regionAtBlockOffset(logicalTopForChild(child)); - borderBox = child->borderBoxRectInRegion(region, offsetFromLogicalTopOfFirstPage() + logicalTopForChild(child), DoNotCacheRenderBoxRegionInfo); + borderBox = child->borderBoxRectInRegion(region, DoNotCacheRenderBoxRegionInfo); LayoutUnit childLogicalWidthAtNewLogicalTopOffset = isHorizontalWritingMode() ? borderBox.width() : borderBox.height(); child->setLogicalTop(childOldLogicalTop); @@ -4768,7 +5092,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu // Hit test contents if we don't have columns. if (!hasColumns()) { if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { - updateHitTestResult(result, locationInContainer.point() - localOffset); + updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); return true; } if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset))) @@ -4779,6 +5103,15 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu } } + // Check if the point is outside radii. + if (!isRenderView() && style()->hasBorderRadius()) { + LayoutRect borderRect = borderBoxRect(); + borderRect.moveBy(adjustedLocation); + RoundedRect border = style()->getRoundedBorderFor(borderRect, view()); + if (!locationInContainer.intersects(border)) + return false; + } + // Now hit test our background if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) { LayoutRect boundsRect(adjustedLocation, size()); @@ -4833,7 +5166,10 @@ public: { int colCount = m_colInfo->columnCount(); m_colIndex = colCount - 1; - m_currLogicalTopOffset = colCount * m_colInfo->columnHeight() * m_direction; + + m_currLogicalTopOffset = m_block.initialBlockOffsetForPainting(); + m_currLogicalTopOffset = colCount * m_block.blockDeltaForPaintingNextColumn(); + update(); } @@ -4851,12 +5187,6 @@ public: { LayoutUnit currLogicalLeftOffset = (m_isHorizontal ? m_colRect.x() : m_colRect.y()) - m_logicalLeft; offset += m_isHorizontal ? LayoutSize(currLogicalLeftOffset, m_currLogicalTopOffset) : LayoutSize(m_currLogicalTopOffset, currLogicalLeftOffset); - if (m_colInfo->progressionAxis() == ColumnInfo::BlockAxis) { - if (m_isHorizontal) - offset.expand(0, m_colRect.y() - m_block.borderTop() - m_block.paddingTop()); - else - offset.expand(m_colRect.x() - m_block.borderLeft() - m_block.paddingLeft(), 0); - } } private: @@ -4864,10 +5194,9 @@ private: { if (m_colIndex < 0) return; - m_colRect = m_block.columnRectAt(const_cast<ColumnInfo*>(m_colInfo), m_colIndex); m_block.flipForWritingMode(m_colRect); - m_currLogicalTopOffset -= (m_isHorizontal ? m_colRect.height() : m_colRect.width()) * m_direction; + m_currLogicalTopOffset -= m_block.blockDeltaForPaintingNextColumn(); } const RenderBlock& m_block; @@ -4943,22 +5272,22 @@ Position RenderBlock::positionForBox(InlineBox *box, bool start) const if (!box) return Position(); - if (!box->renderer()->node()) - return createLegacyEditingPosition(node(), start ? caretMinOffset() : caretMaxOffset()); + if (!box->renderer()->nonPseudoNode()) + return createLegacyEditingPosition(nonPseudoNode(), start ? caretMinOffset() : caretMaxOffset()); if (!box->isInlineTextBox()) - return createLegacyEditingPosition(box->renderer()->node(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset()); + return createLegacyEditingPosition(box->renderer()->nonPseudoNode(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset()); InlineTextBox* textBox = toInlineTextBox(box); - return createLegacyEditingPosition(box->renderer()->node(), start ? textBox->start() : textBox->start() + textBox->len()); + return createLegacyEditingPosition(box->renderer()->nonPseudoNode(), start ? textBox->start() : textBox->start() + textBox->len()); } static inline bool isEditingBoundary(RenderObject* ancestor, RenderObject* child) { - ASSERT(!ancestor || ancestor->node()); - ASSERT(child && child->node()); + ASSERT(!ancestor || ancestor->nonPseudoNode()); + ASSERT(child && child->nonPseudoNode()); return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView()) - || ancestor->node()->rendererIsEditable() == child->node()->rendererIsEditable(); + || ancestor->nonPseudoNode()->rendererIsEditable() == child->nonPseudoNode()->rendererIsEditable(); } // FIXME: This function should go on RenderObject as an instance method. Then @@ -4974,14 +5303,14 @@ static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock* LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation)); // If this is an anonymous renderer, we just recur normally - Node* childNode = child->node(); + Node* childNode = child->nonPseudoNode(); if (!childNode) return child->positionForPoint(pointInChildCoordinates); // Otherwise, first make sure that the editability of the parent and child agree. // If they don't agree, then we return a visible position just before or after the child RenderObject* ancestor = parent; - while (ancestor && !ancestor->node()) + while (ancestor && !ancestor->nonPseudoNode()) ancestor = ancestor->parent(); // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal @@ -5039,7 +5368,7 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const LayoutPoin } } - bool moveCaretToBoundary = document()->frame()->editor()->behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom(); + bool moveCaretToBoundary = document()->frame()->editor().behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom(); if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) { // y coordinate is below last root line box, pretend we hit it @@ -5218,7 +5547,7 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width) bool destroyColumns = !requiresColumns(count); if (destroyColumns) { if (hasColumns()) { - delete gColumnInfoMap->take(this); + gColumnInfoMap->take(this); setHasColumns(false); } } else { @@ -5229,7 +5558,7 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width) if (!gColumnInfoMap) gColumnInfoMap = new ColumnInfoMap; info = new ColumnInfo; - gColumnInfoMap->add(this, info); + gColumnInfoMap->add(this, adoptPtr(info)); setHasColumns(true); } info->setDesiredColumnCount(count); @@ -5300,9 +5629,9 @@ LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const // Compute the appropriate rect based off our information. LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth(); LayoutUnit colLogicalHeight = colInfo->columnHeight(); - LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); + LayoutUnit colLogicalTop = borderAndPaddingBefore(); LayoutUnit colLogicalLeft = logicalLeftOffsetForContent(); - int colGap = columnGap(); + LayoutUnit colGap = columnGap(); if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { if (style()->isLeftToRightDirection() ^ colInfo->progressionIsReversed()) colLogicalLeft += index * (colLogicalWidth + colGap); @@ -5330,7 +5659,7 @@ bool RenderBlock::relayoutForPagination(bool hasSpecifiedPageLogicalHeight, Layo addOverflowFromInlineChildren(); else addOverflowFromBlockChildren(); - LayoutUnit layoutOverflowLogicalBottom = (isHorizontalWritingMode() ? layoutOverflowRect().maxY() : layoutOverflowRect().maxX()) - borderBefore() - paddingBefore(); + LayoutUnit layoutOverflowLogicalBottom = (isHorizontalWritingMode() ? layoutOverflowRect().maxY() : layoutOverflowRect().maxX()) - borderAndPaddingBefore(); // FIXME: We don't balance properly at all in the presence of forced page breaks. We need to understand what // the distance between forced page breaks is so that we can avoid making the minimum column height too tall. @@ -5344,7 +5673,7 @@ bool RenderBlock::relayoutForPagination(bool hasSpecifiedPageLogicalHeight, Layo // maximum page break distance. if (!pageLogicalHeight) { LayoutUnit distanceBetweenBreaks = max<LayoutUnit>(colInfo->maximumDistanceBetweenForcedBreaks(), - view()->layoutState()->pageLogicalOffset(this, borderBefore() + paddingBefore() + layoutOverflowLogicalBottom) - colInfo->forcedBreakOffset()); + view()->layoutState()->pageLogicalOffset(this, borderAndPaddingBefore() + layoutOverflowLogicalBottom) - colInfo->forcedBreakOffset()); columnHeight = max(colInfo->minimumColumnHeight(), distanceBetweenBreaks); } } else if (layoutOverflowLogicalBottom > boundedMultiply(pageLogicalHeight, desiredColumnCount)) { @@ -5364,7 +5693,7 @@ bool RenderBlock::relayoutForPagination(bool hasSpecifiedPageLogicalHeight, Layo colInfo->setColumnCountAndHeight(ceilf((float)layoutOverflowLogicalBottom / pageLogicalHeight), pageLogicalHeight); if (columnCount(colInfo)) { - setLogicalHeight(borderBefore() + paddingBefore() + colInfo->columnHeight() + borderAfter() + paddingAfter() + scrollbarLogicalHeight()); + setLogicalHeight(borderAndPaddingBefore() + colInfo->columnHeight() + borderAndPaddingAfter() + scrollbarLogicalHeight()); m_overflow.clear(); } else m_overflow = savedOverflow.release(); @@ -5476,13 +5805,13 @@ void RenderBlock::adjustRectForColumns(LayoutRect& r) const LayoutRect result; bool isHorizontal = isHorizontalWritingMode(); - LayoutUnit beforeBorderPadding = borderBefore() + paddingBefore(); + LayoutUnit beforeBorderPadding = borderAndPaddingBefore(); LayoutUnit colHeight = colInfo->columnHeight(); if (!colHeight) return; LayoutUnit startOffset = max(isHorizontal ? r.y() : r.x(), beforeBorderPadding); - LayoutUnit endOffset = min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight); + LayoutUnit endOffset = max(min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight), beforeBorderPadding); // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744 unsigned startColumn = (startOffset - beforeBorderPadding) / colHeight; @@ -5525,7 +5854,7 @@ LayoutPoint RenderBlock::flipForWritingModeIncludingColumns(const LayoutPoint& p return point; ColumnInfo* colInfo = columnInfo(); LayoutUnit columnLogicalHeight = colInfo->columnHeight(); - LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + LayoutUnit expandedLogicalHeight = borderAndPaddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAndPaddingAfter() + scrollbarLogicalHeight(); if (isHorizontalWritingMode()) return LayoutPoint(point.x(), expandedLogicalHeight - point.y()); return LayoutPoint(expandedLogicalHeight - point.x(), point.y()); @@ -5539,7 +5868,7 @@ void RenderBlock::adjustStartEdgeForWritingModeIncludingColumns(LayoutRect& rect ColumnInfo* colInfo = columnInfo(); LayoutUnit columnLogicalHeight = colInfo->columnHeight(); - LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + LayoutUnit expandedLogicalHeight = borderAndPaddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAndPaddingAfter() + scrollbarLogicalHeight(); if (isHorizontalWritingMode()) rect.setY(expandedLogicalHeight - rect.maxY()); @@ -5561,7 +5890,7 @@ void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) for (unsigned i = 0; i < colCount; ++i) { // Compute the edges for a given column in the block progression direction. - LayoutRect sliceRect = LayoutRect(logicalLeft, borderBefore() + paddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight); + LayoutRect sliceRect = LayoutRect(logicalLeft, borderAndPaddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight); if (!isHorizontalWritingMode()) sliceRect = sliceRect.transposedRect(); @@ -5573,7 +5902,7 @@ void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset); else - offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderBefore() - paddingBefore()); + offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderAndPaddingBefore()); return; } } else { @@ -5581,61 +5910,55 @@ void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) offset.expand(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft); else - offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderBefore() - paddingBefore(), 0); + offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderAndPaddingBefore(), 0); return; } } } } +void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const +{ + if (childrenInline()) { + // FIXME: Remove this const_cast. + const_cast<RenderBlock*>(this)->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); + } else + computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); + + maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth); + + if (!style()->autoWrap() && childrenInline()) { + // A horizontal marquee with inline children has no minimum width. + if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) + minLogicalWidth = 0; + } + + if (isTableCell()) { + Length tableCellWidth = toRenderTableCell(this)->styleOrColLogicalWidth(); + if (tableCellWidth.isFixed() && tableCellWidth.value() > 0) + maxLogicalWidth = max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth.value())); + } + + int scrollbarWidth = instrinsicScrollbarLogicalWidth(); + maxLogicalWidth += scrollbarWidth; + minLogicalWidth += scrollbarWidth; +} + void RenderBlock::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); updateFirstLetter(); + m_minPreferredLogicalWidth = 0; + m_maxPreferredLogicalWidth = 0; + RenderStyle* styleToUse = style(); if (!isTableCell() && styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0 - && style()->marqueeBehavior() != MALTERNATE && !(isDeprecatedFlexItem() && !styleToUse->logicalWidth().intValue())) + && !(isDeprecatedFlexItem() && !styleToUse->logicalWidth().intValue())) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value()); - else { - m_minPreferredLogicalWidth = 0; - m_maxPreferredLogicalWidth = 0; - - if (childrenInline()) - computeInlinePreferredLogicalWidths(); - else - computeBlockPreferredLogicalWidths(); - - m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - - if (!styleToUse->autoWrap() && childrenInline()) { - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; - - // A horizontal marquee with inline children has no minimum width. - if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) - m_minPreferredLogicalWidth = 0; - } - - int scrollbarWidth = 0; - // FIXME: This should only be done for horizontal writing mode. - // For vertical writing mode, this should check overflowX and use the horizontalScrollbarHeight. - if (hasOverflowClip() && styleToUse->overflowY() == OSCROLL) { - layer()->setHasVerticalScrollbar(true); - scrollbarWidth = verticalScrollbarWidth(); - m_maxPreferredLogicalWidth += scrollbarWidth; - } - - if (isTableCell()) { - Length w = toRenderTableCell(this)->styleOrColLogicalWidth(); - if (w.isFixed() && w.value() > 0) { - m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(w.value())); - scrollbarWidth = 0; - } - } - - m_minPreferredLogicalWidth += scrollbarWidth; - } + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); @@ -5783,7 +6106,7 @@ static inline LayoutUnit adjustFloatForSubPixelLayout(float value) } -void RenderBlock::computeInlinePreferredLogicalWidths() +void RenderBlock::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) { float inlineMax = 0; float inlineMin = 0; @@ -5806,7 +6129,12 @@ void RenderBlock::computeInlinePreferredLogicalWidths() autoWrap = oldAutoWrap = styleToUse->autoWrap(); InlineMinMaxIterator childIterator(this); - bool addedTextIndent = false; // Only gets added in once. + + // Only gets added to the max preffered width once. + bool addedTextIndent = false; + // Signals the text indent was more negative than the min preferred width + bool hasRemainingNegativeTextIndent = false; + LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw, view()); RenderObject* prevFloat = 0; bool isPrevChildInlineFlow = false; @@ -5900,22 +6228,21 @@ void RenderBlock::computeInlinePreferredLogicalWidths() bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; } // If we're supposed to clear the previous float, then terminate maxwidth as well. if (clearPreviousFloat) { - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); + updatePreferredWidth(maxLogicalWidth, inlineMax); inlineMax = 0; } // Add in text-indent. This is added in only once. - LayoutUnit ti = 0; - if (!addedTextIndent) { - ti = textIndent; - childMin += ti.ceilToFloat(); - childMax += ti.ceilToFloat(); + if (!addedTextIndent && !child->isFloating()) { + LayoutUnit ceiledIndent = textIndent.ceilToFloat(); + childMin += ceiledIndent; + childMax += ceiledIndent; if (childMin < 0) textIndent = adjustFloatForSubPixelLayout(childMin); @@ -5928,19 +6255,19 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) { if (child->isFloating()) - updatePreferredWidth(m_minPreferredLogicalWidth, childMin); + updatePreferredWidth(minLogicalWidth, childMin); else inlineMin += childMin; } else { // Now check our line. - updatePreferredWidth(m_minPreferredLogicalWidth, childMin); + updatePreferredWidth(minLogicalWidth, childMin); // Now start a new line. inlineMin = 0; } if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; } @@ -5955,7 +6282,7 @@ void RenderBlock::computeInlinePreferredLogicalWidths() RenderText* t = toRenderText(child); if (t->isWordBreak()) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; continue; } @@ -5979,7 +6306,7 @@ void RenderBlock::computeInlinePreferredLogicalWidths() // This text object will not be rendered, but it may still provide a breaking opportunity. if (!hasBreak && childMax == 0) { if (autoWrap && (beginWS || endWS)) { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; } continue; @@ -5992,18 +6319,24 @@ void RenderBlock::computeInlinePreferredLogicalWidths() // Add in text-indent. This is added in only once. float ti = 0; - if (!addedTextIndent) { + if (!addedTextIndent || hasRemainingNegativeTextIndent) { ti = textIndent.ceilToFloat(); - childMin += ti; - childMax += ti; beginMin += ti; - beginMax += ti; - if (childMin < 0) - textIndent = childMin; - else + // It the text indent negative and larger than the child minimum, we re-use the remainder + // in future minimum calculations, but using the negative value again on the maximum + // will lead to under-counting the max pref width. + if (!addedTextIndent) { + childMax += ti; + beginMax += ti; addedTextIndent = true; + } + + if (childMin < 0) { + textIndent = childMin; + hasRemainingNegativeTextIndent = true; + } } // If we have no breakable characters at all, @@ -6016,10 +6349,10 @@ void RenderBlock::computeInlinePreferredLogicalWidths() // we start and end with whitespace. if (beginWS) // Go ahead and end the current line. - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); else { inlineMin += beginMin; - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); childMin -= ti; } @@ -6028,11 +6361,11 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (endWS) { // We end in whitespace, which means we can go ahead // and end our current line. - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = 0; shouldBreakLineAfterText = false; } else { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); + updatePreferredWidth(minLogicalWidth, inlineMin); inlineMin = endMin; shouldBreakLineAfterText = true; } @@ -6040,8 +6373,8 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (hasBreak) { inlineMax += beginMax; - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); - updatePreferredWidth(m_maxPreferredLogicalWidth, childMax); + updatePreferredWidth(maxLogicalWidth, inlineMax); + updatePreferredWidth(maxLogicalWidth, childMax); inlineMax = endMax; addedTextIndent = true; } else @@ -6052,8 +6385,8 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (child->isListMarker()) stripFrontSpaces = true; } else { - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); + updatePreferredWidth(minLogicalWidth, inlineMin); + updatePreferredWidth(maxLogicalWidth, inlineMax); inlineMin = inlineMax = 0; stripFrontSpaces = true; trailingSpaceChild = 0; @@ -6071,11 +6404,11 @@ void RenderBlock::computeInlinePreferredLogicalWidths() if (styleToUse->collapseWhiteSpace()) stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); - updatePreferredWidth(m_minPreferredLogicalWidth, inlineMin); - updatePreferredWidth(m_maxPreferredLogicalWidth, inlineMax); + updatePreferredWidth(minLogicalWidth, inlineMin); + updatePreferredWidth(maxLogicalWidth, inlineMax); } -void RenderBlock::computeBlockPreferredLogicalWidths() +void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { RenderStyle* styleToUse = style(); bool nowrap = styleToUse->whiteSpace() == NOWRAP; @@ -6094,11 +6427,11 @@ void RenderBlock::computeBlockPreferredLogicalWidths() if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth; if (childStyle->clear() & CLEFT) { - m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth); floatLeftWidth = 0; } if (childStyle->clear() & CRIGHT) { - m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth); floatRightWidth = 0; } } @@ -6129,11 +6462,11 @@ void RenderBlock::computeBlockPreferredLogicalWidths() } LayoutUnit w = childMinPreferredLogicalWidth + margin; - m_minPreferredLogicalWidth = max(w, m_minPreferredLogicalWidth); + minLogicalWidth = max(w, minLogicalWidth); // IE ignores tables for calculation of nowrap. Makes some sense. if (nowrap && !child->isTable()) - m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(w, maxLogicalWidth); w = childMaxPreferredLogicalWidth + margin; @@ -6151,26 +6484,26 @@ void RenderBlock::computeBlockPreferredLogicalWidths() w = max(w, floatLeftWidth + floatRightWidth); } else - m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth); floatLeftWidth = floatRightWidth = 0; } if (child->isFloating()) { - if (styleToUse->floating() == LeftFloat) + if (childStyle->floating() == LeftFloat) floatLeftWidth += w; else floatRightWidth += w; } else - m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(w, maxLogicalWidth); child = child->nextSibling(); } // Always make sure these values are non-negative. - m_minPreferredLogicalWidth = max<LayoutUnit>(0, m_minPreferredLogicalWidth); - m_maxPreferredLogicalWidth = max<LayoutUnit>(0, m_maxPreferredLogicalWidth); + minLogicalWidth = max<LayoutUnit>(0, minLogicalWidth); + maxLogicalWidth = max<LayoutUnit>(0, maxLogicalWidth); - m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth); + maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth); } bool RenderBlock::hasLineIfEmpty() const @@ -6181,7 +6514,7 @@ bool RenderBlock::hasLineIfEmpty() const if (node()->isRootEditableElement()) return true; - if (node()->isShadowRoot() && toShadowRoot(node())->host()->hasTagName(inputTag)) + if (node()->isShadowRoot() && isHTMLInputElement(toShadowRoot(node())->host())) return true; return false; @@ -6329,10 +6662,17 @@ RenderBlock* RenderBlock::firstLineBlock() const if (hasPseudo) break; RenderObject* parentBlock = firstLineBlock->parent(); - if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() || - !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow()) + // We include isRenderButton in this check because buttons are + // implemented using flex box but should still support first-line. The + // flex box spec requires that flex box does not support first-line, + // though. + // FIXME: Remove when buttons are implemented with align-items instead + // of flexbox. + if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() + || !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow() + || (parentBlock->isFlexibleBox() && !parentBlock->isRenderButton())) break; - ASSERT(parentBlock->isRenderBlock()); + ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock()); firstLineBlock = toRenderBlock(parentBlock); } @@ -6374,14 +6714,21 @@ static inline RenderObject* findFirstLetterBlock(RenderBlock* start) { RenderObject* firstLetterBlock = start; while (true) { + // We include isRenderButton in these two checks because buttons are + // implemented using flex box but should still support first-letter. + // The flex box spec requires that flex box does not support + // first-letter, though. + // FIXME: Remove when buttons are implemented with align-items instead + // of flexbox. bool canHaveFirstLetterRenderer = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER) - && firstLetterBlock->canHaveGeneratedChildren(); + && firstLetterBlock->canHaveGeneratedChildren() + && (!firstLetterBlock->isFlexibleBox() || firstLetterBlock->isRenderButton()); if (canHaveFirstLetterRenderer) return firstLetterBlock; RenderObject* parentBlock = firstLetterBlock->parent(); if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock || - !parentBlock->isBlockFlow()) + !parentBlock->isBlockFlow() || (parentBlock->isFlexibleBox() && !parentBlock->isRenderButton())) return 0; firstLetterBlock = parentBlock; } @@ -6400,9 +6747,9 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO // The first-letter renderer needs to be replaced. Create a new renderer of the right type. RenderObject* newFirstLetter; if (pseudoStyle->display() == INLINE) - newFirstLetter = new (renderArena()) RenderInline(document()); + newFirstLetter = RenderInline::createAnonymous(document()); else - newFirstLetter = new (renderArena()) RenderBlock(document()); + newFirstLetter = RenderBlock::createAnonymous(document()); newFirstLetter->setStyle(pseudoStyle); // Move the first letter into the new renderer. @@ -6446,9 +6793,9 @@ void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); RenderObject* firstLetter = 0; if (pseudoStyle->display() == INLINE) - firstLetter = new (renderArena()) RenderInline(document()); + firstLetter = RenderInline::createAnonymous(document()); else - firstLetter = new (renderArena()) RenderBlock(document()); + firstLetter = RenderBlock::createAnonymous(document()); firstLetter->setStyle(pseudoStyle); firstLetterContainer->addChild(firstLetter, currentChild); @@ -6571,28 +6918,6 @@ static bool shouldCheckLines(RenderObject* obj) && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERTICAL); } -static RootInlineBox* getLineAtIndex(RenderBlock* block, int i, int& count) -{ - if (block->style()->visibility() == VISIBLE) { - if (block->childrenInline()) { - for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { - if (count++ == i) - return box; - } - } - else { - for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) { - if (shouldCheckLines(obj)) { - RootInlineBox *box = getLineAtIndex(toRenderBlock(obj), i, count); - if (box) - return box; - } - } - } - } - return 0; -} - static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count) { if (block->style()->visibility() == VISIBLE) { @@ -6620,23 +6945,54 @@ static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, return -1; } -RootInlineBox* RenderBlock::lineAtIndex(int i) +RootInlineBox* RenderBlock::lineAtIndex(int i) const { - int count = 0; - return getLineAtIndex(this, i, count); + ASSERT(i >= 0); + + if (style()->visibility() != VISIBLE) + return 0; + + if (childrenInline()) { + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + if (!i--) + return box; + } else { + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!shouldCheckLines(child)) + continue; + if (RootInlineBox* box = toRenderBlock(child)->lineAtIndex(i)) + return box; + } + } + + return 0; } -int RenderBlock::lineCount() +int RenderBlock::lineCount(const RootInlineBox* stopRootInlineBox, bool* found) const { int count = 0; + if (style()->visibility() == VISIBLE) { if (childrenInline()) - for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { count++; + if (box == stopRootInlineBox) { + if (found) + *found = true; + break; + } + } else for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) - if (shouldCheckLines(obj)) - count += toRenderBlock(obj)->lineCount(); + if (shouldCheckLines(obj)) { + bool recursiveFound = false; + count += toRenderBlock(obj)->lineCount(stopRootInlineBox, &recursiveFound); + if (recursiveFound) { + if (found) + *found = true; + break; + } + } } return count; } @@ -6691,32 +7047,30 @@ void RenderBlock::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutUnit& } } -void RenderBlock::borderFitAdjust(LayoutRect& rect) const +void RenderBlock::fitBorderToLinesIfNeeded() { - if (style()->borderFit() == BorderFitBorder) + if (style()->borderFit() == BorderFitBorder || hasOverrideWidth()) return; // Walk any normal flow lines to snugly fit. LayoutUnit left = LayoutUnit::max(); LayoutUnit right = LayoutUnit::min(); - LayoutUnit oldWidth = rect.width(); + LayoutUnit oldWidth = contentWidth(); adjustForBorderFit(0, left, right); - if (left != LayoutUnit::max()) { - left = min(left, oldWidth - (borderRight() + paddingRight())); - - left -= (borderLeft() + paddingLeft()); - if (left > 0) { - rect.move(left, 0); - rect.expand(-left, 0); - } - } - if (right != LayoutUnit::min()) { - right = max(right, borderLeft() + paddingLeft()); - - right += (borderRight() + paddingRight()); - if (right < oldWidth) - rect.expand(-(oldWidth - right), 0); - } + + // Clamp to our existing edges. We can never grow. We only shrink. + LayoutUnit leftEdge = borderLeft() + paddingLeft(); + LayoutUnit rightEdge = leftEdge + oldWidth; + left = min(rightEdge, max(leftEdge, left)); + right = max(leftEdge, min(rightEdge, right)); + + LayoutUnit newContentWidth = right - left; + if (newContentWidth == oldWidth) + return; + + setOverrideLogicalContentWidth(newContentWidth); + layoutBlock(false); + clearOverrideLogicalContentWidth(); } void RenderBlock::clearTruncation() @@ -6757,6 +7111,99 @@ void RenderBlock::setMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg) m_rareData->m_margins.setNegativeMarginAfter(neg); } +void RenderBlock::setMustDiscardMarginBefore(bool value) +{ + if (style()->marginBeforeCollapse() == MDISCARD) { + ASSERT(value); + return; + } + + if (!m_rareData && !value) + return; + + if (!m_rareData) + m_rareData = adoptPtr(new RenderBlockRareData(this)); + + m_rareData->m_discardMarginBefore = value; +} + +void RenderBlock::setMustDiscardMarginAfter(bool value) +{ + if (style()->marginAfterCollapse() == MDISCARD) { + ASSERT(value); + return; + } + + if (!m_rareData && !value) + return; + + if (!m_rareData) + m_rareData = adoptPtr(new RenderBlockRareData(this)); + + m_rareData->m_discardMarginAfter = value; +} + +bool RenderBlock::mustDiscardMarginBefore() const +{ + return style()->marginBeforeCollapse() == MDISCARD || (m_rareData && m_rareData->m_discardMarginBefore); +} + +bool RenderBlock::mustDiscardMarginAfter() const +{ + return style()->marginAfterCollapse() == MDISCARD || (m_rareData && m_rareData->m_discardMarginAfter); +} + +bool RenderBlock::mustDiscardMarginBeforeForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginBefore() : (child->style()->marginBeforeCollapse() == MDISCARD); + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginAfter() : (child->style()->marginAfterCollapse() == MDISCARD); + + // FIXME: We return false here because the implementation is not geometrically complete. We have values only for before/after, not start/end. + // In case the boxes are perpendicular we assume the property is not specified. + return false; +} + +bool RenderBlock::mustDiscardMarginAfterForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginAfter() : (child->style()->marginAfterCollapse() == MDISCARD); + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->mustDiscardMarginBefore() : (child->style()->marginBeforeCollapse() == MDISCARD); + + // FIXME: See |mustDiscardMarginBeforeForChild| above. + return false; +} + +bool RenderBlock::mustSeparateMarginBeforeForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + const RenderStyle* childStyle = child->style(); + if (!child->isWritingModeRoot()) + return childStyle->marginBeforeCollapse() == MSEPARATE; + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return childStyle->marginAfterCollapse() == MSEPARATE; + + // FIXME: See |mustDiscardMarginBeforeForChild| above. + return false; +} + +bool RenderBlock::mustSeparateMarginAfterForChild(const RenderBox* child) const +{ + ASSERT(!child->selfNeedsLayout()); + const RenderStyle* childStyle = child->style(); + if (!child->isWritingModeRoot()) + return childStyle->marginAfterCollapse() == MSEPARATE; + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return childStyle->marginBeforeCollapse() == MSEPARATE; + + // FIXME: See |mustDiscardMarginBeforeForChild| above. + return false; +} + void RenderBlock::setPaginationStrut(LayoutUnit strut) { if (!m_rareData) { @@ -6777,6 +7224,23 @@ void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset) m_rareData->m_pageLogicalOffset = logicalOffset; } +void RenderBlock::setBreakAtLineToAvoidWidow(RootInlineBox* lineToBreak) +{ + ASSERT(lineToBreak); + if (!m_rareData) + m_rareData = adoptPtr(new RenderBlockRareData(this)); + m_rareData->m_shouldBreakAtLineToAvoidWidow = true; + m_rareData->m_lineBreakToAvoidWidow = lineToBreak; +} + +void RenderBlock::clearShouldBreakAtLineToAvoidWidow() const +{ + if (!m_rareData) + return; + m_rareData->m_shouldBreakAtLineToAvoidWidow = false; + m_rareData->m_lineBreakToAvoidWidow = 0; +} + void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const { // For blocks inside inlines, we go ahead and include margins so that we run right up to the @@ -6863,29 +7327,14 @@ LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, La LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset()); - if (extraWidthToEndOfLine) { - if (isRenderBlock()) { - *extraWidthToEndOfLine = width() - caretRect.maxX(); - } else { - // FIXME: This code looks wrong. - // myRight and containerRight are set up, but then clobbered. - // So *extraWidthToEndOfLine will always be 0 here. - - LayoutUnit myRight = caretRect.maxX(); - // FIXME: why call localToAbsoluteForContent() twice here, too? - FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0)); - - LayoutUnit containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent(); - FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0)); - - *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); - } - } + // FIXME: Does this need to adjust for vertical orientation? + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = width() - caretRect.maxX(); return caretRect; } -void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset) +void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) { // For blocks inside inlines, we go ahead and include margins so that we run right up to the // inline boxes above and below us (thus getting merged with them to form a single irregular @@ -6920,16 +7369,16 @@ void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& a FloatPoint pos; // FIXME: This doesn't work correctly with transforms. if (box->layer()) - pos = curr->localToAbsolute(); + pos = curr->localToContainerPoint(FloatPoint(), paintContainer); else pos = FloatPoint(additionalOffset.x() + box->x(), additionalOffset.y() + box->y()); - box->addFocusRingRects(rects, flooredLayoutPoint(pos)); + box->addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer); } } } if (inlineElementContinuation()) - inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location())); + inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location()), paintContainer); } RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const @@ -6945,16 +7394,17 @@ bool RenderBlock::hasNextPage(LayoutUnit logicalOffset, PageBoundaryRule pageBou { ASSERT(view()->layoutState() && view()->layoutState()->isPaginated()); - if (!inRenderFlowThread()) + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) return true; // Printing and multi-column both make new pages to accommodate content. // See if we're in the last region. LayoutUnit pageOffset = offsetFromLogicalTopOfFirstPage() + logicalOffset; - RenderRegion* region = enclosingRenderFlowThread()->regionAtBlockOffset(pageOffset, this); + RenderRegion* region = flowThread->regionAtBlockOffset(pageOffset, this); if (!region) return false; if (region->isLastRegion()) - return region->isRenderRegionSet() || region->style()->regionOverflow() == BreakRegionOverflow + return region->isRenderRegionSet() || region->style()->regionFragment() == BreakRegionFragment || (pageBoundaryRule == IncludePageBoundary && pageOffset == region->logicalTopForFlowThreadContent()); return true; } @@ -6996,7 +7446,8 @@ LayoutUnit RenderBlock::applyBeforeBreak(RenderBox* child, LayoutUnit logicalOff // FIXME: Add page break checking here when we support printing. bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. - bool checkRegionBreaks = inRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); bool checkBeforeAlways = (checkColumnBreaks && child->style()->columnBreakBefore() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakBefore() == PBALWAYS) || (checkRegionBreaks && child->style()->regionBreakBefore() == PBALWAYS); if (checkBeforeAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) { @@ -7004,7 +7455,7 @@ LayoutUnit RenderBlock::applyBeforeBreak(RenderBox* child, LayoutUnit logicalOff view()->layoutState()->addForcedColumnBreak(child, logicalOffset); if (checkRegionBreaks) { LayoutUnit offsetBreakAdjustment = 0; - if (enclosingRenderFlowThread()->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset, child, true, &offsetBreakAdjustment)) + if (flowThread->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset, child, true, &offsetBreakAdjustment)) return logicalOffset + offsetBreakAdjustment; } return nextPageLogicalTop(logicalOffset, IncludePageBoundary); @@ -7017,18 +7468,22 @@ LayoutUnit RenderBlock::applyAfterBreak(RenderBox* child, LayoutUnit logicalOffs // FIXME: Add page break checking here when we support printing. bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. - bool checkRegionBreaks = inRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); bool checkAfterAlways = (checkColumnBreaks && child->style()->columnBreakAfter() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakAfter() == PBALWAYS) || (checkRegionBreaks && child->style()->regionBreakAfter() == PBALWAYS); if (checkAfterAlways && inNormalFlow(child) && hasNextPage(logicalOffset, IncludePageBoundary)) { - marginInfo.setMarginAfterQuirk(true); // Cause margins to be discarded for any following content. + LayoutUnit marginOffset = marginInfo.canCollapseWithMarginBefore() ? LayoutUnit() : marginInfo.margin(); + + // So our margin doesn't participate in the next collapsing steps. + marginInfo.clearMargin(); + if (checkColumnBreaks) view()->layoutState()->addForcedColumnBreak(child, logicalOffset); if (checkRegionBreaks) { - LayoutUnit marginOffset = marginInfo.canCollapseWithMarginBefore() ? LayoutUnit() : marginInfo.margin(); LayoutUnit offsetBreakAdjustment = 0; - if (enclosingRenderFlowThread()->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset + marginOffset, child, false, &offsetBreakAdjustment)) - return logicalOffset + offsetBreakAdjustment; + if (flowThread->addForcedRegionBreak(offsetFromLogicalTopOfFirstPage() + logicalOffset + marginOffset, child, false, &offsetBreakAdjustment)) + return logicalOffset + marginOffset + offsetBreakAdjustment; } return nextPageLogicalTop(logicalOffset, IncludePageBoundary); } @@ -7042,21 +7497,23 @@ LayoutUnit RenderBlock::pageLogicalTopForOffset(LayoutUnit offset) const LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->m_layoutOffset.height() : renderView->layoutState()->m_layoutOffset.width(); LayoutUnit cumulativeOffset = offset + blockLogicalTop; - if (!inRenderFlowThread()) { + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) { LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight(); if (!pageLogicalHeight) return 0; return cumulativeOffset - roundToInt(cumulativeOffset - firstPageLogicalTop) % roundToInt(pageLogicalHeight); } - return enclosingRenderFlowThread()->pageLogicalTopForOffset(cumulativeOffset); + return flowThread->pageLogicalTopForOffset(cumulativeOffset); } LayoutUnit RenderBlock::pageLogicalHeightForOffset(LayoutUnit offset) const { RenderView* renderView = view(); - if (!inRenderFlowThread()) + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) return renderView->layoutState()->m_pageLogicalHeight; - return enclosingRenderFlowThread()->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage()); + return flowThread->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage()); } LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const @@ -7064,7 +7521,8 @@ LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, P RenderView* renderView = view(); offset += offsetFromLogicalTopOfFirstPage(); - if (!inRenderFlowThread()) { + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) { LayoutUnit pageLogicalHeight = renderView->layoutState()->m_pageLogicalHeight; LayoutUnit remainingHeight = pageLogicalHeight - intMod(offset, pageLogicalHeight); if (pageBoundaryRule == IncludePageBoundary) { @@ -7075,25 +7533,24 @@ LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, P return remainingHeight; } - return enclosingRenderFlowThread()->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule); + return flowThread->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule); } LayoutUnit RenderBlock::adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins) { bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; - bool checkRegionBreaks = inRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); bool isUnsplittable = child->isUnsplittableForPagination() || (checkColumnBreaks && child->style()->columnBreakInside() == PBAVOID) || (checkPageBreaks && child->style()->pageBreakInside() == PBAVOID) || (checkRegionBreaks && child->style()->regionBreakInside() == PBAVOID); if (!isUnsplittable) return logicalOffset; LayoutUnit childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : LayoutUnit()); - LayoutState* layoutState = view()->layoutState(); - if (layoutState->m_columnInfo) - layoutState->m_columnInfo->updateMinimumColumnHeight(childLogicalHeight); LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); - bool hasUniformPageLogicalHeight = !inRenderFlowThread() || enclosingRenderFlowThread()->regionsHaveUniformLogicalHeight(); + bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); + updateMinimumPageHeight(logicalOffset, childLogicalHeight); if (!pageLogicalHeight || (hasUniformPageLogicalHeight && childLogicalHeight > pageLogicalHeight) || !hasNextPage(logicalOffset)) return logicalOffset; @@ -7121,7 +7578,39 @@ bool RenderBlock::pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, return !checkRegion; } -void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta) +void RenderBlock::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) +{ + if (RenderFlowThread* flowThread = flowThreadContainingBlock()) + flowThread->setPageBreak(offsetFromLogicalTopOfFirstPage() + offset, spaceShortage); +} + +void RenderBlock::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight) +{ + if (RenderFlowThread* flowThread = flowThreadContainingBlock()) + flowThread->updateMinimumPageHeight(offsetFromLogicalTopOfFirstPage() + offset, minHeight); + else if (ColumnInfo* colInfo = view()->layoutState()->m_columnInfo) + colInfo->updateMinimumColumnHeight(minHeight); +} + +static inline LayoutUnit calculateMinimumPageHeight(RenderStyle* renderStyle, RootInlineBox* lastLine, LayoutUnit lineTop, LayoutUnit lineBottom) +{ + // We may require a certain minimum number of lines per page in order to satisfy + // orphans and widows, and that may affect the minimum page height. + unsigned lineCount = max<unsigned>(renderStyle->hasAutoOrphans() ? 1 : renderStyle->orphans(), renderStyle->hasAutoWidows() ? 1 : renderStyle->widows()); + if (lineCount > 1) { + RootInlineBox* line = lastLine; + for (unsigned i = 1; i < lineCount && line->prevRootBox(); i++) + line = line->prevRootBox(); + + // FIXME: Paginating using line overflow isn't all fine. See FIXME in + // adjustLinePositionForPagination() for more details. + LayoutRect overflow = line->logicalVisualOverflowRect(line->lineTop(), line->lineBottom()); + lineTop = min(line->lineTopWithLeading(), overflow.y()); + } + return lineBottom - lineTop; +} + +void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta, RenderFlowThread* flowThread) { // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since @@ -7144,23 +7633,24 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, Layout // line and all following lines. LayoutRect logicalVisualOverflow = lineBox->logicalVisualOverflowRect(lineBox->lineTop(), lineBox->lineBottom()); LayoutUnit logicalOffset = min(lineBox->lineTopWithLeading(), logicalVisualOverflow.y()); - LayoutUnit lineHeight = max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY()) - logicalOffset; - RenderView* renderView = view(); - LayoutState* layoutState = renderView->layoutState(); - if (layoutState->m_columnInfo) - layoutState->m_columnInfo->updateMinimumColumnHeight(lineHeight); + LayoutUnit logicalBottom = max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY()); + LayoutUnit lineHeight = logicalBottom - logicalOffset; + updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(style(), lineBox, logicalOffset, logicalBottom)); logicalOffset += delta; lineBox->setPaginationStrut(0); lineBox->setIsFirstAfterPageBreak(false); LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); - bool hasUniformPageLogicalHeight = !inRenderFlowThread() || enclosingRenderFlowThread()->regionsHaveUniformLogicalHeight(); + bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); // If lineHeight is greater than pageLogicalHeight, but logicalVisualOverflow.height() still fits, we are // still going to add a strut, so that the visible overflow fits on a single page. if (!pageLogicalHeight || (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight) || !hasNextPage(logicalOffset)) return; LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary); - if (remainingLogicalHeight < lineHeight) { + + if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineBox)) { + if (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineBox) + clearShouldBreakAtLineToAvoidWidow(); // If we have a non-uniform page height, then we have to shift further possibly. if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, lineHeight)) return; @@ -7170,7 +7660,9 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, Layout } LayoutUnit totalLogicalHeight = lineHeight + max<LayoutUnit>(0, logicalOffset); LayoutUnit pageLogicalHeightAtNewOffset = hasUniformPageLogicalHeight ? pageLogicalHeight : pageLogicalHeightForOffset(logicalOffset + remainingLogicalHeight); - if (lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset && !isOutOfFlowPositioned() && !isTableCell()) + setPageBreak(logicalOffset, lineHeight - remainingLogicalHeight); + if (((lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset) || (!style()->hasAutoOrphans() && style()->orphans() >= lineCount(lineBox))) + && !isOutOfFlowPositioned() && !isTableCell()) setPaginationStrut(remainingLogicalHeight + max<LayoutUnit>(0, logicalOffset)); else { delta += remainingLogicalHeight; @@ -7215,6 +7707,21 @@ LayoutUnit RenderBlock::adjustBlockChildForPagination(LayoutUnit logicalTopAfter // If the object has a page or column break value of "before", then we should shift to the top of the next page. LayoutUnit result = applyBeforeBreak(child, logicalTopAfterClear); + if (pageLogicalHeightForOffset(result)) { + LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(result, ExcludePageBoundary); + LayoutUnit spaceShortage = child->logicalHeight() - remainingLogicalHeight; + if (spaceShortage > 0) { + // If the child crosses a column boundary, report a break, in case nothing inside it has already + // done so. The column balancer needs to know how much it has to stretch the columns to make more + // content fit. If no breaks are reported (but do occur), the balancer will have no clue. FIXME: + // This should be improved, though, because here we just pretend that the child is + // unsplittable. A splittable child, on the other hand, has break opportunities at every position + // where there's no child content, border or padding. In other words, we risk stretching more + // than necessary. + setPageBreak(result, spaceShortage); + } + } + // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one. LayoutUnit logicalTopBeforeUnsplittableAdjustment = result; LayoutUnit logicalTopAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, result); @@ -7247,19 +7754,16 @@ LayoutUnit RenderBlock::adjustBlockChildForPagination(LayoutUnit logicalTopAfter return result; } -bool RenderBlock::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta) const +bool RenderBlock::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta, RenderFlowThread* flowThread) const { - if (!inRenderFlowThread()) + if (!flowThread) return false; RenderRegion* currentRegion = regionAtBlockOffset(rootBox->lineTopWithLeading() + lineDelta); - // Just bail if we still don't have a region. - if (!rootBox->hasContainingRegion() && !currentRegion) - return false; // Just bail if the region didn't change. - if (rootBox->hasContainingRegion() && rootBox->containingRegion() == currentRegion) + if (rootBox->containingRegion() == currentRegion) return false; - return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion, offsetFromLogicalTopOfFirstPage()); + return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion); } LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const @@ -7267,94 +7771,71 @@ LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const LayoutState* layoutState = view()->layoutState(); if (layoutState && !layoutState->isPaginated()) return 0; + + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (flowThread) + return flowThread->offsetFromLogicalTopOfFirstRegion(this); + if (layoutState) { - // FIXME: Sanity check that the renderer in the layout state is ours, since otherwise the computation will be off. - // Right now this assert gets hit inside computeLogicalHeight for percentage margins, since they're computed using - // widths which can vary in each region. Until we patch that, we can't have this assert. - // ASSERT(layoutState->m_renderer == this); + ASSERT(layoutState->m_renderer == this); LayoutSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset; return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); } - // FIXME: Right now, this assert is hit outside layout, from logicalLeftSelectionOffset in selectionGapRectsForRepaint (called from FrameSelection::selectAll). - // ASSERT(inRenderFlowThread()); - - // FIXME: This is a slower path that doesn't use layout state and relies on getting your logical top inside the enclosing flow thread. It doesn't - // work with columns or pages currently, but it should once they have been switched over to using flow threads. - if (!inRenderFlowThread()) - return 0; - - const RenderBlock* currentBlock = this; - LayoutRect blockRect(0, 0, width(), height()); - - while (currentBlock && !currentBlock->isRenderFlowThread()) { - RenderBlock* containerBlock = currentBlock->containingBlock(); - ASSERT(containerBlock); - if (!containerBlock) - return 0; - LayoutPoint currentBlockLocation = currentBlock->location(); - - if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) { - // We have to put the block rect in container coordinates - // and we have to take into account both the container and current block flipping modes - if (containerBlock->style()->isFlippedBlocksWritingMode()) { - if (containerBlock->isHorizontalWritingMode()) - blockRect.setY(currentBlock->height() - blockRect.maxY()); - else - blockRect.setX(currentBlock->width() - blockRect.maxX()); - } - currentBlock->flipForWritingMode(blockRect); - } - blockRect.moveBy(currentBlockLocation); - currentBlock = containerBlock; - }; - return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x(); + + ASSERT_NOT_REACHED(); + return 0; } RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const { - if (!inRenderFlowThread()) - return 0; - - RenderFlowThread* flowThread = enclosingRenderFlowThread(); + RenderFlowThread* flowThread = flowThreadContainingBlock(); if (!flowThread || !flowThread->hasValidRegionInfo()) return 0; return flowThread->regionAtBlockOffset(offsetFromLogicalTopOfFirstPage() + blockOffset, true); } +void RenderBlock::updateStaticInlinePositionForChild(RenderBox* child, LayoutUnit logicalTop) +{ + if (child->style()->isOriginalDisplayInlineType()) + setStaticInlinePositionForChild(child, logicalTop, startAlignedOffsetForLine(logicalTop, false)); + else + setStaticInlinePositionForChild(child, logicalTop, startOffsetForContent(logicalTop)); +} + void RenderBlock::setStaticInlinePositionForChild(RenderBox* child, LayoutUnit blockOffset, LayoutUnit inlinePosition) { - if (inRenderFlowThread()) { + if (flowThreadContainingBlock()) { // Shift the inline position to exclude the region offset. inlinePosition += startOffsetForContent() - startOffsetForContent(blockOffset); } child->layer()->setStaticInlinePosition(inlinePosition); } -bool RenderBlock::logicalWidthChangedInRegions() const +bool RenderBlock::logicalWidthChangedInRegions(RenderFlowThread* flowThread) const { - if (!inRenderFlowThread()) - return false; - - RenderFlowThread* flowThread = enclosingRenderFlowThread(); if (!flowThread || !flowThread->hasValidRegionInfo()) - return 0; + return false; - return flowThread->logicalWidthChangedInRegions(this, offsetFromLogicalTopOfFirstPage()); + return flowThread->logicalWidthChangedInRegionsForBlock(this); } RenderRegion* RenderBlock::clampToStartAndEndRegions(RenderRegion* region) const { - ASSERT(region && inRenderFlowThread()); - + RenderFlowThread* flowThread = flowThreadContainingBlock(); + + ASSERT(isRenderView() || (region && flowThread)); + if (isRenderView()) + return region; + // We need to clamp to the block, since we want any lines or blocks that overflow out of the // logical top or logical bottom of the block to size as though the border box in the first and // last regions extended infinitely. Otherwise the lines are going to size according to the regions // they overflow into, which makes no sense when this block doesn't exist in |region| at all. RenderRegion* startRegion; RenderRegion* endRegion; - enclosingRenderFlowThread()->getRegionRangeForBox(this, startRegion, endRegion); + flowThread->getRegionRangeForBox(this, startRegion, endRegion); if (startRegion && region->logicalTopForFlowThreadContent() < startRegion->logicalTopForFlowThreadContent()) return startRegion; @@ -7398,6 +7879,40 @@ LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox* child) co return marginAfterForChild(child); } +bool RenderBlock::hasMarginBeforeQuirk(const RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // margin quirk. + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the opposite edge. + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk(); + + // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about + // whether or not authors specified quirky ems, since they're an implementation detail. + return false; +} + +bool RenderBlock::hasMarginAfterQuirk(const RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // margin quirk. + if (!child->isWritingModeRoot()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the opposite edge. + if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk(); + + // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about + // whether or not authors specified quirky ems, since they're an implementation detail. + return false; +} + RenderBlock::MarginValues RenderBlock::marginValuesForChild(RenderBox* child) const { LayoutUnit childBeforePositive = 0; @@ -7467,23 +7982,40 @@ const char* RenderBlock::renderName() const return "RenderBlock (floating)"; if (isOutOfFlowPositioned()) return "RenderBlock (positioned)"; - if (isAnonymousColumnsBlock()) + if (style() && isAnonymousColumnsBlock()) return "RenderBlock (anonymous multi-column)"; - if (isAnonymousColumnSpanBlock()) + if (style() && isAnonymousColumnSpanBlock()) return "RenderBlock (anonymous multi-column span)"; - if (isAnonymousBlock()) + if (style() && isAnonymousBlock()) return "RenderBlock (anonymous)"; - else if (isAnonymous()) + // FIXME: Temporary hack while the new generated content system is being implemented. + if (isPseudoElement()) + return "RenderBlock (generated)"; + if (isAnonymous()) return "RenderBlock (generated)"; if (isRelPositioned()) return "RenderBlock (relative positioned)"; if (isStickyPositioned()) return "RenderBlock (sticky positioned)"; - if (isRunIn()) + if (style() && isRunIn()) return "RenderBlock (run-in)"; return "RenderBlock"; } +inline RenderBlock::FloatingObjects::FloatingObjects(const RenderBlock* renderer, bool horizontalWritingMode) + : m_placedFloatsTree(UninitializedTree) + , m_leftObjectsCount(0) + , m_rightObjectsCount(0) + , m_horizontalWritingMode(horizontalWritingMode) + , m_renderer(renderer) +{ +} + +void RenderBlock::createFloatingObjects() +{ + m_floatingObjects = adoptPtr(new FloatingObjects(this, isHorizontalWritingMode())); +} + inline void RenderBlock::FloatingObjects::clear() { m_set.clear(); @@ -7511,8 +8043,8 @@ inline void RenderBlock::FloatingObjects::decreaseObjectsCount(FloatingObject::T inline RenderBlock::FloatingObjectInterval RenderBlock::FloatingObjects::intervalForFloatingObject(FloatingObject* floatingObject) { if (m_horizontalWritingMode) - return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().pixelSnappedY(), floatingObject->frameRect().pixelSnappedMaxY(), floatingObject); - return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().pixelSnappedX(), floatingObject->frameRect().pixelSnappedMaxX(), floatingObject); + return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().y(), floatingObject->frameRect().maxY(), floatingObject); + return RenderBlock::FloatingObjectInterval(floatingObject->frameRect().x(), floatingObject->frameRect().maxX(), floatingObject); } void RenderBlock::FloatingObjects::addPlacedObject(FloatingObject* floatingObject) @@ -7660,15 +8192,18 @@ TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, c RenderBlock* RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderObject* parent, EDisplay display) { - // FIXME: Do we need to cover the new flex box here ? // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ? EDisplay newDisplay; RenderBlock* newBox = 0; if (display == BOX || display == INLINE_BOX) { - newBox = new (parent->renderArena()) RenderDeprecatedFlexibleBox(parent->document() /* anonymous box */); + // FIXME: Remove this case once we have eliminated all internal users of old flexbox + newBox = RenderDeprecatedFlexibleBox::createAnonymous(parent->document()); newDisplay = BOX; + } else if (display == FLEX || display == INLINE_FLEX) { + newBox = RenderFlexibleBox::createAnonymous(parent->document()); + newDisplay = FLEX; } else { - newBox = new (parent->renderArena()) RenderBlock(parent->document() /* anonymous box */); + newBox = RenderBlock::createAnonymous(parent->document()); newDisplay = BLOCK; } @@ -7682,7 +8217,7 @@ RenderBlock* RenderBlock::createAnonymousColumnsWithParentRenderer(const RenderO RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK); newStyle->inheritColumnPropertiesFrom(parent->style()); - RenderBlock* newBox = new (parent->renderArena()) RenderBlock(parent->document() /* anonymous box */); + RenderBlock* newBox = RenderBlock::createAnonymous(parent->document()); newBox->setStyle(newStyle.release()); return newBox; } @@ -7692,7 +8227,7 @@ RenderBlock* RenderBlock::createAnonymousColumnSpanWithParentRenderer(const Rend RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK); newStyle->setColumnSpan(ColumnSpanAll); - RenderBlock* newBox = new (parent->renderArena()) RenderBlock(parent->document() /* anonymous box */); + RenderBlock* newBox = RenderBlock::createAnonymous(parent->document()); newBox->setStyle(newStyle.release()); return newBox; } @@ -7727,7 +8262,7 @@ String ValueToString<int>::string(const int value) String ValueToString<RenderBlock::FloatingObject*>::string(const RenderBlock::FloatingObject* floatingObject) { - return String::format("%p (%dx%d %dx%d)", floatingObject, floatingObject->frameRect().pixelSnappedX(), floatingObject->frameRect().pixelSnappedY(), floatingObject->frameRect().pixelSnappedMaxX(), floatingObject->frameRect().pixelSnappedMaxY()); + return String::format("%p (%ix%i %ix%i)", floatingObject, floatingObject->frameRect().x().toInt(), floatingObject->frameRect().y().toInt(), floatingObject->frameRect().maxX().toInt(), floatingObject->frameRect().maxY().toInt()); } #endif |