diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/rendering/RenderBlock.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/rendering/RenderBlock.cpp | 1820 |
1 files changed, 518 insertions, 1302 deletions
diff --git a/chromium/third_party/WebKit/Source/core/rendering/RenderBlock.cpp b/chromium/third_party/WebKit/Source/core/rendering/RenderBlock.cpp index 84c790b6ab7..e7cf72c32b3 100644 --- a/chromium/third_party/WebKit/Source/core/rendering/RenderBlock.cpp +++ b/chromium/third_party/WebKit/Source/core/rendering/RenderBlock.cpp @@ -24,7 +24,7 @@ #include "config.h" #include "core/rendering/RenderBlock.h" -#include "HTMLNames.h" +#include "core/HTMLNames.h" #include "core/accessibility/AXObjectCache.h" #include "core/dom/Document.h" #include "core/dom/Element.h" @@ -33,8 +33,8 @@ #include "core/editing/Editor.h" #include "core/editing/FrameSelection.h" #include "core/fetch/ResourceLoadPriorityOptimizer.h" -#include "core/frame/Frame.h" #include "core/frame/FrameView.h" +#include "core/frame/LocalFrame.h" #include "core/page/Page.h" #include "core/frame/Settings.h" #include "core/rendering/FastTextAutosizer.h" @@ -43,18 +43,19 @@ #include "core/rendering/HitTestResult.h" #include "core/rendering/InlineIterator.h" #include "core/rendering/InlineTextBox.h" -#include "core/rendering/LayoutRectRecorder.h" #include "core/rendering/LayoutRepainter.h" #include "core/rendering/PaintInfo.h" #include "core/rendering/RenderCombineText.h" #include "core/rendering/RenderDeprecatedFlexibleBox.h" #include "core/rendering/RenderFlexibleBox.h" +#include "core/rendering/RenderFlowThread.h" +#include "core/rendering/RenderGrid.h" #include "core/rendering/RenderInline.h" #include "core/rendering/RenderLayer.h" #include "core/rendering/RenderMarquee.h" -#include "core/rendering/RenderNamedFlowThread.h" #include "core/rendering/RenderRegion.h" #include "core/rendering/RenderTableCell.h" +#include "core/rendering/RenderTextControl.h" #include "core/rendering/RenderTextFragment.h" #include "core/rendering/RenderTheme.h" #include "core/rendering/RenderView.h" @@ -63,6 +64,7 @@ #include "core/rendering/style/RenderStyle.h" #include "platform/geometry/FloatQuad.h" #include "platform/geometry/TransformState.h" +#include "platform/graphics/GraphicsContextCullSaver.h" #include "platform/graphics/GraphicsContextStateSaver.h" #include "wtf/StdLibExtras.h" #include "wtf/TemporaryChange.h" @@ -82,7 +84,14 @@ struct SameSizeAsRenderBlock : public RenderBox { uint32_t bitfields; }; +struct SameSizeAsRenderBlockRareData { + int paginationStrut; + int pageLogicalOffset; + uint32_t bitfields; +}; + COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small); +COMPILE_ASSERT(sizeof(RenderBlock::RenderBlockRareData) == sizeof(SameSizeAsRenderBlockRareData), RenderBlockRareData_should_stay_small); typedef WTF::HashMap<const RenderBox*, OwnPtr<ColumnInfo> > ColumnInfoMap; static ColumnInfoMap* gColumnInfoMap = 0; @@ -108,6 +117,8 @@ class OverflowEventDispatcher { public: OverflowEventDispatcher(const RenderBlock* block) : m_block(block) + , m_hadHorizontalLayoutOverflow(false) + , m_hadVerticalLayoutOverflow(false) { m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document().hasListenerType(Document::OVERFLOWCHANGED_LISTENER); if (m_shouldDispatchEvent) { @@ -130,7 +141,7 @@ public: if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged) return; - RefPtr<OverflowEvent> event = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); + RefPtrWillBeRawPtr<OverflowEvent> event = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); event->setTarget(m_block->node()); m_block->document().enqueueAnimationFrameEvent(event.release()); } @@ -144,12 +155,12 @@ private: RenderBlock::RenderBlock(ContainerNode* node) : RenderBox(node) - , m_lineHeight(-1) , m_hasMarginBeforeQuirk(false) , m_hasMarginAfterQuirk(false) , m_beingDestroyed(false) , m_hasMarkupTruncation(false) , m_hasBorderOrPaddingLogicalWidthChanged(false) + , m_hasOnlySelfCollapsingChildren(false) { setChildrenInline(true); } @@ -174,8 +185,11 @@ static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, Tracke static void appendImageIfNotNull(Vector<ImageResource*>& imageResources, const StyleImage* styleImage) { - if (styleImage && styleImage->cachedImage()) - imageResources.append(styleImage->cachedImage()); + if (styleImage && styleImage->cachedImage()) { + ImageResource* imageResource = styleImage->cachedImage(); + if (imageResource && !imageResource->isLoaded()) + imageResources.append(styleImage->cachedImage()); + } } static void appendLayers(Vector<ImageResource*>& images, const FillLayer* styleLayer) @@ -185,6 +199,25 @@ static void appendLayers(Vector<ImageResource*>& images, const FillLayer* styleL } } +static void appendImagesFromStyle(Vector<ImageResource*>& images, RenderStyle& blockStyle) +{ + appendLayers(images, blockStyle.backgroundLayers()); + appendLayers(images, blockStyle.maskLayers()); + + const ContentData* contentData = blockStyle.contentData(); + if (contentData && contentData->isImage()) { + const ImageContentData* imageContentData = static_cast<const ImageContentData*>(contentData); + appendImageIfNotNull(images, imageContentData->image()); + } + if (blockStyle.boxReflect()) + appendImageIfNotNull(images, blockStyle.boxReflect()->mask().image()); + appendImageIfNotNull(images, blockStyle.listStyleImage()); + appendImageIfNotNull(images, blockStyle.borderImageSource()); + appendImageIfNotNull(images, blockStyle.maskBoxImageSource()); + if (blockStyle.shapeOutside()) + appendImageIfNotNull(images, blockStyle.shapeOutside()->image()); +} + RenderBlock::~RenderBlock() { if (hasColumns()) @@ -240,25 +273,27 @@ void RenderBlock::willBeDestroyed() if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0)) gDelayedUpdateScrollInfoSet->remove(this); - FastTextAutosizer* textAutosizer = document().fastTextAutosizer(); - if (textAutosizer) + if (FastTextAutosizer* textAutosizer = document().fastTextAutosizer()) textAutosizer->destroy(this); RenderBox::willBeDestroyed(); } -void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) +void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) { RenderStyle* oldStyle = style(); - setReplaced(newStyle->isDisplayInlineType()); + setReplaced(newStyle.isDisplayInlineType()); - if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle->position()) { - if (newStyle->position() == StaticPosition) + if (oldStyle && parent()) { + bool oldStyleIsContainer = oldStyle->position() != StaticPosition || oldStyle->hasTransformRelatedProperty(); + bool newStyleIsContainer = newStyle.position() != StaticPosition || newStyle.hasTransformRelatedProperty(); + + if (oldStyleIsContainer && !newStyleIsContainer) { // Clear our positioned objects list. Our absolutely positioned descendants will be // inserted into our containing block's positioned objects list during layout. removePositionedObjects(0, NewContainingBlock); - else if (oldStyle->position() == StaticPosition) { + } else if (!oldStyleIsContainer && newStyleIsContainer) { // Remove our absolutely positioned descendants from their current containing block. // They will be inserted into our positioned objects list during layout. RenderObject* cb = parent(); @@ -298,8 +333,6 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty RenderStyle* newStyle = style(); - updateShapeInsideInfoAfterStyleChange(newStyle->resolvedShapeInside(), oldStyle ? oldStyle->resolvedShapeInside() : RenderStyle::initialShapeInside()); - if (!isAnonymousBlock()) { // Ensure that all of our continuation blocks pick up the new style. for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { @@ -310,16 +343,63 @@ void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldSty } } - FastTextAutosizer* textAutosizer = document().fastTextAutosizer(); - if (textAutosizer) + if (FastTextAutosizer* textAutosizer = document().fastTextAutosizer()) textAutosizer->record(this); propagateStyleToAnonymousChildren(true); - m_lineHeight = -1; // 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); + m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff.needsFullLayout() && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, newStyle); + + // If the style has unloaded images, want to notify the ResourceLoadPriorityOptimizer so that + // network priorities can be set. + Vector<ImageResource*> images; + appendImagesFromStyle(images, *newStyle); + if (images.isEmpty()) + ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->removeRenderObject(this); + else + ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->addRenderObject(this); +} + +void RenderBlock::invalidateTreeAfterLayout(const RenderLayerModelObject& invalidationContainer) +{ + // Note, we don't want to early out here using shouldCheckForInvalidationAfterLayout as + // we have to make sure we go through any positioned objects as they won't be seen in + // the normal tree walk. + + if (shouldCheckForPaintInvalidationAfterLayout()) + RenderBox::invalidateTreeAfterLayout(invalidationContainer); + + // Take care of positioned objects. This is required as LayoutState keeps a single clip rect. + if (TrackedRendererListHashSet* positionedObjects = this->positionedObjects()) { + TrackedRendererListHashSet::iterator end = positionedObjects->end(); + LayoutState state(*this, isTableRow() ? LayoutSize() : locationOffset()); + for (TrackedRendererListHashSet::iterator it = positionedObjects->begin(); it != end; ++it) { + RenderBox* box = *it; + + // One of the renderers we're skipping over here may be the child's repaint container, + // so we can't pass our own repaint container along. + const RenderLayerModelObject& repaintContainerForChild = *box->containerForPaintInvalidation(); + + // If the positioned renderer is absolutely positioned and it is inside + // a relatively positioend inline element, we need to account for + // the inline elements position in LayoutState. + if (box->style()->position() == AbsolutePosition) { + RenderObject* container = box->container(&repaintContainerForChild, 0); + if (container->isInFlowPositioned() && container->isRenderInline()) { + // FIXME: We should be able to use layout-state for this. + // Currently, we will place absolutly positioned elements inside + // relatively positioned inline blocks in the wrong location. crbug.com/371485 + ForceHorriblySlowRectMapping slowRectMapping(*this); + box->invalidateTreeAfterLayout(repaintContainerForChild); + continue; + } + } + + box->invalidateTreeAfterLayout(repaintContainerForChild); + } + } } RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) @@ -458,7 +538,7 @@ RenderBlockFlow* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBl { RenderBlock* firstChildIgnoringAnonymousWrappers = 0; for (RenderObject* curr = this; curr; curr = curr->parent()) { - if (!curr->isRenderBlock() || curr->isFloatingOrOutOfFlowPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip() + if (!curr->isRenderBlock() || curr->isFloatingOrOutOfFlowPositioned() || curr->isTableCell() || curr->isDocumentElement() || curr->isRenderView() || curr->hasOverflowClip() || curr->isInlineBlockOrInlineTable()) return 0; @@ -622,9 +702,9 @@ void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) // get deleted properly. Because objects moves from the pre block into the post block, we want to // make new line boxes instead of leaving the old line boxes around. - pre->setNeedsLayoutAndPrefWidthsRecalc(); - block->setNeedsLayoutAndPrefWidthsRecalc(); - post->setNeedsLayoutAndPrefWidthsRecalc(); + pre->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); + block->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); + post->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); } void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlockFlow* newBlockBox, RenderObject* newChild) @@ -672,10 +752,10 @@ void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, R // get deleted properly. Because objects moved from the pre block into the post block, we want to // make new line boxes instead of leaving the old line boxes around. if (pre) - pre->setNeedsLayoutAndPrefWidthsRecalc(); - block->setNeedsLayoutAndPrefWidthsRecalc(); + pre->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); + block->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); if (post) - post->setNeedsLayoutAndPrefWidthsRecalc(); + post->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); } RenderBlockFlow* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) @@ -725,7 +805,7 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, || beforeChildAnonymousContainer->isRenderFullScreenPlaceholder() ) { // Insert the child into the anonymous block box instead of here. - if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild) + if (newChild->isInline() || newChild->isFloatingOrOutOfFlowPositioned() || beforeChild->parent()->slowFirstChild() != beforeChild) beforeChild->parent()->addChild(newChild, beforeChild); else addChild(newChild, beforeChild->parent()); @@ -751,7 +831,7 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, } // Check for a spanning element in columns. - if (gColumnFlowSplitEnabled) { + if (gColumnFlowSplitEnabled && !document().regionBasedColumnsEnabled()) { RenderBlockFlow* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); if (columnsBlockAncestor) { TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); @@ -886,19 +966,6 @@ void RenderBlock::deleteLineBoxTree() cache->recomputeIsIgnored(this); } -RootInlineBox* RenderBlock::createAndAppendRootInlineBox() -{ - RootInlineBox* rootBox = createRootInlineBox(); - m_lineBoxes.appendLineBox(rootBox); - - if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox) { - if (AXObjectCache* cache = document().existingAXObjectCache()) - cache->recomputeIsIgnored(this); - } - - return rootBox; -} - void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) { // makeChildrenNonInline takes a block whose children are *all* inline and it @@ -938,7 +1005,7 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) ASSERT(!c->isInline()); #endif - repaint(); + paintInvalidationForWholeRenderer(); } void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) @@ -986,6 +1053,10 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) // Remove all the information in the flow thread associated with the leftover anonymous block. child->removeFromRenderFlowThread(); + // RenderGrid keeps track of its children, we must notify it about changes in the tree. + if (child->parent()->isRenderGrid()) + toRenderGrid(child->parent())->dirtyGrid(); + child->setParent(0); child->setPreviousSibling(0); child->setNextSibling(0); @@ -1021,7 +1092,7 @@ void RenderBlock::collapseAnonymousBlockChild(RenderBlock* parent, RenderBlock* // destroyed. See crbug.com/282088 if (child->beingDestroyed()) return; - parent->setNeedsLayoutAndPrefWidthsRecalc(); + parent->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); parent->setChildrenInline(child->childrenInline()); RenderObject* nextSibling = child->nextSibling(); @@ -1033,8 +1104,6 @@ void RenderBlock::collapseAnonymousBlockChild(RenderBlock* parent, RenderBlock* // Explicitly delete the child's line box tree, or the special anonymous // block handling in willBeDestroyed will cause problems. child->deleteLineBoxTree(); - if (childFlowThread && childFlowThread->isRenderNamedFlowThread()) - toRenderNamedFlowThread(childFlowThread)->removeFlowChildInfo(child); child->destroy(); } @@ -1057,9 +1126,9 @@ void RenderBlock::removeChild(RenderObject* oldChild) RenderObject* next = oldChild->nextSibling(); bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next); if (canMergeAnonymousBlocks && prev && next) { - prev->setNeedsLayoutAndPrefWidthsRecalc(); - RenderBlock* nextBlock = toRenderBlock(next); - RenderBlock* prevBlock = toRenderBlock(prev); + prev->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); + RenderBlockFlow* nextBlock = toRenderBlockFlow(next); + RenderBlockFlow* prevBlock = toRenderBlockFlow(prev); if (prev->childrenInline() != next->childrenInline()) { RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; @@ -1079,7 +1148,7 @@ void RenderBlock::removeChild(RenderObject* oldChild) // Now just put the inlineChildrenBlock inside the blockChildrenBlock. blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0, inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer()); - next->setNeedsLayoutAndPrefWidthsRecalc(); + next->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child // of "this". we null out prev or next so that is not used later in the function. @@ -1128,7 +1197,7 @@ void RenderBlock::removeChild(RenderObject* oldChild) // we need to remove ourself and fix the continuation chain. if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild->isListMarker()) { RenderObject* containingBlockIgnoringAnonymous = containingBlock(); - while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymousBlock()) + while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymous()) containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock(); for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) { if (curr->virtualContinuation() != this) @@ -1160,6 +1229,20 @@ bool RenderBlock::isSelfCollapsingBlock() const // (c) have border/padding, // (d) have a min-height // (e) have specified that one of our margins can't collapse using a CSS extension + // (f) establish a new block formatting context. + + // The early exit must be done before we check for clean layout. + // We should be able to give a quick answer if the box is a relayout boundary. + // Being a relayout boundary implies a block formatting context, and also + // our internal layout shouldn't affect our container in any way. + if (createsBlockFormattingContext()) + return false; + + // Placeholder elements are not laid out until the dimensions of their parent text control are known, so they + // don't get layout until their parent has had layout - this is unique in the layout tree and means + // when we call isSelfCollapsingBlock on them we find that they still need layout. + ASSERT(!needsLayout() || (node() && node()->isElementNode() && toElement(node())->shadowPseudoId() == "-webkit-input-placeholder")); + if (logicalHeight() > 0 || isTable() || borderAndPaddingLogicalHeight() || style()->logicalMinHeight().isPositive() @@ -1186,6 +1269,8 @@ bool RenderBlock::isSelfCollapsingBlock() const // Whether or not we collapse is dependent on whether all our normal flow children // are also self-collapsing. + if (m_hasOnlySelfCollapsingChildren) + return true; for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { if (child->isFloatingOrOutOfFlowPositioned()) continue; @@ -1248,7 +1333,6 @@ void RenderBlock::updateScrollInfoAfterLayout() void RenderBlock::layout() { OverflowEventDispatcher dispatcher(this); - LayoutRectRecorder recorder(*this); // Update our first letter info now. updateFirstLetter(); @@ -1257,9 +1341,6 @@ void RenderBlock::layout() // layoutBlock(). layoutBlock(false); - if (frameView()->partialLayout().isStopping()) - return; - // 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) @@ -1268,42 +1349,13 @@ void RenderBlock::layout() invalidateBackgroundObscurationStatus(); } -void RenderBlock::didLayout(ResourceLoadPriorityOptimizer& optimizer) -{ - RenderBox::didLayout(optimizer); - updateStyleImageLoadingPriorities(optimizer); -} - -void RenderBlock::didScroll(ResourceLoadPriorityOptimizer& optimizer) -{ - RenderBox::didScroll(optimizer); - updateStyleImageLoadingPriorities(optimizer); -} - -void RenderBlock::updateStyleImageLoadingPriorities(ResourceLoadPriorityOptimizer& optimizer) +bool RenderBlock::updateImageLoadingPriorities() { - RenderStyle* blockStyle = style(); - if (!blockStyle) - return; - Vector<ImageResource*> images; - - appendLayers(images, blockStyle->backgroundLayers()); - appendLayers(images, blockStyle->maskLayers()); - - const ContentData* contentData = blockStyle->contentData(); - if (contentData && contentData->isImage()) { - const ImageContentData* imageContentData = static_cast<const ImageContentData*>(contentData); - appendImageIfNotNull(images, imageContentData->image()); - } - if (blockStyle->boxReflect()) - appendImageIfNotNull(images, blockStyle->boxReflect()->mask().image()); - appendImageIfNotNull(images, blockStyle->listStyleImage()); - appendImageIfNotNull(images, blockStyle->borderImageSource()); - appendImageIfNotNull(images, blockStyle->maskBoxImageSource()); + appendImagesFromStyle(images, *style()); if (images.isEmpty()) - return; + return false; LayoutRect viewBounds = viewRect(); LayoutRect objectBounds = absoluteContentBox(); @@ -1318,165 +1370,16 @@ void RenderBlock::updateStyleImageLoadingPriorities(ResourceLoadPriorityOptimize ResourceLoadPriorityOptimizer::VisibilityStatus status = isVisible ? ResourceLoadPriorityOptimizer::Visible : ResourceLoadPriorityOptimizer::NotVisible; - for (Vector<ImageResource*>::iterator it = images.begin(), end = images.end(); it != end; ++it) - optimizer.notifyImageResourceVisibility(*it, status); -} - -void RenderBlock::relayoutShapeDescendantIfMoved(RenderBlock* child, LayoutSize offset) -{ - LayoutUnit left = isHorizontalWritingMode() ? offset.width() : offset.height(); - if (!left || !child || child->shapeInsideInfo() || !layoutShapeInsideInfo()) - return; - // Propagate layout markers only up to the child, as we are still in the middle - // of a layout pass - child->setNormalChildNeedsLayout(true); - child->markShapeInsideDescendantsForLayout(); - child->layoutIfNeeded(); -} - -ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const -{ - if (ShapeInsideInfo* shapeInsideInfo = view()->layoutState()->shapeInsideInfo()) - return shapeInsideInfo; - - RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (allowsShapeInsideInfoSharing(flowThread)) { - LayoutUnit lineHeight = this->lineHeight(false, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); - // regionAtBlockOffset returns regions like an array first={0,N-1}, second={N,M-1}, ... - LayoutUnit offset = logicalHeight() + lineHeight - LayoutUnit(1); - RenderRegion* region = regionAtBlockOffset(offset); - if (region && region->logicalHeight()) - return region->shapeInsideInfo(); - } - - return 0; -} - -LayoutSize RenderBlock::logicalOffsetFromShapeAncestorContainer(const RenderBlock* container) const -{ - const RenderBlock* currentBlock = this; - LayoutRect blockRect(currentBlock->borderBoxRect()); - while (currentBlock && !currentBlock->isRenderFlowThread() && currentBlock != container) { - RenderBlock* containerBlock = currentBlock->containingBlock(); - ASSERT(containerBlock); - if (!containerBlock) - return LayoutSize(); - - 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 - // Bug: Flipping inline and block directions at the same time will not work, - // as one of the flipped dimensions will not yet have been set to its final size - 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(currentBlock->location()); - currentBlock = containerBlock; - } - - LayoutSize result = isHorizontalWritingMode() ? LayoutSize(blockRect.x(), blockRect.y()) : LayoutSize(blockRect.y(), blockRect.x()); - return result; -} - -void RenderBlock::imageChanged(WrappedImagePtr image, const IntRect*) -{ - RenderBox::imageChanged(image); - - if (!parent() || !everHadLayout()) - return; - - ShapeValue* shapeValue = style()->shapeInside(); - if (shapeValue && shapeValue->image() && shapeValue->image()->data() == image) { - ShapeInsideInfo* shapeInsideInfo = ensureShapeInsideInfo(); - shapeInsideInfo->dirtyShapeSize(); - markShapeInsideDescendantsForLayout(); - } - - ShapeValue* shapeOutsideValue = style()->shapeOutside(); - if (isFloating() && shapeOutsideValue && shapeOutsideValue->image() && shapeOutsideValue->image()->data() == image) - parent()->setNeedsLayoutAndPrefWidthsRecalc(); -} - -void RenderBlock::updateShapeInsideInfoAfterStyleChange(const ShapeValue* shapeInside, const ShapeValue* oldShapeInside) -{ - // FIXME: A future optimization would do a deep comparison for equality. - if (shapeInside == oldShapeInside) - return; - - if (shapeInside) { - ShapeInsideInfo* shapeInsideInfo = ensureShapeInsideInfo(); - shapeInsideInfo->dirtyShapeSize(); - } else { - setShapeInsideInfo(nullptr); - markShapeInsideDescendantsForLayout(); - } -} - -static inline bool shapeInfoRequiresRelayout(const RenderBlock* block) -{ - ShapeInsideInfo* info = block->shapeInsideInfo(); - if (info) - info->setNeedsLayout(info->shapeSizeDirty()); - else - info = block->layoutShapeInsideInfo(); - return info && info->needsLayout(); -} - -bool RenderBlock::updateRegionsAndShapesLogicalSize(RenderFlowThread* flowThread) -{ - if (!flowThread && !shapeInsideInfo()) - return shapeInfoRequiresRelayout(this); - - LayoutUnit oldHeight = logicalHeight(); - LayoutUnit oldTop = logicalTop(); - - // Compute the maximum logical height content may cause this block to expand to - // FIXME: These should eventually use the const computeLogicalHeight rather than updateLogicalHeight - setLogicalHeight(RenderFlowThread::maxLogicalHeight()); - updateLogicalHeight(); - - computeShapeSize(); - - // Set our start and end regions. No regions above or below us will be considered by our children. They are - // effectively clamped to our region range. - computeRegionRangeForBlock(flowThread); - - setLogicalHeight(oldHeight); - setLogicalTop(oldTop); - - return shapeInfoRequiresRelayout(this); -} - -void RenderBlock::computeShapeSize() -{ - ShapeInsideInfo* shapeInsideInfo = this->shapeInsideInfo(); - if (!shapeInsideInfo) - return; - - if (isRenderNamedFlowFragment()) { - ShapeInsideInfo* parentShapeInsideInfo = toRenderBlock(parent())->shapeInsideInfo(); - ASSERT(parentShapeInsideInfo); - shapeInsideInfo->setShapeSize(parentShapeInsideInfo->shapeSize().width(), parentShapeInsideInfo->shapeSize().height()); - } else { - bool percentageLogicalHeightResolvable = percentageLogicalHeightIsResolvableFromBlock(this, false); - shapeInsideInfo->setShapeSize(logicalWidth(), percentageLogicalHeightResolvable ? logicalHeight() : LayoutUnit()); + LayoutRect screenArea; + if (!objectBounds.isEmpty()) { + screenArea = viewBounds; + screenArea.intersect(objectBounds); } -} -void RenderBlock::updateRegionsAndShapesAfterChildLayout(RenderFlowThread* flowThread, bool heightChanged) -{ - // A previous sibling has changed dimension, so we need to relayout the shape with the content - ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); - if (heightChanged && shapeInsideInfo) - shapeInsideInfo->dirtyShapeSize(); + for (Vector<ImageResource*>::iterator it = images.begin(), end = images.end(); it != end; ++it) + ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->notifyImageResourceVisibility(*it, status, screenArea); - computeRegionRangeForBlock(flowThread); + return true; } void RenderBlock::computeRegionRangeForBlock(RenderFlowThread* flowThread) @@ -1499,37 +1402,7 @@ bool RenderBlock::updateLogicalWidthAndColumnWidth() return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged; } -void RenderBlock::checkForPaginationLogicalHeightChange(LayoutUnit& pageLogicalHeight, bool& pageLogicalHeightChanged, bool& hasSpecifiedPageLogicalHeight) -{ - ColumnInfo* colInfo = columnInfo(); - if (hasColumns()) { - if (!pageLogicalHeight) { - // We need to go ahead and set our explicit page height if one exists, so that we can - // avoid doing two layout passes. - updateLogicalHeight(); - LayoutUnit columnHeight = contentLogicalHeight(); - if (columnHeight > 0) { - pageLogicalHeight = columnHeight; - hasSpecifiedPageLogicalHeight = true; - } - setLogicalHeight(0); - } - if (colInfo->columnHeight() != pageLogicalHeight && everHadLayout()) { - colInfo->setColumnHeight(pageLogicalHeight); - pageLogicalHeightChanged = true; - } - - if (!hasSpecifiedPageLogicalHeight && !pageLogicalHeight) - colInfo->clearForcedBreaks(); - - colInfo->setPaginationUnit(paginationUnit()); - } else if (isRenderFlowThread()) { - pageLogicalHeight = 1; // This is just a hack to always make sure we have a page logical height. - pageLogicalHeightChanged = toRenderFlowThread(this)->pageLogicalSizeChanged(); - } -} - -void RenderBlock::layoutBlock(bool, LayoutUnit) +void RenderBlock::layoutBlock(bool) { ASSERT_NOT_REACHED(); clearNeedsLayout(); @@ -1577,14 +1450,9 @@ void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); } - // Add visual overflow from box-shadow and border-image-outset. addVisualEffectOverflow(); - // Add visual overflow from theme. addVisualOverflowFromTheme(); - - if (isRenderNamedFlowThread()) - toRenderNamedFlowThread(this)->computeOversetStateForRegions(oldClientAfterEdge); } void RenderBlock::addOverflowFromBlockChildren() @@ -1622,79 +1490,10 @@ void RenderBlock::addVisualOverflowFromTheme() addVisualOverflow(inflatedRect); } -bool RenderBlock::expandsToEncloseOverhangingFloats() const +bool RenderBlock::createsBlockFormattingContext() const { - return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated()) - || hasColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot(); -} - -LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox* child, LayoutUnit childMarginStart, RenderRegion* region) -{ - LayoutUnit startPosition = startOffsetForContent(region); - - // Add in our start margin. - LayoutUnit oldPosition = startPosition + childMarginStart; - LayoutUnit newPosition = oldPosition; - - LayoutUnit blockOffset = logicalTopForChild(child); - if (region) - blockOffset = max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage())); - - LayoutUnit startOff = startOffsetForLineInRegion(blockOffset, false, region, logicalHeightForChild(child)); - - if (style()->textAlign() != WEBKIT_CENTER && !child->style()->marginStartUsing(style()).isAuto()) { - if (childMarginStart < 0) - startOff += childMarginStart; - newPosition = max(newPosition, startOff); // Let the float sit in the child's margin if it can fit. - } else if (startOff != startPosition) - newPosition = startOff + childMarginStart; - - return newPosition - oldPosition; -} - -void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child, ApplyLayoutDeltaMode applyDelta) -{ - LayoutUnit startPosition = borderStart() + paddingStart(); - if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) - startPosition -= verticalScrollbarWidth(); - LayoutUnit totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + availableLogicalWidth(); - - // Add in our start margin. - LayoutUnit childMarginStart = marginStartForChild(child); - LayoutUnit newPosition = startPosition + childMarginStart; - - // 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() && !flowThreadContainingBlock()) - newPosition += computeStartPositionDeltaForChildAvoidingFloats(child, marginStartForChild(child)); - - setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), applyDelta); -} - -void RenderBlock::setLogicalLeftForChild(RenderBox* child, LayoutUnit logicalLeft, ApplyLayoutDeltaMode applyDelta) -{ - if (isHorizontalWritingMode()) { - if (applyDelta == ApplyLayoutDelta) - view()->addLayoutDelta(LayoutSize(child->x() - logicalLeft, 0)); - child->setX(logicalLeft); - } else { - if (applyDelta == ApplyLayoutDelta) - view()->addLayoutDelta(LayoutSize(0, child->y() - logicalLeft)); - child->setY(logicalLeft); - } -} - -void RenderBlock::setLogicalTopForChild(RenderBox* child, LayoutUnit logicalTop, ApplyLayoutDeltaMode applyDelta) -{ - if (isHorizontalWritingMode()) { - if (applyDelta == ApplyLayoutDelta) - view()->addLayoutDelta(LayoutSize(0, child->y() - logicalTop)); - child->setY(logicalTop); - } else { - if (applyDelta == ApplyLayoutDelta) - view()->addLayoutDelta(LayoutSize(child->x() - logicalTop, 0)); - child->setX(logicalTop); - } + return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || isFlexItemIncludingDeprecated() + || style()->specifiesColumns() || isRenderFlowThread() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isDocumentElement() || style()->columnSpan(); } void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox* child) @@ -1718,8 +1517,8 @@ void RenderBlock::simplifiedNormalFlowLayout() if (!o->isOutOfFlowPositioned() && (o->isReplaced() || o->isFloating())) { o->layoutIfNeeded(); if (toRenderBox(o)->inlineBoxWrapper()) { - RootInlineBox* box = toRenderBox(o)->inlineBoxWrapper()->root(); - lineBoxes.add(box); + RootInlineBox& box = toRenderBox(o)->inlineBoxWrapper()->root(); + lineBoxes.add(&box); } } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) { o->clearNeedsLayout(); @@ -1742,45 +1541,49 @@ void RenderBlock::simplifiedNormalFlowLayout() bool RenderBlock::simplifiedLayout() { - if ((!posChildNeedsLayout() && !needsSimplifiedNormalFlowLayout()) || normalChildNeedsLayout() || selfNeedsLayout()) + // Check if we need to do a full layout. + if (normalChildNeedsLayout() || selfNeedsLayout()) return false; - LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); - - if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) + // Check that we actually need to do a simplified layout. + if (!posChildNeedsLayout() && !(needsSimplifiedNormalFlowLayout() || needsPositionedMovementLayout())) return false; - // Lay out positioned descendants or objects that just need to recompute overflow. - if (needsSimplifiedNormalFlowLayout()) - simplifiedNormalFlowLayout(); - - // Make sure a forced break is applied after the content if we are a flow thread in a simplified layout. - // This ensures the size information is correctly computed for the last auto-height region receiving content. - if (isRenderFlowThread()) - toRenderFlowThread(this)->applyBreakAfterContent(clientLogicalBottom()); - - // Lay out our positioned objects if our positioned child bit is set. - // 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. - // 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(); + { + // LayoutState needs this deliberate scope to pop before repaint + LayoutState state(*this, locationOffset()); + + if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) + return false; + + FastTextAutosizer::LayoutScope fastTextAutosizerLayoutScope(this); - updateLayerTransform(); + // Lay out positioned descendants or objects that just need to recompute overflow. + if (needsSimplifiedNormalFlowLayout()) + simplifiedNormalFlowLayout(); + + // Lay out our positioned objects if our positioned child bit is set. + // 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() || needsPositionedMovementLayout() || canContainFixedPosObjects) + layoutPositionedObjects(false, needsPositionedMovementLayout() ? ForcedLayoutAfterContainingBlockMoved : (!posChildNeedsLayout() && canContainFixedPosObjects ? LayoutOnlyFixedPositionedObjects : DefaultLayout)); + + // 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. + // 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); + } + + updateLayerTransformAfterLayout(); updateScrollInfoAfterLayout(); @@ -1806,9 +1609,10 @@ void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject* child, RenderBox* box = toRenderBox(child); if (hasStaticInlinePosition) { - LayoutUnit oldLeft = box->logicalLeft(); - box->updateLogicalWidth(); - if (box->logicalLeft() != oldLeft) + LogicalExtentComputedValues computedValues; + box->computeLogicalWidth(computedValues); + LayoutUnit newLeft = computedValues.m_position; + if (newLeft != box->logicalLeft()) layoutScope.setChildNeedsLayout(child); } else if (hasStaticBlockPosition) { LayoutUnit oldTop = box->logicalTop(); @@ -1833,7 +1637,7 @@ LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox* child) co return margin; } -void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly) +void RenderBlock::layoutPositionedObjects(bool relayoutChildren, PositionedLayoutBehavior info) { TrackedRendererListHashSet* positionedDescendants = positionedObjects(); if (!positionedDescendants) @@ -1847,12 +1651,15 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPosit for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { r = *it; - SubtreeLayoutScope layoutScope(r); + // FIXME: this should only be set from clearNeedsLayout crbug.com/361250 + r->setLayoutDidGetCalled(true); + + SubtreeLayoutScope layoutScope(*r); // 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, layoutScope); - if (fixedPositionObjectsOnly) { + if (info == LayoutOnlyFixedPositionedObjects) { r->layoutIfNeeded(); continue; } @@ -1871,11 +1678,6 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPosit if (!r->needsLayout()) r->markForPaginationRelayoutIfNeeded(layoutScope); - // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width - // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout. - if (r->needsPositionedMovementLayoutOnly() && r->tryLayoutDoingPositionedMovementOnly()) - r->clearNeedsLayout(); - // If we are paginated or in a line grid, go ahead and compute a vertical position for our object now. // If it's wrong we'll lay out again. LayoutUnit oldLogicalTop = 0; @@ -1888,6 +1690,11 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPosit oldLogicalTop = logicalTopForChild(r); } + // FIXME: We should be able to do a r->setNeedsPositionedMovementLayout() here instead of a full layout. Need + // to investigate why it does not trigger the correct invalidations in that case. crbug.com/350756 + if (info == ForcedLayoutAfterContainingBlockMoved) + r->setNeedsLayoutAndFullPaintInvalidation(); + r->layoutIfNeeded(); // Lay out again if our estimate was wrong. @@ -1896,7 +1703,7 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPosit } if (hasColumns()) - view()->layoutState()->m_columnInfo = columnInfo(); // FIXME: Kind of gross. We just put this back into the layout state so that pop() will work. + view()->layoutState()->setColumnInfo(columnInfo()); // FIXME: Kind of gross. We just put this back into the layout state so that pop() will work. } void RenderBlock::markPositionedObjectsForLayout() @@ -1918,7 +1725,7 @@ void RenderBlock::markForPaginationRelayoutIfNeeded(SubtreeLayoutScope& layoutSc 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())) layoutScope.setChildNeedsLayout(this); } @@ -1930,13 +1737,13 @@ void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) PaintPhase phase = paintInfo.phase; + LayoutRect overflowBox; // Check if we need to do anything at all. - // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView + // FIXME: Could eliminate the isDocumentElement() check if we fix background painting so that the RenderView // paints the root's background. - if (!isRoot()) { - LayoutRect overflowBox = overflowRectForPaintRejection(); + if (!isDocumentElement()) { + overflowBox = overflowRectForPaintRejection(); flipForWritingMode(overflowBox); - overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); overflowBox.moveBy(adjustedPaintOffset); if (!overflowBox.intersects(paintInfo.rect)) return; @@ -1949,7 +1756,16 @@ void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) contentsClipBehavior = SkipContentsClipIfPossible; bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, contentsClipBehavior); - paintObject(paintInfo, adjustedPaintOffset); + { + GraphicsContextCullSaver cullSaver(*paintInfo.context); + // Cull if we have more than one child and we didn't already clip. + bool shouldCull = document().settings()->containerCullingEnabled() && !pushedClip && !isDocumentElement() + && firstChild() && lastChild() && firstChild() != lastChild(); + if (shouldCull) + cullSaver.cull(overflowBox); + + paintObject(paintInfo, adjustedPaintOffset); + } if (pushedClip) popContentsClip(paintInfo, phase, adjustedPaintOffset); @@ -1980,7 +1796,7 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain bool antialias = shouldAntialiasLines(paintInfo.context); if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { - bool leftToRight = style()->isLeftToRightDirection() ^ colInfo->progressionIsReversed(); + bool leftToRight = style()->isLeftToRightDirection(); LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth(); LayoutUnit ruleAdd = logicalLeftOffsetForContent(); LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth(); @@ -2012,13 +1828,13 @@ void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& pain ruleLogicalLeft = currLogicalLeftOffset; } } else { - bool topToBottom = !style()->isFlippedBlocksWritingMode() ^ colInfo->progressionIsReversed(); + bool topToBottom = !style()->isFlippedBlocksWritingMode(); LayoutUnit ruleLeft = isHorizontalWritingMode() ? borderLeft() + paddingLeft() - : colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter()); + : colGap / 2 - colGap - ruleThickness / 2 + borderBefore() + paddingBefore(); LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness; LayoutUnit ruleTop = isHorizontalWritingMode() - ? colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderBefore() + paddingBefore() : borderAfter() + paddingAfter()) + ? colGap / 2 - colGap - ruleThickness / 2 + borderBefore() + paddingBefore() : borderStart() + paddingStart(); LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight(); LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); @@ -2072,7 +1888,7 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& p } colRect.moveBy(paintOffset); PaintInfo info(paintInfo); - info.rect.intersect(pixelSnappedIntRect(colRect)); + info.rect.intersect(enclosingIntRect(colRect)); if (!info.rect.isEmpty()) { GraphicsContextStateSaver stateSaver(*context); @@ -2088,7 +1904,7 @@ void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& p // like overflow:hidden. // FIXME: Content and column rules that extend outside column boxes at the edges of the multi-column element // are clipped according to the 'overflow' property. - context->clip(pixelSnappedIntRect(clipRect)); + context->clip(enclosingIntRect(clipRect)); // Adjust our x and y when painting. LayoutPoint adjustedPaintOffset = paintOffset + offset; @@ -2173,31 +1989,42 @@ void RenderBlock::paintAsInlineBlock(RenderObject* renderer, PaintInfo& paintInf } } -bool RenderBlock::hasCaret(CaretType type) const +static inline bool caretBrowsingEnabled(const Frame* frame) { - // Paint the caret if the FrameSelection says so or if caret browsing is enabled - bool caretBrowsing = frame()->settings() && frame()->settings()->caretBrowsingEnabled(); - RenderObject* caretPainter; - bool isContentEditable; - if (type == CursorCaret) { - caretPainter = frame()->selection().caretRenderer(); - isContentEditable = frame()->selection().rendererIsEditable(); - } else { - caretPainter = frame()->page()->dragCaretController().caretRenderer(); - isContentEditable = frame()->page()->dragCaretController().isContentEditable(); - } - return caretPainter == this && (isContentEditable || caretBrowsing); + Settings* settings = frame->settings(); + return settings && settings->caretBrowsingEnabled(); } -void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffset, CaretType type) +static inline bool hasCursorCaret(const FrameSelection& selection, const RenderBlock* block, bool caretBrowsing) { - if (!hasCaret(type)) - return; + return selection.caretRenderer() == block && (selection.rendererIsEditable() || caretBrowsing); +} - if (type == CursorCaret) - frame()->selection().paintCaret(paintInfo.context, paintOffset, paintInfo.rect); - else - frame()->page()->dragCaretController().paintDragCaret(frame(), paintInfo.context, paintOffset, paintInfo.rect); +static inline bool hasDragCaret(const DragCaretController& dragCaretController, const RenderBlock* block, bool caretBrowsing) +{ + return dragCaretController.caretRenderer() == block && (dragCaretController.isContentEditable() || caretBrowsing); +} + +bool RenderBlock::hasCaret() const +{ + bool caretBrowsing = caretBrowsingEnabled(frame()); + return hasCursorCaret(frame()->selection(), this, caretBrowsing) + || hasDragCaret(frame()->page()->dragCaretController(), this, caretBrowsing); +} + +void RenderBlock::paintCarets(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +{ + bool caretBrowsing = caretBrowsingEnabled(frame()); + + FrameSelection& selection = frame()->selection(); + if (hasCursorCaret(selection, this, caretBrowsing)) { + selection.paintCaret(paintInfo.context, paintOffset, paintInfo.rect); + } + + DragCaretController& dragCaretController = frame()->page()->dragCaretController(); + if (hasDragCaret(dragCaretController, this, caretBrowsing)) { + dragCaretController.paintDragCaret(frame(), paintInfo.context, paintOffset, paintInfo.rect); + } } void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) @@ -2287,8 +2114,7 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground, // then paint the caret. if (paintPhase == PaintPhaseForeground) { - paintCaret(paintInfo, paintOffset, CursorCaret); - paintCaret(paintInfo, paintOffset, DragCaret); + paintCarets(paintInfo, paintOffset); } } @@ -2383,11 +2209,11 @@ bool RenderBlock::isSelectionRoot() const if (isTable()) return false; - if (isBody() || isRoot() || hasOverflowClip() + if (isBody() || isDocumentElement() || hasOverflowClip() || isPositioned() || isFloating() || isTableCell() || isInlineBlockOrInlineTable() || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot() - || isRenderFlowThread()) + || isRenderFlowThread() || isFlexItemIncludingDeprecated()) return true; if (view() && view()->selectionStart()) { @@ -2430,17 +2256,16 @@ void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintO LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo); if (!gapRectsBounds.isEmpty()) { - if (RenderLayer* layer = enclosingLayer()) { - gapRectsBounds.moveBy(-paintOffset); - if (!hasLayer()) { - LayoutRect localBounds(gapRectsBounds); - flipForWritingMode(localBounds); - gapRectsBounds = localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox(); - if (layer->renderer()->hasOverflowClip()) - gapRectsBounds.move(layer->renderBox()->scrolledContentOffset()); - } - layer->addBlockSelectionGapsBounds(gapRectsBounds); + RenderLayer* layer = enclosingLayer(); + gapRectsBounds.moveBy(-paintOffset); + if (!hasLayer()) { + LayoutRect localBounds(gapRectsBounds); + flipForWritingMode(localBounds); + gapRectsBounds = localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox(); + if (layer->renderer()->hasOverflowClip()) + gapRectsBounds.move(layer->renderBox()->scrolledContentOffset()); } + layer->addBlockSelectionGapsBounds(gapRectsBounds); } } } @@ -2490,7 +2315,7 @@ GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& r rootBlock->flipForWritingMode(flippedBlockRect); flippedBlockRect.moveBy(rootBlockPhysicalPosition); clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), positionedObjects()); - if (isBody() || isRoot()) // The <body> must make sure to examine its containingBlock's positioned objects. + if (isBody() || isDocumentElement()) // The <body> must make sure to examine its containingBlock's positioned objects. for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock()) clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes. clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock); @@ -2650,38 +2475,18 @@ void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) { - 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()); - return logicalLeft; - } else { - RenderBlock* cb = this; - while (cb != rootBlock) { - logicalLeft += cb->logicalLeft(); - cb = cb->containingBlock(); - } - } - return logicalLeft; + // The border can potentially be further extended by our containingBlock(). + if (rootBlock != this) + return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop()); + return logicalLeftOffsetForContent(); } LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) { - 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()); - return logicalRight; - } else { - RenderBlock* cb = this; - while (cb != rootBlock) { - logicalRight += cb->logicalLeft(); - cb = cb->containingBlock(); - } - } - return logicalRight; + // The border can potentially be further extended by our containingBlock(). + if (rootBlock != this) + return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop()); + return logicalRightOffsetForContent(); } RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot(LayoutSize& offset) const @@ -2900,7 +2705,7 @@ void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox* descendant) void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox* parent) { ASSERT(gPercentHeightContainerMap); - for (RenderObject* curr = parent->firstChild(); curr; curr = curr->nextInPreOrder(parent)) { + for (RenderObject* curr = parent->slowFirstChild(); curr; curr = curr->nextInPreOrder(parent)) { if (!curr->isBox()) continue; @@ -2915,107 +2720,9 @@ void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox* parent) LayoutUnit RenderBlock::textIndentOffset() const { LayoutUnit cw = 0; - RenderView* renderView = 0; if (style()->textIndent().isPercent()) cw = containingBlock()->availableLogicalWidth(); - else if (style()->textIndent().isViewportPercentage()) - renderView = view(); - return minimumValueForLength(style()->textIndent(), cw, renderView); -} - -LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region) const -{ - LayoutUnit logicalLeftOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); - if (!region) - return logicalLeftOffset; - LayoutRect boxRect = borderBoxRectInRegion(region); - return logicalLeftOffset + (isHorizontalWritingMode() ? boxRect.x() : boxRect.y()); -} - -LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region) const -{ - LayoutUnit logicalRightOffset = style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); - logicalRightOffset += availableLogicalWidth(); - if (!region) - return logicalRightOffset; - LayoutRect boxRect = borderBoxRectInRegion(region); - return logicalRightOffset - (logicalWidth() - (isHorizontalWritingMode() ? boxRect.maxX() : boxRect.maxY())); -} - -LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const -{ - LayoutUnit left = offsetFromFloats; - - if (applyTextIndent && style()->isLeftToRightDirection()) - left += textIndentOffset(); - - if (style()->lineAlign() == LineAlignNone) - return left; - - // Push in our left offset so that it is aligned with the character grid. - LayoutState* layoutState = view()->layoutState(); - if (!layoutState) - return left; - - RenderBlock* lineGrid = layoutState->lineGrid(); - if (!lineGrid || lineGrid->style()->writingMode() != style()->writingMode()) - return left; - - // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge? - float maxCharWidth = lineGrid->style()->font().primaryFont()->maxCharWidth(); - if (!maxCharWidth) - return left; - - LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height(); - LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height(); - - // Push in to the nearest character width. - // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945). - // FIXME: This doesn't work with columns or regions (https://bugs.webkit.org/show_bug.cgi?id=79942). - // FIXME: This doesn't work when the inline position of the object isn't set ahead of time. - // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout. - // (https://bugs.webkit.org/show_bug.cgi?id=79944) - float remainder = fmodf(maxCharWidth - fmodf(left + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth); - left += remainder; - return left; -} - -LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const -{ - LayoutUnit right = offsetFromFloats; - - if (applyTextIndent && !style()->isLeftToRightDirection()) - right -= textIndentOffset(); - - if (style()->lineAlign() == LineAlignNone) - return right; - - // Push in our right offset so that it is aligned with the character grid. - LayoutState* layoutState = view()->layoutState(); - if (!layoutState) - return right; - - RenderBlock* lineGrid = layoutState->lineGrid(); - if (!lineGrid || lineGrid->style()->writingMode() != style()->writingMode()) - return right; - - // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge? - float maxCharWidth = lineGrid->style()->font().primaryFont()->maxCharWidth(); - if (!maxCharWidth) - return right; - - LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height(); - LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height(); - - // Push in to the nearest character width. - // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945). - // FIXME: This doesn't work with columns or regions (https://bugs.webkit.org/show_bug.cgi?id=79942). - // FIXME: This doesn't work when the inline position of the object isn't set ahead of time. - // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout. - // (https://bugs.webkit.org/show_bug.cgi?id=79944) - float remainder = fmodf(fmodf(right + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth); - right -= LayoutUnit::fromFloatCeil(remainder); - return right; + return minimumValueForLength(style()->textIndent(), cw); } void RenderBlock::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest) @@ -3042,22 +2749,6 @@ bool RenderBlock::avoidsFloats() const return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); } -void RenderBlock::markShapeInsideDescendantsForLayout() -{ - if (!everHadLayout()) - return; - if (childrenInline()) { - setNeedsLayout(); - return; - } - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (!child->isRenderBlock()) - continue; - RenderBlock* childBlock = toRenderBlock(child); - childBlock->markShapeInsideDescendantsForLayout(); - } -} - bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset) { if (!scrollsOverflow()) @@ -3081,24 +2772,53 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu if (!isRenderView()) { // Check if we need to do anything at all. - LayoutRect overflowBox = visualOverflowRect(); + // If we have clipping, then we can't have any spillout. + LayoutRect overflowBox = hasOverflowClip() ? borderBoxRect() : visualOverflowRect(); flipForWritingMode(overflowBox); overflowBox.moveBy(adjustedLocation); if (!locationInContainer.intersects(overflowBox)) return false; } - if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) { + if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) + && visibleToHitTestRequest(request) + && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) { updateHitTestResult(result, locationInContainer.point() - localOffset); // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet. if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer)) return true; } + if (style()->clipPath()) { + switch (style()->clipPath()->type()) { + case ClipPathOperation::SHAPE: { + ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style()->clipPath()); + // FIXME: handle marginBox etc. + if (!clipPath->path(borderBoxRect()).contains(locationInContainer.point() - localOffset, clipPath->windRule())) + return false; + break; + } + case ClipPathOperation::REFERENCE: + // FIXME: handle REFERENCE + break; + } + } + // If we have clipping, then we can't have any spillout. bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); bool useClip = (hasControlClip() || useOverflowClip); - bool checkChildren = !useClip || (hasControlClip() ? locationInContainer.intersects(controlClipRect(adjustedLocation)) : locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region(), IncludeOverlayScrollbarSize))); + bool checkChildren = !useClip; + if (!checkChildren) { + if (hasControlClip()) { + checkChildren = locationInContainer.intersects(controlClipRect(adjustedLocation)); + } else { + LayoutRect clipRect = overflowClipRect(adjustedLocation, IncludeOverlayScrollbarSize); + if (style()->hasBorderRadius()) + checkChildren = locationInContainer.intersects(style()->getRoundedBorderFor(clipRect)); + else + checkChildren = locationInContainer.intersects(clipRect); + } + } if (checkChildren) { // Hit test descendants first. LayoutSize scrolledOffset(localOffset); @@ -3120,10 +2840,10 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu } // Check if the point is outside radii. - if (!isRenderView() && style()->hasBorderRadius()) { + if (style()->hasBorderRadius()) { LayoutRect borderRect = borderBoxRect(); borderRect.moveBy(adjustedLocation); - RoundedRect border = style()->getRoundedBorderFor(borderRect, view()); + RoundedRect border = style()->getRoundedBorderFor(borderRect); if (!locationInContainer.intersects(border)) return false; } @@ -3239,9 +2959,6 @@ void RenderBlock::adjustForColumnRect(LayoutSize& offset, const LayoutPoint& loc bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { - if (isRenderRegion()) - return toRenderRegion(this)->hitTestFlowThreadContents(request, result, locationInContainer, accumulatedOffset, hitTestAction); - if (childrenInline() && !isTable()) { // We have to hit-test our line boxes. if (m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction)) @@ -3266,14 +2983,14 @@ Position RenderBlock::positionForBox(InlineBox *box, bool start) const if (!box) return Position(); - if (!box->renderer()->nonPseudoNode()) + if (!box->renderer().nonPseudoNode()) return createLegacyEditingPosition(nonPseudoNode(), start ? caretMinOffset() : caretMaxOffset()); if (!box->isInlineTextBox()) - return createLegacyEditingPosition(box->renderer()->nonPseudoNode(), 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()->nonPseudoNode(), 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) @@ -3385,12 +3102,12 @@ PositionWithAffinity RenderBlock::positionForPointWithInlineChildren(const Layou } // pass the box a top position that is inside it - LayoutPoint point(pointInLogicalContents.x(), closestBox->root()->blockDirectionPointInLine()); + LayoutPoint point(pointInLogicalContents.x(), closestBox->root().blockDirectionPointInLine()); if (!isHorizontalWritingMode()) point = point.transposedPoint(); - if (closestBox->renderer()->isReplaced()) - return positionForPointRespectingEditingBoundaries(this, toRenderBox(closestBox->renderer()), point); - return closestBox->renderer()->positionForPoint(point); + if (closestBox->renderer().isReplaced()) + return positionForPointRespectingEditingBoundaries(this, &toRenderBox(closestBox->renderer()), point); + return closestBox->renderer().positionForPoint(point); } if (lastRootBoxWithChildren) { @@ -3505,7 +3222,7 @@ void RenderBlock::calcColumnWidth() LayoutUnit desiredColumnWidth = contentLogicalWidth(); // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination. - if (document().paginated() || (style()->hasAutoColumnCount() && style()->hasAutoColumnWidth()) || !style()->hasInlineColumnAxis()) { + if (document().paginated() || !style()->specifiesColumns()) { setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); return; } @@ -3530,12 +3247,12 @@ void RenderBlock::calcColumnWidth() bool RenderBlock::requiresColumns(int desiredColumnCount) const { - // If overflow-y is set to paged-x or paged-y on the body or html element, we'll handle the paginating - // in the RenderView instead. - bool isPaginated = (style()->overflowY() == OPAGEDX || style()->overflowY() == OPAGEDY) && !(isRoot() || isBody()); + // Paged overflow is treated as multicol here, unless this element was the one that got its + // overflow propagated to the viewport. + bool isPaginated = style()->isOverflowPaged() && node() != document().viewportDefiningElement(); return firstChild() - && (desiredColumnCount != 1 || !style()->hasAutoColumnWidth() || !style()->hasInlineColumnAxis() || isPaginated) + && (desiredColumnCount != 1 || !style()->hasAutoColumnWidth() || isPaginated) && !firstChild()->isAnonymousColumnsBlock() && !firstChild()->isAnonymousColumnSpanBlock(); } @@ -3559,37 +3276,15 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width) gColumnInfoMap->add(this, adoptPtr(info)); setHasColumns(true); } - info->setDesiredColumnCount(count); info->setDesiredColumnWidth(width); - info->setProgressionAxis(style()->hasInlineColumnAxis() ? ColumnInfo::InlineAxis : ColumnInfo::BlockAxis); - info->setProgressionIsReversed(style()->columnProgression() == ReverseColumnProgression); - } -} - -void RenderBlock::updateColumnInfoFromStyle(RenderStyle* style) -{ - if (!hasColumns()) - return; - - ColumnInfo* info = gColumnInfoMap->get(this); - - bool needsLayout = false; - ColumnInfo::Axis oldAxis = info->progressionAxis(); - ColumnInfo::Axis newAxis = style->hasInlineColumnAxis() ? ColumnInfo::InlineAxis : ColumnInfo::BlockAxis; - if (oldAxis != newAxis) { - info->setProgressionAxis(newAxis); - needsLayout = true; - } - - bool oldProgressionIsReversed = info->progressionIsReversed(); - bool newProgressionIsReversed = style->columnProgression() == ReverseColumnProgression; - if (oldProgressionIsReversed != newProgressionIsReversed) { - info->setProgressionIsReversed(newProgressionIsReversed); - needsLayout = true; + if (style()->isOverflowPaged()) { + info->setDesiredColumnCount(1); + info->setProgressionAxis(style()->hasInlinePaginationAxis() ? ColumnInfo::InlineAxis : ColumnInfo::BlockAxis); + } else { + info->setDesiredColumnCount(count); + info->setProgressionAxis(ColumnInfo::InlineAxis); + } } - - if (needsLayout) - setNeedsLayoutAndPrefWidthsRecalc(); } LayoutUnit RenderBlock::desiredColumnWidth() const @@ -3624,15 +3319,12 @@ LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const LayoutUnit colLogicalLeft = logicalLeftOffsetForContent(); LayoutUnit colGap = columnGap(); if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { - if (style()->isLeftToRightDirection() ^ colInfo->progressionIsReversed()) + if (style()->isLeftToRightDirection()) colLogicalLeft += index * (colLogicalWidth + colGap); else colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap); } else { - if (!colInfo->progressionIsReversed()) - colLogicalTop += index * (colLogicalHeight + colGap); - else - colLogicalTop += contentLogicalHeight() - colLogicalHeight - index * (colLogicalHeight + colGap); + colLogicalTop += index * (colLogicalHeight + colGap); } if (isHorizontalWritingMode()) @@ -3640,17 +3332,6 @@ LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); } -bool RenderBlock::relayoutToAvoidWidows(LayoutStateMaintainer& statePusher) -{ - if (!shouldBreakAtLineToAvoidWidow()) - return false; - - statePusher.pop(); - setEverHadLayout(true); - layoutBlock(false); - return true; -} - void RenderBlock::adjustPointToColumnContents(LayoutPoint& point) const { // Just bail if we have no columns. @@ -3826,10 +3507,10 @@ void RenderBlock::adjustStartEdgeForWritingModeIncludingColumns(LayoutRect& rect rect.setX(expandedLogicalHeight - rect.maxX()); } -void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) const +LayoutSize RenderBlock::columnOffset(const LayoutPoint& point) const { if (!hasColumns()) - return; + return LayoutSize(); ColumnInfo* colInfo = columnInfo(); @@ -3850,30 +3531,29 @@ void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) if (isHorizontalWritingMode()) { if (point.y() >= sliceRect.y() && point.y() < sliceRect.maxY()) { if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) - offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset); - else - offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderBefore() - paddingBefore()); - return; + return LayoutSize(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset); + return LayoutSize(0, columnRectAt(colInfo, i).y() - logicalOffset - borderBefore() - paddingBefore()); } } else { if (point.x() >= sliceRect.x() && point.x() < sliceRect.maxX()) { if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) - offset.expand(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft); - else - offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderBefore() - paddingBefore(), 0); - return; + return LayoutSize(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft); + return LayoutSize(columnRectAt(colInfo, i).x() - logicalOffset - borderBefore() - paddingBefore(), 0); } } } + + return LayoutSize(); } void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { if (childrenInline()) { // FIXME: Remove this const_cast. - const_cast<RenderBlock*>(this)->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); - } else + toRenderBlockFlow(const_cast<RenderBlock*>(this))->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); + } else { computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); + } maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth); @@ -3937,9 +3617,6 @@ void RenderBlock::computePreferredLogicalWidths() void RenderBlock::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { - // FIXME: make this method virtual and move the code to RenderMultiColumnBlock once the old - // multicol code is gone. - if (!style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth()) { // The min/max intrinsic widths calculated really tell how much space elements need when // laid out inside the columns. In order to eventually end up with the desired column width, @@ -3962,429 +3639,6 @@ void RenderBlock::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalW } } -struct InlineMinMaxIterator { -/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to - inline min/max width calculations. Note the following about the way it walks: - (1) Positioned content is skipped (since it does not contribute to min/max width of a block) - (2) We do not drill into the children of floats or replaced elements, since you can't break - in the middle of such an element. - (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have - distinct borders/margin/padding that contribute to the min/max width. -*/ - RenderObject* parent; - RenderObject* current; - bool endOfInline; - - InlineMinMaxIterator(RenderObject* p, bool end = false) - :parent(p), current(p), endOfInline(end) {} - - RenderObject* next(); -}; - -RenderObject* InlineMinMaxIterator::next() -{ - RenderObject* result = 0; - bool oldEndOfInline = endOfInline; - endOfInline = false; - while (current || current == parent) { - if (!oldEndOfInline && - (current == parent || - (!current->isFloating() && !current->isReplaced() && !current->isOutOfFlowPositioned()))) - result = current->firstChild(); - if (!result) { - // We hit the end of our inline. (It was empty, e.g., <span></span>.) - if (!oldEndOfInline && current->isRenderInline()) { - result = current; - endOfInline = true; - break; - } - - while (current && current != parent) { - result = current->nextSibling(); - if (result) break; - current = current->parent(); - if (current && current != parent && current->isRenderInline()) { - result = current; - endOfInline = true; - break; - } - } - } - - if (!result) - break; - - if (!result->isOutOfFlowPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isRenderInline())) - break; - - current = result; - result = 0; - } - - // Update our position. - current = result; - return current; -} - -static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit) -{ - if (cssUnit.type() != Auto) - return (cssUnit.isFixed() ? static_cast<LayoutUnit>(cssUnit.value()) : childValue); - return 0; -} - -static LayoutUnit getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfInline) -{ - RenderStyle* childStyle = child->style(); - if (endOfInline) - return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) + - getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) + - child->borderEnd(); - return getBPMWidth(child->marginStart(), childStyle->marginStart()) + - getBPMWidth(child->paddingStart(), childStyle->paddingStart()) + - child->borderStart(); -} - -static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, - RenderObject* trailingSpaceChild) -{ - if (trailingSpaceChild && trailingSpaceChild->isText()) { - // Collapse away the trailing space at the end of a block. - RenderText* t = toRenderText(trailingSpaceChild); - const UChar space = ' '; - const Font& font = t->style()->font(); // FIXME: This ignores first-line. - float spaceWidth = font.width(RenderBlockFlow::constructTextRun(t, font, &space, 1, t->style())); - inlineMax -= spaceWidth + font.wordSpacing(); - if (inlineMin > inlineMax) - inlineMin = inlineMax; - } -} - -static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& result) -{ - LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result); - preferredWidth = max(snappedResult, preferredWidth); -} - -// When converting between floating point and LayoutUnits we risk losing precision -// with each conversion. When this occurs while accumulating our preferred widths, -// we can wind up with a line width that's larger than our maxPreferredWidth due to -// pure float accumulation. -static inline LayoutUnit adjustFloatForSubPixelLayout(float value) -{ - return LayoutUnit::fromFloatCeil(value); -} - - -void RenderBlock::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) -{ - float inlineMax = 0; - float inlineMin = 0; - - RenderStyle* styleToUse = style(); - RenderBlock* containingBlock = this->containingBlock(); - LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit(); - - // If we are at the start of a line, we want to ignore all white-space. - // Also strip spaces if we previously had text that ended in a trailing space. - bool stripFrontSpaces = true; - RenderObject* trailingSpaceChild = 0; - - // Firefox and Opera will allow a table cell to grow to fit an image inside it under - // very specific cirucumstances (in order to match common WinIE renderings). - // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) - bool allowImagesToBreak = !document().inQuirksMode() || !isTableCell() || !styleToUse->logicalWidth().isIntrinsicOrAuto(); - - bool autoWrap, oldAutoWrap; - autoWrap = oldAutoWrap = styleToUse->autoWrap(); - - InlineMinMaxIterator childIterator(this); - - // Only gets added to the max preffered width once. - bool addedTextIndent = false; - // Signals the text indent was more negative than the min preferred width - bool hasRemainingNegativeTextIndent = false; - - LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw, view()); - RenderObject* prevFloat = 0; - bool isPrevChildInlineFlow = false; - bool shouldBreakLineAfterText = false; - while (RenderObject* child = childIterator.next()) { - autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() : - child->style()->autoWrap(); - - if (!child->isBR()) { - // Step One: determine whether or not we need to go ahead and - // terminate our current line. Each discrete chunk can become - // the new min-width, if it is the widest chunk seen so far, and - // it can also become the max-width. - - // Children fall into three categories: - // (1) An inline flow object. These objects always have a min/max of 0, - // and are included in the iteration solely so that their margins can - // be added in. - // - // (2) An inline non-text non-flow object, e.g., an inline replaced element. - // These objects can always be on a line by themselves, so in this situation - // we need to go ahead and break the current line, and then add in our own - // margins and min/max width on its own line, and then terminate the line. - // - // (3) A text object. Text runs can have breakable characters at the start, - // the middle or the end. They may also lose whitespace off the front if - // we're already ignoring whitespace. In order to compute accurate min-width - // information, we need three pieces of information. - // (a) the min-width of the first non-breakable run. Should be 0 if the text string - // starts with whitespace. - // (b) the min-width of the last non-breakable run. Should be 0 if the text string - // ends with whitespace. - // (c) the min/max width of the string (trimmed for whitespace). - // - // If the text string starts with whitespace, then we need to go ahead and - // terminate our current line (unless we're already in a whitespace stripping - // mode. - // - // If the text string has a breakable character in the middle, but didn't start - // with whitespace, then we add the width of the first non-breakable run and - // then end the current line. We then need to use the intermediate min/max width - // values (if any of them are larger than our current min/max). We then look at - // the width of the last non-breakable run and use that to start a new line - // (unless we end in whitespace). - RenderStyle* childStyle = child->style(); - float childMin = 0; - float childMax = 0; - - if (!child->isText()) { - // Case (1) and (2). Inline replaced and inline flow elements. - if (child->isRenderInline()) { - // Add in padding/border/margin from the appropriate side of - // the element. - float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline); - childMin += bpm; - childMax += bpm; - - inlineMin += childMin; - inlineMax += childMax; - - child->clearPreferredLogicalWidthsDirty(); - } else { - // Inline replaced elts add in their margins to their min/max values. - LayoutUnit margins = 0; - Length startMargin = childStyle->marginStart(); - Length endMargin = childStyle->marginEnd(); - if (startMargin.isFixed()) - margins += adjustFloatForSubPixelLayout(startMargin.value()); - if (endMargin.isFixed()) - margins += adjustFloatForSubPixelLayout(endMargin.value()); - childMin += margins.ceilToFloat(); - childMax += margins.ceilToFloat(); - } - } - - if (!child->isRenderInline() && !child->isText()) { - // Case (2). Inline replaced elements and floats. - // Go ahead and terminate the current line as far as - // minwidth is concerned. - LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth; - if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) { - RenderBox* childBox = toRenderBox(child); - LogicalExtentComputedValues computedValues; - childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues); - childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent; - } else { - childMinPreferredLogicalWidth = child->minPreferredLogicalWidth(); - childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); - } - childMin += childMinPreferredLogicalWidth.ceilToFloat(); - childMax += childMaxPreferredLogicalWidth.ceilToFloat(); - - bool clearPreviousFloat; - if (child->isFloating()) { - clearPreviousFloat = (prevFloat - && ((prevFloat->style()->floating() == LeftFloat && (childStyle->clear() & CLEFT)) - || (prevFloat->style()->floating() == RightFloat && (childStyle->clear() & CRIGHT)))); - prevFloat = child; - } else - clearPreviousFloat = false; - - bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; - if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - - // If we're supposed to clear the previous float, then terminate maxwidth as well. - if (clearPreviousFloat) { - updatePreferredWidth(maxLogicalWidth, inlineMax); - inlineMax = 0; - } - - // Add in text-indent. This is added in only once. - if (!addedTextIndent && !child->isFloating()) { - float ceiledTextIndent = textIndent.ceilToFloat(); - childMin += ceiledTextIndent; - childMax += ceiledTextIndent; - - if (childMin < 0) - textIndent = adjustFloatForSubPixelLayout(childMin); - else - addedTextIndent = true; - } - - // Add our width to the max. - inlineMax += max<float>(0, childMax); - - if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) { - if (child->isFloating()) - updatePreferredWidth(minLogicalWidth, childMin); - else - inlineMin += childMin; - } else { - // Now check our line. - updatePreferredWidth(minLogicalWidth, childMin); - - // Now start a new line. - inlineMin = 0; - } - - if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - - // We are no longer stripping whitespace at the start of - // a line. - if (!child->isFloating()) { - stripFrontSpaces = false; - trailingSpaceChild = 0; - } - } else if (child->isText()) { - // Case (3). Text. - RenderText* t = toRenderText(child); - - if (t->isWordBreak()) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - continue; - } - - if (t->style()->hasTextCombine() && t->isCombineText()) - toRenderCombineText(t)->combineText(); - - // Determine if we have a breakable character. Pass in - // whether or not we should ignore any spaces at the front - // of the string. If those are going to be stripped out, - // then they shouldn't be considered in the breakable char - // check. - bool hasBreakableChar, hasBreak; - float firstLineMinWidth, lastLineMinWidth; - bool hasBreakableStart, hasBreakableEnd; - float firstLineMaxWidth, lastLineMaxWidth; - t->trimmedPrefWidths(inlineMax, - firstLineMinWidth, hasBreakableStart, lastLineMinWidth, hasBreakableEnd, - hasBreakableChar, hasBreak, firstLineMaxWidth, lastLineMaxWidth, - childMin, childMax, stripFrontSpaces); - - // This text object will not be rendered, but it may still provide a breaking opportunity. - if (!hasBreak && childMax == 0) { - if (autoWrap && (hasBreakableStart || hasBreakableEnd)) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - continue; - } - - if (stripFrontSpaces) - trailingSpaceChild = child; - else - trailingSpaceChild = 0; - - // Add in text-indent. This is added in only once. - float ti = 0; - if (!addedTextIndent || hasRemainingNegativeTextIndent) { - ti = textIndent.ceilToFloat(); - childMin += ti; - firstLineMinWidth += ti; - - // It the text indent negative and larger than the child minimum, we re-use the remainder - // in future minimum calculations, but using the negative value again on the maximum - // will lead to under-counting the max pref width. - if (!addedTextIndent) { - childMax += ti; - firstLineMaxWidth += ti; - addedTextIndent = true; - } - - if (childMin < 0) { - textIndent = childMin; - hasRemainingNegativeTextIndent = true; - } - } - - // If we have no breakable characters at all, - // then this is the easy case. We add ourselves to the current - // min and max and continue. - if (!hasBreakableChar) { - inlineMin += childMin; - } else { - if (hasBreakableStart) { - updatePreferredWidth(minLogicalWidth, inlineMin); - } else { - inlineMin += firstLineMinWidth; - updatePreferredWidth(minLogicalWidth, inlineMin); - childMin -= ti; - } - - inlineMin = childMin; - - if (hasBreakableEnd) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - shouldBreakLineAfterText = false; - } else { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = lastLineMinWidth; - shouldBreakLineAfterText = true; - } - } - - if (hasBreak) { - inlineMax += firstLineMaxWidth; - updatePreferredWidth(maxLogicalWidth, inlineMax); - updatePreferredWidth(maxLogicalWidth, childMax); - inlineMax = lastLineMaxWidth; - addedTextIndent = true; - } else { - inlineMax += max<float>(0, childMax); - } - } - - // Ignore spaces after a list marker. - if (child->isListMarker()) - stripFrontSpaces = true; - } else { - updatePreferredWidth(minLogicalWidth, inlineMin); - updatePreferredWidth(maxLogicalWidth, inlineMax); - inlineMin = inlineMax = 0; - stripFrontSpaces = true; - trailingSpaceChild = 0; - addedTextIndent = true; - } - - if (!child->isText() && child->isRenderInline()) - isPrevChildInlineFlow = true; - else - isPrevChildInlineFlow = false; - - oldAutoWrap = autoWrap; - } - - if (styleToUse->collapseWhiteSpace()) - stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); - - updatePreferredWidth(minLogicalWidth, inlineMin); - updatePreferredWidth(maxLogicalWidth, inlineMax); -} - void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { RenderStyle* styleToUse = style(); @@ -4491,7 +3745,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; @@ -4506,16 +3760,13 @@ LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, if (isReplaced() && linePositionMode == PositionOnContainingLine) return RenderBox::lineHeight(firstLine, direction, linePositionMode); - if (firstLine && document().styleEngine()->usesFirstLineRules()) { - RenderStyle* s = style(firstLine); - if (s != style()) - return s->computedLineHeight(view()); - } - - if (m_lineHeight == -1) - m_lineHeight = style()->computedLineHeight(view()); + RenderStyle* s = style(firstLine && document().styleEngine()->usesFirstLineRules()); + return s->computedLineHeight(); +} - return m_lineHeight; +int RenderBlock::beforeMarginInLineDirection(LineDirectionMode direction) const +{ + return direction == HorizontalLine ? marginTop() : marginRight(); } int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const @@ -4524,7 +3775,7 @@ int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, Lin // the base class. If we're being queried as though we're the root line // box, then the fact that we're an inline-block is irrelevant, and we behave // just like a block. - if (isReplaced() && linePositionMode == PositionOnContainingLine) { + if (isInline() && linePositionMode == PositionOnContainingLine) { // For "leaf" theme objects, let the theme decide what the baseline position is. // FIXME: Might be better to have a custom CSS property instead, so that if the theme // is turned off, checkboxes/radios will still have decent baselines. @@ -4553,7 +3804,7 @@ int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, Lin baselinePos = -1; } if (baselinePos != -1) - return direction == HorizontalLine ? marginTop() + baselinePos : marginRight() + baselinePos; + return beforeMarginInLineDirection(direction) + baselinePos; return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); } @@ -4603,7 +3854,7 @@ int RenderBlock::firstLineBoxBaseline() const int RenderBlock::inlineBlockBaseline(LineDirectionMode direction) const { - if (style()->overflowY() != OVISIBLE) { + if (!style()->isOverflowVisible()) { // We are not calling RenderBox::baselinePosition here because the caller should add the margin-top/margin-right, not us. return direction == HorizontalLine ? height() + m_marginBox.bottom() : width() + m_marginBox.left(); } @@ -4663,10 +3914,12 @@ RenderBlock* RenderBlock::firstLineBlock() const // FIXME: Remove when buttons are implemented with align-items instead // of flexbox. if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() - || !parentBlock || parentBlock->firstChild() != firstLineBlock + || !parentBlock || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) break; ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock()); + if (toRenderBlock(parentBlock)->firstChild() != firstLineBlock) + break; firstLineBlock = toRenderBlock(parentBlock); } @@ -4699,9 +3952,9 @@ static inline bool isPunctuationForFirstLetter(UChar c) || charCategory == Punctuation_Other; } -static inline bool shouldSkipForFirstLetter(UChar c) +static inline bool isSpaceForFirstLetter(UChar c) { - return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c); + return isSpaceOrNewline(c) || c == noBreakSpace; } static inline RenderObject* findFirstLetterBlock(RenderBlock* start) @@ -4721,8 +3974,12 @@ static inline RenderObject* findFirstLetterBlock(RenderBlock* start) return firstLetterBlock; RenderObject* parentBlock = firstLetterBlock->parent(); - if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock || - (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) + if (firstLetterBlock->isReplaced() || !parentBlock + || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) { + return 0; + } + ASSERT(parentBlock->isRenderBlock()); + if (toRenderBlock(parentBlock)->firstChild() != firstLetterBlock) return 0; firstLetterBlock = parentBlock; } @@ -4737,7 +3994,9 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); ASSERT(firstLetter->isFloating() || firstLetter->isInline()); - if (RenderStyle::compare(firstLetter->style(), pseudoStyle) == Reattach) { + ForceHorriblySlowRectMapping slowRectMapping(*this); + + if (RenderStyle::stylePropagationDiff(firstLetter->style(), pseudoStyle) == Reattach) { // The first-letter renderer needs to be replaced. Create a new renderer of the right type. RenderBoxModelObject* newFirstLetter; if (pseudoStyle->display() == INLINE) @@ -4747,8 +4006,7 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO newFirstLetter->setStyle(pseudoStyle); // Move the first letter into the new renderer. - LayoutStateDisabler layoutStateDisabler(view()); - while (RenderObject* child = firstLetter->firstChild()) { + while (RenderObject* child = firstLetter->slowFirstChild()) { if (child->isText()) toRenderText(child)->removeAndDestroyTextBoxes(); firstLetter->removeChild(child); @@ -4771,7 +4029,7 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO } else firstLetter->setStyle(pseudoStyle); - for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) { + for (RenderObject* genChild = firstLetter->slowFirstChild(); genChild; genChild = genChild->nextSibling()) { if (genChild->isText()) genChild->setStyle(pseudoStyle); } @@ -4780,38 +4038,34 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO static inline unsigned firstLetterLength(const String& text) { unsigned length = 0; - bool punctuationOpen = false; + unsigned textLength = text.length(); - // Account for leading spaces and punctuation. - while (length < text.length() && shouldSkipForFirstLetter((text)[length])) { - if (isPunctuationForFirstLetter((text)[length])) - punctuationOpen = true; + // Account for leading spaces first. + while (length < textLength && isSpaceForFirstLetter(text[length])) + length++; + // Now account for leading punctuation. + while (length < textLength && isPunctuationForFirstLetter(text[length])) length++; - } - // Bail if we didn't find a letter - if (text.length() && length == text.length()) + // Bail if we didn't find a letter before the end of the text or before a space. + if (isSpaceForFirstLetter(text[length]) || (textLength && length == textLength)) return 0; - // Account for first letter. + // Account the next character for first letter. length++; - if (!punctuationOpen) - return length; - - // Keep looking for whitespace and allowed punctuation, but avoid - // accumulating just whitespace into the :first-letter. - for (unsigned scanLength = length; scanLength < text.length(); ++scanLength) { - UChar c = (text)[scanLength]; + // Keep looking allowed punctuation for the :first-letter. + for (unsigned scanLength = length; scanLength < textLength; ++scanLength) { + UChar c = text[scanLength]; - if (!shouldSkipForFirstLetter(c)) + if (!isPunctuationForFirstLetter(c)) break; - if (isPunctuationForFirstLetter(c)) - length = scanLength + 1; + length = scanLength + 1; } + // FIXME: If textLength is 0, length may still be 1! return length; } @@ -4821,14 +4075,18 @@ void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend RenderObject* firstLetterContainer = currentChild->parent(); RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); - RenderObject* firstLetter = 0; + RenderBoxModelObject* firstLetter = 0; if (pseudoStyle->display() == INLINE) firstLetter = RenderInline::createAnonymous(&document()); else firstLetter = RenderBlockFlow::createAnonymous(&document()); firstLetter->setStyle(pseudoStyle); - firstLetterContainer->addChild(firstLetter, currentChild); + // FIXME: The first letter code should not modify the render tree during + // layout. crbug.com/370458 + DeprecatedDisableModifyRenderTreeStructureAsserts disabler; + + firstLetterContainer->addChild(firstLetter, currentChild); RenderText* textObj = toRenderText(currentChild); // The original string is going to be either a generated content string or a DOM node's @@ -4848,7 +4106,7 @@ void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend firstLetterContainer->addChild(remainingText, textObj); firstLetterContainer->removeChild(textObj); remainingText->setFirstLetter(firstLetter); - toRenderBoxModelObject(firstLetter)->setFirstLetterRemainingText(remainingText); + firstLetter->setFirstLetterRemainingText(remainingText); // construct text fragment for the first letter RenderTextFragment* letter = @@ -4874,7 +4132,7 @@ void RenderBlock::updateFirstLetter() return; // Drill into inlines looking for our first text child. - RenderObject* currChild = firstLetterBlock->firstChild(); + RenderObject* currChild = firstLetterBlock->slowFirstChild(); unsigned length = 0; while (currChild) { if (currChild->isText()) { @@ -4888,18 +4146,19 @@ void RenderBlock::updateFirstLetter() currChild = currChild->nextSibling(); } else if (currChild->isFloatingOrOutOfFlowPositioned()) { if (currChild->style()->styleType() == FIRST_LETTER) { - currChild = currChild->firstChild(); + currChild = currChild->slowFirstChild(); break; } currChild = currChild->nextSibling(); - } else if (currChild->isReplaced() || currChild->isRenderButton() || currChild->isMenuList()) + } else if (currChild->isReplaced() || currChild->isRenderButton() || currChild->isMenuList()) { break; - else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild->canHaveGeneratedChildren()) { + } else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild->canHaveGeneratedChildren()) { // We found a lower-level node with first-letter, which supersedes the higher-level style firstLetterBlock = currChild; - currChild = currChild->firstChild(); - } else - currChild = currChild->firstChild(); + currChild = currChild->slowFirstChild(); + } else { + currChild = currChild->slowFirstChild(); + } } if (!currChild) @@ -4912,12 +4171,14 @@ void RenderBlock::updateFirstLetter() return; } - if (!currChild->isText() || currChild->isBR()) + // FIXME: This black-list of disallowed RenderText subclasses is fragile. + // Should counter be on this list? What about RenderTextFragment? + if (!currChild->isText() || currChild->isBR() || toRenderText(currChild)->isWordBreak()) return; // Our layout state is not valid for the repaints we are going to trigger by // adding and removing children of firstLetterContainer. - LayoutStateDisabler layoutStateDisabler(view()); + ForceHorriblySlowRectMapping slowRectMapping(*this); createFirstLetterRenderer(firstLetterBlock, currChild, length); } @@ -5059,7 +4320,7 @@ void RenderBlock::fitBorderToLinesIfNeeded() LayoutUnit leftEdge = borderLeft() + paddingLeft(); LayoutUnit rightEdge = leftEdge + oldWidth; left = min(rightEdge, max(leftEdge, left)); - right = max(leftEdge, min(rightEdge, right)); + right = max(left, min(rightEdge, right)); LayoutUnit newContentWidth = right - left; if (newContentWidth == oldWidth) @@ -5168,17 +4429,18 @@ void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const if (isAnonymousBlockContinuation()) { // FIXME: This is wrong for block-flows that are horizontal. // https://bugs.webkit.org/show_bug.cgi?id=46781 - FloatRect localRect(0, -collapsedMarginBefore(), - width(), height() + collapsedMarginBefore() + collapsedMarginAfter()); + FloatRect localRect(0, -collapsedMarginBefore().toFloat(), + width().toFloat(), (height() + collapsedMarginBefore() + collapsedMarginAfter()).toFloat()); quads.append(localToAbsoluteQuad(localRect, 0 /* mode */, wasFixed)); continuation()->absoluteQuads(quads, wasFixed); - } else - quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width(), height()), 0 /* mode */, wasFixed)); + } else { + quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width().toFloat(), height().toFloat()), 0 /* mode */, wasFixed)); + } } -LayoutRect RenderBlock::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const +LayoutRect RenderBlock::rectWithOutlineForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer, LayoutUnit outlineWidth) const { - LayoutRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + LayoutRect r(RenderBox::rectWithOutlineForPaintInvalidation(paintInvalidationContainer, outlineWidth)); if (isAnonymousBlockContinuation()) r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal. return r; @@ -5196,7 +4458,7 @@ void RenderBlock::updateDragState(bool dragOn) continuation()->updateDragState(dragOn); } -RenderStyle* RenderBlock::outlineStyleForRepaint() const +RenderStyle* RenderBlock::outlineStyleForPaintInvalidation() const { return isAnonymousBlockContinuation() ? continuation()->style() : style(); } @@ -5240,10 +4502,10 @@ LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, La LayoutUnit myRight = caretRect.maxX(); // FIXME: why call localToAbsoluteForContent() twice here, too? - FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0)); + FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight.toFloat(), 0)); LayoutUnit containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent(); - FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0)); + FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight.toFloat(), 0)); *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); } @@ -5264,8 +4526,8 @@ void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& a // FIXME: This is wrong for block-flows that are horizontal. // https://bugs.webkit.org/show_bug.cgi?id=46781 bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->node()->renderer())->firstLineBox(); - float topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : LayoutUnit(); - float bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : LayoutUnit(); + LayoutUnit topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : LayoutUnit(); + LayoutUnit bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : LayoutUnit(); LayoutRect rect(additionalOffset.x(), additionalOffset.y() - topMargin, width(), height() + topMargin + bottomMargin); if (!rect.isEmpty()) rects.append(pixelSnappedIntRect(rect)); @@ -5281,18 +4543,7 @@ void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& a rects.append(pixelSnappedIntRect(rect)); } - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (!curr->isText() && !curr->isListMarker() && curr->isBox()) { - RenderBox* box = toRenderBox(curr); - FloatPoint pos; - // FIXME: This doesn't work correctly with transforms. - if (box->layer()) - pos = curr->localToContainerPoint(FloatPoint(), paintContainer); - else - pos = FloatPoint(additionalOffset.x() + box->x(), additionalOffset.y() + box->y()); - box->addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer); - } - } + addChildFocusRingRects(rects, additionalOffset, paintContainer); } if (inlineElementContinuation()) @@ -5324,25 +4575,6 @@ RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* par return createAnonymousWithParentRendererAndDisplay(parent, style()->display()); } -bool RenderBlock::hasNextPage(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const -{ - ASSERT(view()->layoutState() && view()->layoutState()->isPaginated()); - - 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 = flowThread->regionAtBlockOffset(pageOffset, this); - if (!region) - return false; - if (region->isLastRegion()) - return region->isRenderRegionSet() || region->style()->regionFragment() == BreakRegionFragment - || (pageBoundaryRule == IncludePageBoundary && pageOffset == region->logicalTopForFlowThreadContent()); - return true; -} - LayoutUnit RenderBlock::nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const { LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); @@ -5356,16 +4588,11 @@ LayoutUnit RenderBlock::nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundar return logicalOffset + remainingLogicalHeight; } -ColumnInfo::PaginationUnit RenderBlock::paginationUnit() const -{ - return ColumnInfo::Column; -} - LayoutUnit RenderBlock::pageLogicalTopForOffset(LayoutUnit offset) const { RenderView* renderView = view(); - LayoutUnit firstPageLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->m_pageOffset.height() : renderView->layoutState()->m_pageOffset.width(); - LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->m_layoutOffset.height() : renderView->layoutState()->m_layoutOffset.width(); + LayoutUnit firstPageLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->pageOffset().height() : renderView->layoutState()->pageOffset().width(); + LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->layoutOffset().height() : renderView->layoutState()->layoutOffset().width(); LayoutUnit cumulativeOffset = offset + blockLogicalTop; RenderFlowThread* flowThread = flowThreadContainingBlock(); @@ -5383,7 +4610,7 @@ LayoutUnit RenderBlock::pageLogicalHeightForOffset(LayoutUnit offset) const RenderView* renderView = view(); RenderFlowThread* flowThread = flowThreadContainingBlock(); if (!flowThread) - return renderView->layoutState()->m_pageLogicalHeight; + return renderView->layoutState()->pageLogicalHeight(); return flowThread->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage()); } @@ -5394,7 +4621,7 @@ LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, P RenderFlowThread* flowThread = flowThreadContainingBlock(); if (!flowThread) { - LayoutUnit pageLogicalHeight = renderView->layoutState()->m_pageLogicalHeight; + LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight(); LayoutUnit remainingHeight = pageLogicalHeight - intMod(offset, pageLogicalHeight); if (pageBoundaryRule == IncludePageBoundary) { // If includeBoundaryPoint is true the line exactly on the top edge of a @@ -5409,46 +4636,23 @@ LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, P LayoutUnit RenderBlock::adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins) { - bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); - bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; - RenderFlowThread* flowThread = flowThreadContainingBlock(); - bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); + bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns() || flowThreadContainingBlock(); + bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->pageLogicalHeight(); bool isUnsplittable = child->isUnsplittableForPagination() || (checkColumnBreaks && child->style()->columnBreakInside() == PBAVOID) - || (checkPageBreaks && child->style()->pageBreakInside() == PBAVOID) - || (checkRegionBreaks && child->style()->regionBreakInside() == PBAVOID); + || (checkPageBreaks && child->style()->pageBreakInside() == PBAVOID); if (!isUnsplittable) return logicalOffset; LayoutUnit childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : LayoutUnit()); LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); - bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); updateMinimumPageHeight(logicalOffset, childLogicalHeight); - if (!pageLogicalHeight || (hasUniformPageLogicalHeight && childLogicalHeight > pageLogicalHeight) - || !hasNextPage(logicalOffset)) + if (!pageLogicalHeight || childLogicalHeight > pageLogicalHeight) return logicalOffset; LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary); - if (remainingLogicalHeight < childLogicalHeight) { - if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, childLogicalHeight)) - return logicalOffset; + if (remainingLogicalHeight < childLogicalHeight) return logicalOffset + remainingLogicalHeight; - } return logicalOffset; } -bool RenderBlock::pushToNextPageWithMinimumLogicalHeight(LayoutUnit& adjustment, LayoutUnit logicalOffset, LayoutUnit minimumLogicalHeight) const -{ - bool checkRegion = false; - for (LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset + adjustment); pageLogicalHeight; - pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset + adjustment)) { - if (minimumLogicalHeight <= pageLogicalHeight) - return true; - if (!hasNextPage(logicalOffset + adjustment)) - return false; - adjustment += pageLogicalHeight; - checkRegion = true; - } - return !checkRegion; -} - void RenderBlock::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) { if (RenderFlowThread* flowThread = flowThreadContainingBlock()) @@ -5459,7 +4663,7 @@ void RenderBlock::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeigh { if (RenderFlowThread* flowThread = flowThreadContainingBlock()) flowThread->updateMinimumPageHeight(offsetFromLogicalTopOfFirstPage() + offset, minHeight); - else if (ColumnInfo* colInfo = view()->layoutState()->m_columnInfo) + else if (ColumnInfo* colInfo = view()->layoutState()->columnInfo()) colInfo->updateMinimumColumnHeight(minHeight); } @@ -5514,8 +4718,7 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, Layout 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)) + if (!pageLogicalHeight || (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight)) // FIXME: In case the line aligns with the top of the page (or it's slightly shifted downwards) it will not be marked as the first line in the page. // From here, the fix is not straightforward because it's not easy to always determine when the current line is the first in the page. return; @@ -5527,9 +4730,6 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, Layout clearShouldBreakAtLineToAvoidWidow(); setDidBreakAtLineToAvoidWidow(); } - // If we have a non-uniform page height, then we have to shift further possibly. - if (!hasUniformPageLogicalHeight && !pushToNextPageWithMinimumLogicalHeight(remainingLogicalHeight, logicalOffset, lineHeight)) - return; if (lineHeight > pageLogicalHeight) { // Split the top margin in order to avoid splitting the visible part of the line. remainingLogicalHeight -= min(lineHeight - pageLogicalHeight, max<LayoutUnit>(0, logicalVisualOverflow.y() - lineBox->lineTopWithLeading())); @@ -5554,34 +4754,6 @@ void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, Layout } } -void RenderBlock::updateRegionForLine(RootInlineBox* lineBox) const -{ - ASSERT(lineBox); - lineBox->setContainingRegion(regionAtBlockOffset(lineBox->lineTopWithLeading())); - - RootInlineBox* prevLineBox = lineBox->prevRootBox(); - if (!prevLineBox) - return; - - // This check is more accurate than the one in |adjustLinePositionForPagination| because it takes into - // account just the container changes between lines. The before mentioned function doesn't set the flag - // correctly if the line is positioned at the top of the last fragment container. - if (lineBox->containingRegion() != prevLineBox->containingRegion()) - lineBox->setIsFirstAfterPageBreak(true); -} - -bool RenderBlock::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta, RenderFlowThread* flowThread) const -{ - if (!flowThread) - return false; - - RenderRegion* currentRegion = regionAtBlockOffset(rootBox->lineTopWithLeading() + lineDelta); - // Just bail if the region didn't change. - if (rootBox->containingRegion() == currentRegion) - return false; - return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion); -} - LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const { LayoutState* layoutState = view()->layoutState(); @@ -5595,7 +4767,7 @@ LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const if (layoutState) { ASSERT(layoutState->renderer() == this); - LayoutSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset; + LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset(); return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); } @@ -5603,47 +4775,6 @@ LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const return 0; } -RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const -{ - RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (!flowThread || !flowThread->hasValidRegionInfo()) - return 0; - - return flowThread->regionAtBlockOffset(offsetFromLogicalTopOfFirstPage() + blockOffset, true); -} - -bool RenderBlock::logicalWidthChangedInRegions(RenderFlowThread* flowThread) const -{ - if (!flowThread || !flowThread->hasValidRegionInfo()) - return false; - - return flowThread->logicalWidthChangedInRegionsForBlock(this); -} - -RenderRegion* RenderBlock::clampToStartAndEndRegions(RenderRegion* region) const -{ - 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; - flowThread->getRegionRangeForBox(this, startRegion, endRegion); - - if (startRegion && region->logicalTopForFlowThreadContent() < startRegion->logicalTopForFlowThreadContent()) - return startRegion; - if (endRegion && region->logicalTopForFlowThreadContent() > endRegion->logicalTopForFlowThreadContent()) - return endRegion; - - return region; -} - LayoutUnit RenderBlock::collapsedMarginBeforeForChild(const RenderBox* child) const { // If the child has the same directionality as we do, then we can just return its @@ -5781,6 +4912,91 @@ RenderBlockFlow* RenderBlock::createAnonymousColumnSpanWithParentRenderer(const return newBox; } +static bool recalcNormalFlowChildOverflowIfNeeded(RenderObject* renderer) +{ + if (renderer->isOutOfFlowPositioned() || !renderer->needsOverflowRecalcAfterStyleChange()) + return false; + + ASSERT(renderer->isRenderBlock()); + return toRenderBlock(renderer)->recalcOverflowAfterStyleChange(); +} + +bool RenderBlock::recalcChildOverflowAfterStyleChange() +{ + ASSERT(childNeedsOverflowRecalcAfterStyleChange()); + setChildNeedsOverflowRecalcAfterStyleChange(false); + + bool childrenOverflowChanged = false; + + if (childrenInline()) { + ListHashSet<RootInlineBox*> lineBoxes; + for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { + RenderObject* renderer = walker.current(); + if (recalcNormalFlowChildOverflowIfNeeded(renderer)) { + childrenOverflowChanged = true; + if (InlineBox* inlineBoxWrapper = toRenderBlock(renderer)->inlineBoxWrapper()) + lineBoxes.add(&inlineBoxWrapper->root()); + } + } + + // FIXME: Glyph overflow will get lost in this case, but not really a big deal. + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + for (ListHashSet<RootInlineBox*>::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) { + RootInlineBox* box = *it; + box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); + } + } else { + for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { + if (recalcNormalFlowChildOverflowIfNeeded(box)) + childrenOverflowChanged = true; + } + } + + TrackedRendererListHashSet* positionedDescendants = positionedObjects(); + if (!positionedDescendants) + return childrenOverflowChanged; + + TrackedRendererListHashSet::iterator end = positionedDescendants->end(); + for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { + RenderBox* box = *it; + + if (!box->needsOverflowRecalcAfterStyleChange()) + continue; + RenderBlock* block = toRenderBlock(box); + if (!block->recalcOverflowAfterStyleChange() || box->style()->position() == FixedPosition) + continue; + + childrenOverflowChanged = true; + } + return childrenOverflowChanged; +} + +bool RenderBlock::recalcOverflowAfterStyleChange() +{ + ASSERT(needsOverflowRecalcAfterStyleChange()); + + bool childrenOverflowChanged = false; + if (childNeedsOverflowRecalcAfterStyleChange()) + childrenOverflowChanged = recalcChildOverflowAfterStyleChange(); + + if (!selfNeedsOverflowRecalcAfterStyleChange() && !childrenOverflowChanged) + return false; + + setSelfNeedsOverflowRecalcAfterStyleChange(false); + // If the current block needs layout, overflow will be recalculated during + // layout time anyway. We can safely exit here. + if (needsLayout()) + return false; + + LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); + computeOverflow(oldClientAfterEdge, true); + + if (hasOverflowClip()) + layer()->scrollableArea()->updateAfterOverflowRecalc(); + + return !hasOverflowClip(); +} + #ifndef NDEBUG void RenderBlock::checkPositionedObjectsNeedLayout() { |