diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderBlock.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RenderBlock.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderBlock.cpp | 4038 |
1 files changed, 1178 insertions, 2860 deletions
diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp index 27ee2c9bf..90c3b8f4a 100644 --- a/Source/WebCore/rendering/RenderBlock.cpp +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -25,7 +25,6 @@ #include "RenderBlock.h" #include "AXObjectCache.h" -#include "ColumnInfo.h" #include "Document.h" #include "Editor.h" #include "Element.h" @@ -34,7 +33,6 @@ #include "FrameSelection.h" #include "FrameView.h" #include "GraphicsContext.h" -#include "HTMLInputElement.h" #include "HTMLNames.h" #include "HitTestLocation.h" #include "HitTestResult.h" @@ -48,31 +46,34 @@ #include "PaintInfo.h" #include "RenderBlockFlow.h" #include "RenderBoxRegionInfo.h" +#include "RenderButton.h" +#include "RenderChildIterator.h" #include "RenderCombineText.h" #include "RenderDeprecatedFlexibleBox.h" #include "RenderFlexibleBox.h" #include "RenderInline.h" #include "RenderIterator.h" #include "RenderLayer.h" -#include "RenderMarquee.h" +#include "RenderListMarker.h" +#include "RenderMenuList.h" #include "RenderNamedFlowFragment.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" +#include "RenderSVGResourceClipper.h" #include "RenderTableCell.h" #include "RenderTextFragment.h" #include "RenderTheme.h" +#include "RenderTreePosition.h" #include "RenderView.h" -#include "SVGTextRunRenderingContext.h" #include "Settings.h" #include "ShadowRoot.h" +#include "ShapeOutsideInfo.h" #include "TransformState.h" -#include <wtf/StackStats.h> -#include <wtf/TemporaryChange.h> -#if ENABLE(CSS_SHAPES) -#include "ShapeInsideInfo.h" -#include "ShapeOutsideInfo.h" -#endif +#include <wtf/NeverDestroyed.h> +#include <wtf/Optional.h> +#include <wtf/SetForScope.h> +#include <wtf/StackStats.h> using namespace WTF; using namespace Unicode; @@ -82,49 +83,182 @@ namespace WebCore { using namespace HTMLNames; struct SameSizeAsRenderBlock : public RenderBox { - uint32_t bitfields; }; COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small); -typedef WTF::HashMap<const RenderBox*, OwnPtr<ColumnInfo>> ColumnInfoMap; -static ColumnInfoMap* gColumnInfoMap = 0; +typedef HashMap<const RenderBlock*, std::unique_ptr<TrackedRendererListHashSet>> TrackedDescendantsMap; +typedef HashMap<const RenderBox*, std::unique_ptr<HashSet<const RenderBlock*>>> TrackedContainerMap; -static TrackedDescendantsMap* gPositionedDescendantsMap = 0; -static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; +static TrackedDescendantsMap* percentHeightDescendantsMap; +static TrackedContainerMap* percentHeightContainerMap; -static TrackedContainerMap* gPositionedContainerMap = 0; -static TrackedContainerMap* gPercentHeightContainerMap = 0; +static void insertIntoTrackedRendererMaps(const RenderBlock& container, RenderBox& descendant) +{ + if (!percentHeightDescendantsMap) { + percentHeightDescendantsMap = new TrackedDescendantsMap; + percentHeightContainerMap = new TrackedContainerMap; + } -typedef WTF::HashMap<RenderBlock*, OwnPtr<ListHashSet<RenderInline*>>> ContinuationOutlineTableMap; + auto& descendantSet = percentHeightDescendantsMap->ensure(&container, [] { + return std::make_unique<TrackedRendererListHashSet>(); + }).iterator->value; -typedef WTF::HashSet<RenderBlock*> DelayedUpdateScrollInfoSet; -static int gDelayUpdateScrollInfo = 0; -static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0; + bool added = descendantSet->add(&descendant).isNewEntry; + if (!added) { + ASSERT(percentHeightContainerMap->get(&descendant)); + ASSERT(percentHeightContainerMap->get(&descendant)->contains(&container)); + return; + } + + auto& containerSet = percentHeightContainerMap->ensure(&descendant, [] { + return std::make_unique<HashSet<const RenderBlock*>>(); + }).iterator->value; + + ASSERT(!containerSet->contains(&container)); + containerSet->add(&container); +} + +static void removeFromTrackedRendererMaps(RenderBox& descendant) +{ + if (!percentHeightDescendantsMap) + return; + + std::unique_ptr<HashSet<const RenderBlock*>> containerSet = percentHeightContainerMap->take(&descendant); + if (!containerSet) + return; + + for (auto* container : *containerSet) { + // FIXME: Disabling this assert temporarily until we fix the layout + // bugs associated with positioned objects not properly cleared from + // their ancestor chain before being moved. See webkit bug 93766. + // ASSERT(descendant->isDescendantOf(container)); + auto descendantsMapIterator = percentHeightDescendantsMap->find(container); + ASSERT(descendantsMapIterator != percentHeightDescendantsMap->end()); + if (descendantsMapIterator == percentHeightDescendantsMap->end()) + continue; + auto& descendantSet = descendantsMapIterator->value; + ASSERT(descendantSet->contains(&descendant)); + descendantSet->remove(&descendant); + if (descendantSet->isEmpty()) + percentHeightDescendantsMap->remove(descendantsMapIterator); + } +} -static bool gColumnFlowSplitEnabled = true; +class PositionedDescendantsMap { +public: + enum class MoveDescendantToEnd { No, Yes }; + void addDescendant(const RenderBlock& containingBlock, RenderBox& positionedDescendant, MoveDescendantToEnd moveDescendantToEnd) + { + // Protect against double insert where a descendant would end up with multiple containing blocks. + auto* previousContainingBlock = m_containerMap.get(&positionedDescendant); + if (previousContainingBlock && previousContainingBlock != &containingBlock) { + if (auto* descendants = m_descendantsMap.get(previousContainingBlock)) + descendants->remove(&positionedDescendant); + } + + auto& descendants = m_descendantsMap.ensure(&containingBlock, [] { + return std::make_unique<TrackedRendererListHashSet>(); + }).iterator->value; + + bool isNewEntry = moveDescendantToEnd == MoveDescendantToEnd::Yes ? descendants->appendOrMoveToLast(&positionedDescendant).isNewEntry + : descendants->add(&positionedDescendant).isNewEntry; + if (!isNewEntry) { + ASSERT(m_containerMap.contains(&positionedDescendant)); + return; + } + m_containerMap.set(&positionedDescendant, &containingBlock); + } + + void removeDescendant(const RenderBox& positionedDescendant) + { + auto* containingBlock = m_containerMap.take(&positionedDescendant); + if (!containingBlock) + return; + + auto descendantsIterator = m_descendantsMap.find(containingBlock); + ASSERT(descendantsIterator != m_descendantsMap.end()); + if (descendantsIterator == m_descendantsMap.end()) + return; + + auto& descendants = descendantsIterator->value; + ASSERT(descendants->contains(const_cast<RenderBox*>(&positionedDescendant))); + + descendants->remove(const_cast<RenderBox*>(&positionedDescendant)); + if (descendants->isEmpty()) + m_descendantsMap.remove(descendantsIterator); + } + + void removeContainingBlock(const RenderBlock& containingBlock) + { + auto descendants = m_descendantsMap.take(&containingBlock); + if (!descendants) + return; + + for (auto* renderer : *descendants) + m_containerMap.remove(renderer); + } + + TrackedRendererListHashSet* positionedRenderers(const RenderBlock& containingBlock) const + { + return m_descendantsMap.get(&containingBlock); + } + +private: + using DescendantsMap = HashMap<const RenderBlock*, std::unique_ptr<TrackedRendererListHashSet>>; + using ContainerMap = HashMap<const RenderBox*, const RenderBlock*>; + + DescendantsMap m_descendantsMap; + ContainerMap m_containerMap; +}; + +static PositionedDescendantsMap& positionedDescendantsMap() +{ + static NeverDestroyed<PositionedDescendantsMap> mapForPositionedDescendants; + return mapForPositionedDescendants; +} + +typedef HashMap<RenderBlock*, std::unique_ptr<ListHashSet<RenderInline*>>> ContinuationOutlineTableMap; + +struct UpdateScrollInfoAfterLayoutTransaction { + UpdateScrollInfoAfterLayoutTransaction(const RenderView& view) + : nestedCount(0) + , view(&view) + { + } + + int nestedCount; + const RenderView* view; + HashSet<RenderBlock*> blocks; +}; + +typedef Vector<UpdateScrollInfoAfterLayoutTransaction> DelayedUpdateScrollInfoStack; +static std::unique_ptr<DelayedUpdateScrollInfoStack>& updateScrollInfoAfterLayoutTransactionStack() +{ + static NeverDestroyed<std::unique_ptr<DelayedUpdateScrollInfoStack>> delayedUpdatedScrollInfoStack; + return delayedUpdatedScrollInfoStack; +} // Allocated only when some of these fields have non-default values struct RenderBlockRareData { WTF_MAKE_NONCOPYABLE(RenderBlockRareData); WTF_MAKE_FAST_ALLOCATED; public: - RenderBlockRareData() + RenderBlockRareData() : m_paginationStrut(0) , m_pageLogicalOffset(0) - { + , m_flowThreadContainingBlock(std::nullopt) + { } LayoutUnit m_paginationStrut; LayoutUnit m_pageLogicalOffset; -#if ENABLE(CSS_SHAPES) - std::unique_ptr<ShapeInsideInfo> m_shapeInsideInfo; -#endif + std::optional<RenderFlowThread*> m_flowThreadContainingBlock; }; typedef HashMap<const RenderBlock*, std::unique_ptr<RenderBlockRareData>> RenderBlockRareDataMap; -static RenderBlockRareDataMap* gRareDataMap = 0; +static RenderBlockRareDataMap* gRareDataMap; // This class helps dispatching the 'overflow' event on layout change. overflow can be set on RenderBoxes, yet the existing code // only works on RenderBlocks. If this change, this class should be shared with other RenderBoxes. @@ -156,9 +290,9 @@ public: if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged) return; - RefPtr<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); + Ref<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); overflowEvent->setTarget(m_block->element()); - m_block->document().enqueueOverflowEvent(overflowEvent.release()); + m_block->document().enqueueOverflowEvent(WTFMove(overflowEvent)); } private: @@ -168,58 +302,65 @@ private: bool m_hadVerticalLayoutOverflow; }; -RenderBlock::RenderBlock(Element& element, PassRef<RenderStyle> style, unsigned baseTypeFlags) - : RenderBox(element, std::move(style), baseTypeFlags | RenderBlockFlag) - , m_lineHeight(-1) - , m_hasMarginBeforeQuirk(false) - , m_hasMarginAfterQuirk(false) - , m_beingDestroyed(false) - , m_hasMarkupTruncation(false) - , m_hasBorderOrPaddingLogicalWidthChanged(false) - , m_lineLayoutPath(UndeterminedPath) +RenderBlock::RenderBlock(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags) + : RenderBox(element, WTFMove(style), baseTypeFlags | RenderBlockFlag) { } -RenderBlock::RenderBlock(Document& document, PassRef<RenderStyle> style, unsigned baseTypeFlags) - : RenderBox(document, std::move(style), baseTypeFlags | RenderBlockFlag) - , m_lineHeight(-1) - , m_hasMarginBeforeQuirk(false) - , m_hasMarginAfterQuirk(false) - , m_beingDestroyed(false) - , m_hasMarkupTruncation(false) - , m_hasBorderOrPaddingLogicalWidthChanged(false) - , m_lineLayoutPath(UndeterminedPath) +RenderBlock::RenderBlock(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags) + : RenderBox(document, WTFMove(style), baseTypeFlags | RenderBlockFlag) { } -static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap) +static void removeBlockFromPercentageDescendantAndContainerMaps(RenderBlock* block) { - if (OwnPtr<TrackedRendererListHashSet> descendantSet = descendantMap->take(block)) { - TrackedRendererListHashSet::iterator end = descendantSet->end(); - for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { - TrackedContainerMap::iterator it = containerMap->find(*descendant); - ASSERT(it != containerMap->end()); - if (it == containerMap->end()) - continue; - HashSet<RenderBlock*>* containerSet = it->value.get(); - ASSERT(containerSet->contains(block)); - containerSet->remove(block); - if (containerSet->isEmpty()) - containerMap->remove(it); - } + if (!percentHeightDescendantsMap) + return; + std::unique_ptr<TrackedRendererListHashSet> descendantSet = percentHeightDescendantsMap->take(block); + if (!descendantSet) + return; + + for (auto* descendant : *descendantSet) { + auto it = percentHeightContainerMap->find(descendant); + ASSERT(it != percentHeightContainerMap->end()); + if (it == percentHeightContainerMap->end()) + continue; + auto* containerSet = it->value.get(); + ASSERT(containerSet->contains(block)); + containerSet->remove(block); + if (containerSet->isEmpty()) + percentHeightContainerMap->remove(it); } } RenderBlock::~RenderBlock() { - if (hasColumns()) - gColumnInfoMap->take(this); + // Blocks can be added to gRareDataMap during willBeDestroyed(), so this code can't move there. if (gRareDataMap) gRareDataMap->remove(this); - if (gPercentHeightDescendantsMap) - removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); - if (gPositionedDescendantsMap) - removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap); + + // Do not add any more code here. Add it to willBeDestroyed() instead. +} + +// Note that this is not called for RenderBlockFlows. +void RenderBlock::willBeDestroyed() +{ + if (!renderTreeBeingDestroyed()) { + if (parent()) + parent()->dirtyLinesFromChangedChild(*this); + } + + blockWillBeDestroyed(); + + RenderBox::willBeDestroyed(); +} + +void RenderBlock::blockWillBeDestroyed() +{ + removeFromUpdateScrollInfoAfterLayoutTransaction(); + + removeBlockFromPercentageDescendantAndContainerMaps(this); + positionedDescendantsMap().removeContainingBlock(*this); } bool RenderBlock::hasRareData() const @@ -227,67 +368,51 @@ bool RenderBlock::hasRareData() const return gRareDataMap ? gRareDataMap->contains(this) : false; } -void RenderBlock::willBeDestroyed() +void RenderBlock::removePositionedObjectsIfNeeded(const RenderStyle& oldStyle, const RenderStyle& newStyle) { - // Mark as being destroyed to avoid trouble with merges in removeChild(). - m_beingDestroyed = true; + bool hadTransform = oldStyle.hasTransformRelatedProperty(); + bool willHaveTransform = newStyle.hasTransformRelatedProperty(); + if (oldStyle.position() == newStyle.position() && hadTransform == willHaveTransform) + return; - if (!documentBeingDestroyed()) { - if (firstChild() && firstChild()->isRunIn()) - moveRunInToOriginalPosition(*firstChild()); + // We are no longer the containing block for fixed descendants. + if (hadTransform && !willHaveTransform) { + // Our positioned descendants will be inserted into a new containing block's positioned objects list during the next layout. + removePositionedObjects(nullptr, NewContainingBlock); + return; } - // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will - // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. - destroyLeftoverChildren(); - - // Destroy our continuation before anything other than anonymous children. - // The reason we don't destroy it before anonymous children is that they may - // have continuations of their own that are anonymous children of our continuation. - RenderBoxModelObject* continuation = this->continuation(); - if (continuation) { - continuation->destroy(); - setContinuation(0); - } - - if (!documentBeingDestroyed()) { - if (parent()) - parent()->dirtyLinesFromChangedChild(this); + // We are no longer the containing block for absolute positioned descendants. + if (newStyle.position() == StaticPosition && !willHaveTransform) { + // Our positioned descendants will be inserted into a new containing block's positioned objects list during the next layout. + removePositionedObjects(nullptr, NewContainingBlock); + return; } - removeFromDelayedUpdateScrollInfoSet(); - - RenderBox::willBeDestroyed(); + // We are a new containing block. + if (oldStyle.position() == StaticPosition && !hadTransform) { + // Remove our absolutely positioned descendants from their current containing block. + // They will be inserted into our positioned objects list during layout. + auto* containingBlock = parent(); + while (containingBlock && !is<RenderView>(*containingBlock) + && (containingBlock->style().position() == StaticPosition || (containingBlock->isInline() && !containingBlock->isReplaced()))) { + if (containingBlock->style().position() == RelativePosition && containingBlock->isInline() && !containingBlock->isReplaced()) { + containingBlock = containingBlock->containingBlock(); + break; + } + containingBlock = containingBlock->parent(); + } + if (containingBlock && is<RenderBlock>(*containingBlock)) + downcast<RenderBlock>(*containingBlock).removePositionedObjects(this, NewContainingBlock); + } } void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) { const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr; - setReplaced(newStyle.isDisplayInlineType()); - - if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle.position()) { - if (newStyle.position() == StaticPosition) - // Clear our positioned objects list. Our absolutely positioned descendants will be - // inserted into our containing block's positioned objects list during layout. - removePositionedObjects(0, NewContainingBlock); - else if (oldStyle->position() == StaticPosition) { - // Remove our absolutely positioned descendants from their current containing block. - // They will be inserted into our positioned objects list during layout. - auto cb = parent(); - while (cb && (cb->style().position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) { - if (cb->style().position() == RelativePosition && cb->isInline() && !cb->isReplaced()) { - cb = cb->containingBlock(); - break; - } - cb = cb->parent(); - } - - if (cb->isRenderBlock()) - toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock); - } - } - + if (oldStyle) + removePositionedObjectsIfNeeded(*oldStyle, newStyle); RenderBox::styleWillChange(diff, newStyle); } @@ -307,30 +432,28 @@ static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, cons void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { + bool hadTransform = hasTransform(); RenderBox::styleDidChange(diff, oldStyle); - - RenderStyle& newStyle = style(); - -#if ENABLE(CSS_SHAPES) - updateShapeInsideInfoAfterStyleChange(newStyle.resolvedShapeInside(), oldStyle ? oldStyle->resolvedShapeInside() : 0); -#endif + if (hadTransform != hasTransform()) + adjustFlowThreadStateOnContainingBlockChangeIfNeeded(); + + auto& newStyle = style(); if (!isAnonymousBlock()) { // Ensure that all of our continuation blocks pick up the new style. for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { RenderBoxModelObject* nextCont = currCont->continuation(); currCont->setContinuation(0); - currCont->setStyle(newStyle); + currCont->setStyle(RenderStyle::clone(newStyle)); currCont->setContinuation(nextCont); } } propagateStyleToAnonymousChildren(PropagateToBlockChildrenOnly); - 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); + setShouldForceRelayoutChildren(oldStyle && diff == StyleDifferenceLayout && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, &newStyle)); } RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) @@ -338,19 +461,17 @@ RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) if (beforeChild && beforeChild->parent() == this) return this; - RenderBlock* curr = toRenderBlock(continuation()); RenderBlock* nextToLast = this; RenderBlock* last = this; - while (curr) { - if (beforeChild && beforeChild->parent() == curr) { - if (curr->firstChild() == beforeChild) + for (auto* current = downcast<RenderBlock>(continuation()); current; current = downcast<RenderBlock>(current->continuation())) { + if (beforeChild && beforeChild->parent() == current) { + if (current->firstChild() == beforeChild) return last; - return curr; + return current; } nextToLast = last; - last = curr; - curr = toRenderBlock(curr->continuation()); + last = current; } if (!beforeChild && !last->firstChild()) @@ -361,14 +482,14 @@ RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) { RenderBlock* flow = continuationBefore(beforeChild); - ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock()); - RenderBoxModelObject* beforeChildParent = 0; + ASSERT(!beforeChild || is<RenderBlock>(*beforeChild->parent())); + RenderBoxModelObject* beforeChildParent = nullptr; if (beforeChild) - beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); + beforeChildParent = downcast<RenderBoxModelObject>(beforeChild->parent()); else { - RenderBoxModelObject* cont = flow->continuation(); - if (cont) - beforeChildParent = cont; + RenderBoxModelObject* continuation = flow->continuation(); + if (continuation) + beforeChildParent = continuation; else beforeChildParent = flow; } @@ -378,8 +499,6 @@ void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* b return; } - // A continuation always consists of two potential candidates: a block or an anonymous - // column span box holding column span children. bool childIsNormal = newChild->isInline() || !newChild->style().columnSpan(); bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style().columnSpan(); bool flowIsNormal = flow->isInline() || !flow->style().columnSpan(); @@ -402,96 +521,6 @@ void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* b beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); } - -void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) -{ - ASSERT(!continuation()); // We don't yet support column spans that aren't immediate children of the multi-column block. - - // The goal is to locate a suitable box in which to place our child. - RenderBlock* beforeChildParent = 0; - if (beforeChild) { - RenderObject* curr = beforeChild; - while (curr && curr->parent() != this) - curr = curr->parent(); - beforeChildParent = toRenderBlock(curr); - ASSERT(beforeChildParent); - ASSERT(beforeChildParent->isAnonymousColumnsBlock() || beforeChildParent->isAnonymousColumnSpanBlock()); - } else - beforeChildParent = toRenderBlock(lastChild()); - - // If the new child is floating or positioned it can just go in that block. - if (newChild->isFloatingOrOutOfFlowPositioned()) { - beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); - return; - } - - // See if the child can be placed in the box. - bool newChildHasColumnSpan = !newChild->isInline() && newChild->style().columnSpan(); - bool beforeChildParentHoldsColumnSpans = beforeChildParent->isAnonymousColumnSpanBlock(); - - if (newChildHasColumnSpan == beforeChildParentHoldsColumnSpans) { - beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); - return; - } - - if (!beforeChild) { - // Create a new block of the correct type. - RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); - insertChildInternal(newBox, nullptr, NotifyChildren); - newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); - return; - } - - RenderObject* immediateChild = beforeChild; - bool isPreviousBlockViable = true; - while (immediateChild->parent() != this) { - if (isPreviousBlockViable) - isPreviousBlockViable = !immediateChild->previousSibling(); - immediateChild = immediateChild->parent(); - } - if (isPreviousBlockViable && immediateChild->previousSibling()) { - toRenderBlock(immediateChild->previousSibling())->addChildIgnoringAnonymousColumnBlocks(newChild, 0); // Treat like an append. - return; - } - - // Split our anonymous blocks. - RenderObject* newBeforeChild = splitAnonymousBoxesAroundChild(beforeChild); - - - // Create a new anonymous box of the appropriate type. - RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); - insertChildInternal(newBox, newBeforeChild, NotifyChildren); - newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); - return; -} - -RenderBlock* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock) -{ - RenderBlock* firstChildIgnoringAnonymousWrappers = 0; - for (RenderElement* curr = this; curr; curr = curr->parent()) { - if (!curr->isRenderBlock() || curr->isFloatingOrOutOfFlowPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip() - || curr->isInlineBlockOrInlineTable()) - return 0; - - // FIXME: Tables, RenderButtons, and RenderListItems all do special management - // of their children that breaks when the flow is split through them. Disabling - // multi-column for them to avoid this problem. - if (curr->isTable() || curr->isRenderButton() || curr->isListItem()) - return 0; - - RenderBlock* currBlock = toRenderBlock(curr); - if (!currBlock->createsAnonymousWrapper()) - firstChildIgnoringAnonymousWrappers = currBlock; - - if (currBlock->style().specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock())) - return firstChildIgnoringAnonymousWrappers; - - if (currBlock->isAnonymousColumnSpanBlock()) - return 0; - } - return 0; -} - RenderPtr<RenderBlock> RenderBlock::clone() const { RenderPtr<RenderBlock> cloneBlock; @@ -499,7 +528,8 @@ RenderPtr<RenderBlock> RenderBlock::clone() const cloneBlock = RenderPtr<RenderBlock>(createAnonymousBlock()); cloneBlock->setChildrenInline(childrenInline()); } else { - cloneBlock = static_pointer_cast<RenderBlock>(element()->createElementRenderer(style())); + RenderTreePosition insertionPosition(*parent()); + cloneBlock = static_pointer_cast<RenderBlock>(element()->createElementRenderer(RenderStyle::clone(style()), insertionPosition)); cloneBlock->initializeStyle(); // This takes care of setting the right value of childrenInline in case @@ -511,218 +541,15 @@ RenderPtr<RenderBlock> RenderBlock::clone() const return cloneBlock; } -void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, - RenderBlock* middleBlock, - RenderObject* beforeChild, RenderBoxModelObject* oldCont) -{ - // Create a clone of this inline. - RenderPtr<RenderBlock> cloneBlock = clone(); - if (!isAnonymousBlock()) - cloneBlock->setContinuation(oldCont); - - if (!beforeChild && isAfterContent(lastChild())) - beforeChild = lastChild(); - - // If we are moving inline children from |this| to cloneBlock, then we need - // to clear our line box tree. - if (beforeChild && childrenInline()) - deleteLines(); - - // Now take all of the children from beforeChild to the end and remove - // them from |this| and place them in the clone. - moveChildrenTo(cloneBlock.get(), beforeChild, 0, true); - - // Hook |clone| up as the continuation of the middle block. - if (!cloneBlock->isAnonymousBlock()) - middleBlock->setContinuation(cloneBlock.get()); - - // We have been reparented and are now under the fromBlock. We need - // to walk up our block parent chain until we hit the containing anonymous columns block. - // Once we hit the anonymous columns block we're done. - RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); - RenderBoxModelObject* currChild = this; - RenderObject* currChildNextSibling = currChild->nextSibling(); - - while (curr && curr->isDescendantOf(fromBlock) && curr != fromBlock) { - RenderBlock* blockCurr = toRenderBlock(curr); - - // Create a new clone. - RenderPtr<RenderBlock> cloneChild = std::move(cloneBlock); - cloneBlock = blockCurr->clone(); - - // Insert our child clone as the first child. - cloneBlock->addChildIgnoringContinuation(cloneChild.leakPtr(), 0); - - // Hook the clone up as a continuation of |curr|. Note we do encounter - // anonymous blocks possibly as we walk up the block chain. When we split an - // anonymous block, there's no need to do any continuation hookup, since we haven't - // actually split a real element. - if (!blockCurr->isAnonymousBlock()) { - oldCont = blockCurr->continuation(); - blockCurr->setContinuation(cloneBlock.get()); - cloneBlock->setContinuation(oldCont); - } - - // Now we need to take all of the children starting from the first child - // *after* currChild and append them all to the clone. - blockCurr->moveChildrenTo(cloneBlock.get(), currChildNextSibling, 0, true); - - // Keep walking up the chain. - currChild = curr; - currChildNextSibling = currChild->nextSibling(); - curr = toRenderBoxModelObject(curr->parent()); - } - - // Now we are at the columns block level. We need to put the clone into the toBlock. - toBlock->insertChildInternal(cloneBlock.leakPtr(), nullptr, NotifyChildren); - - // Now take all the children after currChild and remove them from the fromBlock - // and put them in the toBlock. - if (currChildNextSibling && currChildNextSibling->parent() == fromBlock) - fromBlock->moveChildrenTo(toBlock, currChildNextSibling, 0, true); -} - -void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, - RenderObject* newChild, RenderBoxModelObject* oldCont) -{ - RenderBlock* pre = 0; - RenderBlock* block = containingColumnsBlock(); - - // Delete our line boxes before we do the inline split into continuations. - block->deleteLines(); - - bool madeNewBeforeBlock = false; - if (block->isAnonymousColumnsBlock()) { - // We can reuse this block and make it the preBlock of the next continuation. - pre = block; - pre->removePositionedObjects(0); - // FIXME-BLOCKFLOW remove this when splitFlow is moved to RenderBlockFlow. - if (pre->isRenderBlockFlow()) - toRenderBlockFlow(pre)->removeFloatingObjects(); - block = toRenderBlock(block->parent()); - } else { - // No anonymous block available for use. Make one. - pre = block->createAnonymousColumnsBlock(); - pre->setChildrenInline(false); - madeNewBeforeBlock = true; - } - - RenderBlock* post = block->createAnonymousColumnsBlock(); - post->setChildrenInline(false); - - RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); - if (madeNewBeforeBlock) - block->insertChildInternal(pre, boxFirst, NotifyChildren); - block->insertChildInternal(newBlockBox, boxFirst, NotifyChildren); - block->insertChildInternal(post, boxFirst, NotifyChildren); - block->setChildrenInline(false); - - if (madeNewBeforeBlock) - block->moveChildrenTo(pre, boxFirst, 0, true); - - splitBlocks(pre, post, newBlockBox, beforeChild, oldCont); - - // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting - // time in makeChildrenNonInline by just setting this explicitly up front. - newBlockBox->setChildrenInline(false); - - // We delayed adding the newChild until now so that the |newBlockBox| would be fully - // connected, thus allowing newChild access to a renderArena should it need - // to wrap itself in additional boxes (e.g., table construction). - newBlockBox->addChild(newChild); - - // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) - // get deleted properly. Because objects moves from the pre block into the post block, we want to - // make new line boxes instead of leaving the old line boxes around. - pre->setNeedsLayoutAndPrefWidthsRecalc(); - block->setNeedsLayoutAndPrefWidthsRecalc(); - post->setNeedsLayoutAndPrefWidthsRecalc(); -} - -void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild) +void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) { - RenderBlock* pre = 0; - RenderBlock* post = 0; - RenderBlock* block = this; // Eventually block will not just be |this|, but will also be a block nested inside |this|. Assign to a variable - // so that we don't have to patch all of the rest of the code later on. - - // Delete the block's line boxes before we do the split. - block->deleteLines(); - - if (beforeChild && beforeChild->parent() != this) - beforeChild = splitAnonymousBoxesAroundChild(beforeChild); - - if (beforeChild != firstChild()) { - pre = block->createAnonymousColumnsBlock(); - pre->setChildrenInline(block->childrenInline()); - } - - if (beforeChild) { - post = block->createAnonymousColumnsBlock(); - post->setChildrenInline(block->childrenInline()); - } - - RenderObject* boxFirst = block->firstChild(); - if (pre) - block->insertChildInternal(pre, boxFirst, NotifyChildren); - block->insertChildInternal(newBlockBox, boxFirst, NotifyChildren); - if (post) - block->insertChildInternal(post, boxFirst, NotifyChildren); - block->setChildrenInline(false); - - // The pre/post blocks always have layers, so we know to always do a full insert/remove (so we pass true as the last argument). - block->moveChildrenTo(pre, boxFirst, beforeChild, true); - block->moveChildrenTo(post, beforeChild, 0, true); - - // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting - // time in makeChildrenNonInline by just setting this explicitly up front. - newBlockBox->setChildrenInline(false); - - // We delayed adding the newChild until now so that the |newBlockBox| would be fully - // connected, thus allowing newChild access to a renderArena should it need - // to wrap itself in additional boxes (e.g., table construction). - newBlockBox->addChild(newChild); - - // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) - // get deleted properly. Because objects moved from the pre block into the post block, we want to - // make new line boxes instead of leaving the old line boxes around. - if (pre) - pre->setNeedsLayoutAndPrefWidthsRecalc(); - block->setNeedsLayoutAndPrefWidthsRecalc(); - if (post) - post->setNeedsLayoutAndPrefWidthsRecalc(); -} - -RenderBlock* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) -{ - // FIXME: This function is the gateway for the addition of column-span support. It will - // be added to in three stages: - // (1) Immediate children of a multi-column block can span. - // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span. - // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we - // cross the streams and have to cope with both types of continuations mixed together). - // This function currently supports (1) and (2). - RenderBlock* columnsBlockAncestor = 0; - if (!newChild->isText() && newChild->style().columnSpan() && !newChild->isBeforeOrAfterContent() - && !newChild->isFloatingOrOutOfFlowPositioned() && !newChild->isInline() && !isAnonymousColumnSpanBlock()) { - columnsBlockAncestor = containingColumnsBlock(false); - if (columnsBlockAncestor) { - // Make sure that none of the parent ancestors have a continuation. - // If yes, we do not want split the block into continuations. - RenderElement* curr = this; - while (curr && curr != columnsBlockAncestor) { - if (curr->isRenderBlock() && toRenderBlock(curr)->continuation()) { - columnsBlockAncestor = 0; - break; - } - curr = curr->parent(); - } - } - } - return columnsBlockAncestor; + if (continuation() && !isAnonymousBlock()) + addChildToContinuation(newChild, beforeChild); + else + addChildIgnoringContinuation(newChild, beforeChild); } -void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) +void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) { if (beforeChild && beforeChild->parent() != this) { RenderElement* beforeChildContainer = beforeChild->parent(); @@ -764,47 +591,6 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, // safe fallback to use the topmost beforeChild container. beforeChild = beforeChildContainer; } - } else { - // We will reach here when beforeChild is a run-in element. - // If run-in element precedes a block-level element, it becomes the - // the first inline child of that block level element. The insertion - // point will be before that block-level element. - ASSERT(beforeChild->isRunIn()); - beforeChild = beforeChildContainer; - } - } - - // Nothing goes before the intruded run-in. - if (beforeChild && beforeChild->isRunIn() && runInIsPlacedIntoSiblingBlock(*beforeChild)) - beforeChild = beforeChild->nextSibling(); - - // Check for a spanning element in columns. - if (gColumnFlowSplitEnabled) { - RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); - if (columnsBlockAncestor) { - TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); - // We are placing a column-span element inside a block. - RenderBlock* newBox = createAnonymousColumnSpanBlock(); - - if (columnsBlockAncestor != this && !isRenderFlowThread()) { - // We are nested inside a multi-column element and are being split by the span. We have to break up - // our block into continuations. - RenderBoxModelObject* oldContinuation = continuation(); - - // When we split an anonymous block, there's no need to do any continuation hookup, - // since we haven't actually split a real element. - if (!isAnonymousBlock()) - setContinuation(newBox); - - splitFlow(beforeChild, newBox, newChild, oldContinuation); - return; - } - - // We have to perform a split of this block's children. This involves creating an anonymous block box to hold - // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into - // one anonymous columns block, and all of the children after |newChild| go into another anonymous block. - makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild); - return; } } @@ -830,7 +616,7 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild(); if (afterChild && afterChild->isAnonymousBlock()) { - toRenderBlock(afterChild)->addChild(newChild); + downcast<RenderBlock>(*afterChild).addChild(newChild); return; } @@ -847,30 +633,11 @@ void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderBox::addChild(newChild, beforeChild); - // Handle placement of run-ins. - placeRunInIfNeeded(*newChild); - - if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) - toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); + if (madeBoxesNonInline && is<RenderBlock>(parent()) && isAnonymousBlock()) + downcast<RenderBlock>(*parent()).removeLeftoverAnonymousBlock(this); // this object may be dead here } -void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - if (continuation() && !isAnonymousBlock()) - addChildToContinuation(newChild, beforeChild); - else - addChildIgnoringContinuation(newChild, beforeChild); -} - -void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) -{ - if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock())) - addChildToAnonymousColumnBlocks(newChild, beforeChild); - else - addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); -} - static void getInlineRun(RenderObject* start, RenderObject* boundary, RenderObject*& inlineRunStart, RenderObject*& inlineRunEnd) @@ -914,14 +681,7 @@ static void getInlineRun(RenderObject* start, RenderObject* boundary, void RenderBlock::deleteLines() { if (AXObjectCache* cache = document().existingAXObjectCache()) - cache->recomputeIsIgnored(this); -} - -void RenderBlock::invalidateLineLayoutPath() -{ - if (m_lineLayoutPath == ForceLineBoxesPath) - return; - m_lineLayoutPath = UndeterminedPath; + cache->recomputeDeferredIsIgnored(*this); } void RenderBlock::makeChildrenNonInline(RenderObject* insertionPoint) @@ -944,13 +704,6 @@ void RenderBlock::makeChildrenNonInline(RenderObject* insertionPoint) deleteLines(); - // Since we are going to have block children, we have to move - // back the run-in to its original place. - if (child->isRunIn()) { - moveRunInToOriginalPosition(*child); - child = firstChild(); - } - while (child) { RenderObject* inlineRunStart; RenderObject* inlineRunEnd; @@ -979,7 +732,7 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) ASSERT(child->isAnonymousBlock()); ASSERT(!child->childrenInline()); - if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock()))) + if (child->continuation()) return; RenderObject* firstAnChild = child->firstChild(); @@ -1017,7 +770,7 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) child->m_next = 0; // Remove all the information in the flow thread associated with the leftover anonymous block. - child->removeFromRenderFlowThread(); + child->resetFlowThreadStateOnRemoval(); child->setParent(0); child->setPreviousSibling(0); @@ -1026,122 +779,103 @@ void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) child->destroy(); } -static bool canMergeAnonymousBlock(RenderBlock* anonymousBlock) +static bool canDropAnonymousBlock(const RenderBlock& anonymousBlock) { - if (anonymousBlock->beingDestroyed() || anonymousBlock->continuation()) + if (anonymousBlock.beingDestroyed() || anonymousBlock.continuation()) return false; - if (anonymousBlock->isRubyRun() || anonymousBlock->isRubyBase()) + if (anonymousBlock.isRubyRun() || anonymousBlock.isRubyBase()) return false; return true; } static bool canMergeContiguousAnonymousBlocks(RenderObject& oldChild, RenderObject* previous, RenderObject* next) { - if (oldChild.documentBeingDestroyed() || oldChild.isInline() || oldChild.virtualContinuation()) + if (oldChild.renderTreeBeingDestroyed() || oldChild.isInline() || oldChild.virtualContinuation()) return false; if (previous) { if (!previous->isAnonymousBlock()) return false; - RenderBlock* previousAnonymousBlock = toRenderBlock(previous); - if (!canMergeAnonymousBlock(previousAnonymousBlock)) - return false; - // FIXME: This check isn't required when inline run-ins can't be split into continuations. - RenderObject* child = previousAnonymousBlock->firstChild(); - if (child && child->isInline() && child->isRunIn()) + RenderBlock& previousAnonymousBlock = downcast<RenderBlock>(*previous); + if (!canDropAnonymousBlock(previousAnonymousBlock)) return false; } if (next) { if (!next->isAnonymousBlock()) return false; - RenderBlock* nextAnonymousBlock = toRenderBlock(next); - if (!canMergeAnonymousBlock(nextAnonymousBlock)) + RenderBlock& nextAnonymousBlock = downcast<RenderBlock>(*next); + if (!canDropAnonymousBlock(nextAnonymousBlock)) return false; } - if (!previous || !next) - return true; - - // Make sure the types of the anonymous blocks match up. - return previous->isAnonymousColumnsBlock() == next->isAnonymousColumnsBlock() - && previous->isAnonymousColumnSpanBlock() == next->isAnonymousColumnSpanBlock(); + return true; } -void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderBlock* child) +void RenderBlock::dropAnonymousBoxChild(RenderBlock& parent, RenderBlock& child) { - parent->setNeedsLayoutAndPrefWidthsRecalc(); - parent->setChildrenInline(child->childrenInline()); - RenderObject* nextSibling = child->nextSibling(); - - RenderFlowThread* childFlowThread = child->flowThreadContainingBlock(); - CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread); - - parent->removeChildInternal(*child, child->hasLayer() ? NotifyChildren : DontNotifyChildren); - child->moveAllChildrenTo(parent, nextSibling, child->hasLayer()); + parent.setNeedsLayoutAndPrefWidthsRecalc(); + parent.setChildrenInline(child.childrenInline()); + RenderObject* nextSibling = child.nextSibling(); + parent.removeChildInternal(child, child.hasLayer() ? NotifyChildren : DontNotifyChildren); + child.moveAllChildrenTo(&parent, nextSibling, child.hasLayer()); // Delete the now-empty block's lines and nuke it. - child->deleteLines(); - if (childFlowThread && childFlowThread->isRenderNamedFlowThread()) - toRenderNamedFlowThread(childFlowThread)->removeFlowChildInfo(child); - child->destroy(); + child.deleteLines(); + child.destroy(); } void RenderBlock::removeChild(RenderObject& oldChild) { // No need to waste time in merging or removing empty anonymous blocks. // We can just bail out if our document is getting destroyed. - if (documentBeingDestroyed()) { + if (renderTreeBeingDestroyed()) { RenderBox::removeChild(oldChild); return; } - // This protects against column split flows when anonymous blocks are getting merged. - TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); - - // If this child is a block, and if our previous and next siblings are - // both anonymous blocks with inline content, then we can go ahead and - // fold the inline content back together. + // If this child is a block, and if our previous and next siblings are both anonymous blocks + // with inline content, then we can fold the inline content back together. RenderObject* prev = oldChild.previousSibling(); RenderObject* next = oldChild.nextSibling(); bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next); if (canMergeAnonymousBlocks && prev && next) { prev->setNeedsLayoutAndPrefWidthsRecalc(); - RenderBlock* nextBlock = toRenderBlock(next); - RenderBlock* prevBlock = toRenderBlock(prev); + RenderBlock& nextBlock = downcast<RenderBlock>(*next); + RenderBlock& prevBlock = downcast<RenderBlock>(*prev); if (prev->childrenInline() != next->childrenInline()) { - RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; - RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock; + RenderBlock& inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; + RenderBlock& blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock; // Place the inline children block inside of the block children block instead of deleting it. // In order to reuse it, we have to reset it to just be a generic anonymous block. Make sure // to clear out inherited column properties by just making a new style, and to also clear the // column span flag if it is set. - ASSERT(!inlineChildrenBlock->continuation()); + ASSERT(!inlineChildrenBlock.continuation()); // Cache this value as it might get changed in setStyle() call. - bool inlineChildrenBlockHasLayer = inlineChildrenBlock->hasLayer(); - inlineChildrenBlock->setStyle(RenderStyle::createAnonymousStyleWithDisplay(&style(), BLOCK)); - removeChildInternal(*inlineChildrenBlock, inlineChildrenBlockHasLayer ? NotifyChildren : DontNotifyChildren); + bool inlineChildrenBlockHasLayer = inlineChildrenBlock.hasLayer(); + inlineChildrenBlock.setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK)); + removeChildInternal(inlineChildrenBlock, inlineChildrenBlockHasLayer ? NotifyChildren : DontNotifyChildren); // Now just put the inlineChildrenBlock inside the blockChildrenBlock. - RenderObject* beforeChild = prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : nullptr; - blockChildrenBlock->insertChildInternal(inlineChildrenBlock, beforeChild, - (inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer()) ? NotifyChildren : DontNotifyChildren); + RenderObject* beforeChild = prev == &inlineChildrenBlock ? blockChildrenBlock.firstChild() : nullptr; + blockChildrenBlock.insertChildInternal(&inlineChildrenBlock, beforeChild, + (inlineChildrenBlockHasLayer || blockChildrenBlock.hasLayer()) ? NotifyChildren : DontNotifyChildren); next->setNeedsLayoutAndPrefWidthsRecalc(); // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child // of "this". we null out prev or next so that is not used later in the function. - if (inlineChildrenBlock == prevBlock) - prev = 0; + if (&inlineChildrenBlock == &prevBlock) + prev = nullptr; else - next = 0; + next = nullptr; } else { // Take all the children out of the |next| block and put them in // the |prev| block. - nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); + nextBlock.moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock.hasLayer() || prevBlock.hasLayer()); // Delete the now-empty block's lines and nuke it. - nextBlock->deleteLines(); - nextBlock->destroy(); - next = 0; + nextBlock.deleteLines(); + nextBlock.destroy(); + next = nullptr; } } @@ -1150,20 +884,26 @@ void RenderBlock::removeChild(RenderObject& oldChild) RenderBox::removeChild(oldChild); RenderObject* child = prev ? prev : next; - if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canCollapseAnonymousBlockChild()) { + if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canDropAnonymousBlockChild()) { // The removal has knocked us down to containing only a single anonymous - // box. We can go ahead and pull the content right back up into our - // box. - collapseAnonymousBoxChild(this, toRenderBlock(child)); - } else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canCollapseAnonymousBlockChild()) { + // box. We can pull the content right back up into our box. + dropAnonymousBoxChild(*this, downcast<RenderBlock>(*child)); + } else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canDropAnonymousBlockChild()) { // It's possible that the removal has knocked us down to a single anonymous - // block with pseudo-style element siblings (e.g. first-letter). If these - // are floating, then we need to pull the content up also. - RenderBlock* anonBlock = toRenderBlock((prev && prev->isAnonymousBlock()) ? prev : next); - if ((anonBlock->previousSibling() || anonBlock->nextSibling()) - && (!anonBlock->previousSibling() || (anonBlock->previousSibling()->style().styleType() != NOPSEUDO && anonBlock->previousSibling()->isFloating() && !anonBlock->previousSibling()->previousSibling())) - && (!anonBlock->nextSibling() || (anonBlock->nextSibling()->style().styleType() != NOPSEUDO && anonBlock->nextSibling()->isFloating() && !anonBlock->nextSibling()->nextSibling()))) { - collapseAnonymousBoxChild(this, anonBlock); + // block with floating siblings. + RenderBlock& anonBlock = downcast<RenderBlock>((prev && prev->isAnonymousBlock()) ? *prev : *next); + if (canDropAnonymousBlock(anonBlock)) { + bool dropAnonymousBlock = true; + for (auto& sibling : childrenOfType<RenderObject>(*this)) { + if (&sibling == &anonBlock) + continue; + if (!sibling.isFloating()) { + dropAnonymousBlock = false; + break; + } + } + if (dropAnonymousBlock) + dropAnonymousBoxChild(*this, anonBlock); } } @@ -1178,28 +918,41 @@ void RenderBlock::removeChild(RenderObject& oldChild) auto containingBlockIgnoringAnonymous = containingBlock(); while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymousBlock()) containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock(); - for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) { - if (curr->virtualContinuation() != this) + for (RenderObject* current = this; current; current = current->previousInPreOrder(containingBlockIgnoringAnonymous)) { + if (current->virtualContinuation() != this) continue; // Found our previous continuation. We just need to point it to // |this|'s next continuation. RenderBoxModelObject* nextContinuation = continuation(); - if (curr->isRenderInline()) - toRenderInline(curr)->setContinuation(nextContinuation); - else if (curr->isRenderBlock()) - toRenderBlock(curr)->setContinuation(nextContinuation); + if (is<RenderInline>(*current)) + downcast<RenderInline>(*current).setContinuation(nextContinuation); + else if (is<RenderBlock>(*current)) + downcast<RenderBlock>(*current).setContinuation(nextContinuation); else ASSERT_NOT_REACHED(); break; } - setContinuation(0); + setContinuation(nullptr); destroy(); } } } +bool RenderBlock::childrenPreventSelfCollapsing() const +{ + // Whether or not we collapse is dependent on whether all our normal flow children + // are also self-collapsing. + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->isFloatingOrOutOfFlowPositioned()) + continue; + if (!child->isSelfCollapsingBlock()) + return true; + } + return false; +} + bool RenderBlock::isSelfCollapsingBlock() const { // We are not self-collapsing if we @@ -1216,9 +969,9 @@ bool RenderBlock::isSelfCollapsingBlock() const Length logicalHeightLength = style().logicalHeight(); bool hasAutoHeight = logicalHeightLength.isAuto(); - if (logicalHeightLength.isPercent() && !document().inQuirksMode()) { + if (logicalHeightLength.isPercentOrCalculated() && !document().inQuirksMode()) { hasAutoHeight = true; - for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { + for (RenderBlock* cb = containingBlock(); cb && !is<RenderView>(*cb); cb = cb->containingBlock()) { if (cb->style().logicalHeight().isFixed() || cb->isTableCell()) hasAutoHeight = false; } @@ -1226,78 +979,82 @@ bool RenderBlock::isSelfCollapsingBlock() const // If the height is 0 or auto, then whether or not we are a self-collapsing block depends // on whether we have content that is all self-collapsing or not. - if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) { - // If the block has inline children, see if we generated any line boxes. If we have any - // line boxes, then we can't be self-collapsing, since we have content. - if (childrenInline()) - return !hasLines(); - - // Whether or not we collapse is dependent on whether all our normal flow children - // are also self-collapsing. - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isFloatingOrOutOfFlowPositioned()) - continue; - if (!child->isSelfCollapsingBlock()) - return false; - } - return true; - } + if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercentOrCalculated()) && logicalHeightLength.isZero())) + return !childrenPreventSelfCollapsing(); + return false; } -void RenderBlock::startDelayUpdateScrollInfo() +static inline UpdateScrollInfoAfterLayoutTransaction* currentUpdateScrollInfoAfterLayoutTransaction() { - if (gDelayUpdateScrollInfo == 0) { - ASSERT(!gDelayedUpdateScrollInfoSet); - gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet; - } - ASSERT(gDelayedUpdateScrollInfoSet); - ++gDelayUpdateScrollInfo; + if (!updateScrollInfoAfterLayoutTransactionStack()) + return nullptr; + return &updateScrollInfoAfterLayoutTransactionStack()->last(); +} + +void RenderBlock::beginUpdateScrollInfoAfterLayoutTransaction() +{ + if (!updateScrollInfoAfterLayoutTransactionStack()) + updateScrollInfoAfterLayoutTransactionStack() = std::make_unique<DelayedUpdateScrollInfoStack>(); + if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty() || currentUpdateScrollInfoAfterLayoutTransaction()->view != &view()) + updateScrollInfoAfterLayoutTransactionStack()->append(UpdateScrollInfoAfterLayoutTransaction(view())); + ++currentUpdateScrollInfoAfterLayoutTransaction()->nestedCount; } -void RenderBlock::finishDelayUpdateScrollInfo() +void RenderBlock::endAndCommitUpdateScrollInfoAfterLayoutTransaction() { - --gDelayUpdateScrollInfo; - ASSERT(gDelayUpdateScrollInfo >= 0); - if (gDelayUpdateScrollInfo == 0) { - ASSERT(gDelayedUpdateScrollInfoSet); + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + ASSERT(transaction); + ASSERT(transaction->view == &view()); + if (--transaction->nestedCount) + return; - OwnPtr<DelayedUpdateScrollInfoSet> infoSet(adoptPtr(gDelayedUpdateScrollInfoSet)); - gDelayedUpdateScrollInfoSet = 0; + // Calling RenderLayer::updateScrollInfoAfterLayout() may cause its associated block to layout again and + // updates its scroll info (i.e. call RenderBlock::updateScrollInfoAfterLayout()). We remove |transaction| + // from the transaction stack to ensure that all subsequent calls to RenderBlock::updateScrollInfoAfterLayout() + // are dispatched immediately. That is, to ensure that such subsequent calls aren't added to |transaction| + // while we are processing it. + Vector<RenderBlock*> blocksToUpdate; + copyToVector(transaction->blocks, blocksToUpdate); + updateScrollInfoAfterLayoutTransactionStack()->removeLast(); + if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty()) + updateScrollInfoAfterLayoutTransactionStack() = nullptr; - for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) { - RenderBlock* block = *it; - if (block->hasOverflowClip()) { - block->layer()->updateScrollInfoAfterLayout(); - block->clearLayoutOverflow(); - } - } + for (auto* block : blocksToUpdate) { + ASSERT(block->hasOverflowClip()); + block->layer()->updateScrollInfoAfterLayout(); + block->clearLayoutOverflow(); } } -void RenderBlock::removeFromDelayedUpdateScrollInfoSet() +void RenderBlock::removeFromUpdateScrollInfoAfterLayoutTransaction() { - if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0)) - gDelayedUpdateScrollInfoSet->remove(this); + if (UNLIKELY(updateScrollInfoAfterLayoutTransactionStack().get() != 0)) { + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + ASSERT(transaction); + if (transaction->view == &view()) + transaction->blocks.remove(this); + } } void RenderBlock::updateScrollInfoAfterLayout() { - if (hasOverflowClip()) { - if (style().isFlippedBlocksWritingMode()) { - // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937 - // Workaround for now. We cannot delay the scroll info for overflow - // for items with opposite writing directions, as the contents needs - // to overflow in that direction - layer()->updateScrollInfoAfterLayout(); + if (!hasOverflowClip()) + return; + + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937 + // Workaround for now. We cannot delay the scroll info for overflow + // for items with opposite writing directions, as the contents needs + // to overflow in that direction + if (!style().isFlippedBlocksWritingMode()) { + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + if (transaction && transaction->view == &view()) { + transaction->blocks.add(this); return; } - - if (gDelayUpdateScrollInfo) - gDelayedUpdateScrollInfoSet->add(this); - else - layer()->updateScrollInfoAfterLayout(); } + if (layer()) + layer()->updateScrollInfoAfterLayout(); } void RenderBlock::layout() @@ -1314,247 +1071,48 @@ void RenderBlock::layout() // It's safe to check for control clip here, since controls can never be table cells. // If we have a lightweight clip, there can never be any overflow from children. - if (hasControlClip() && m_overflow && !gDelayUpdateScrollInfo) + UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); + bool isDelayingUpdateScrollInfoAfterLayoutInView = transaction && transaction->view == &view(); + if (hasControlClip() && m_overflow && !isDelayingUpdateScrollInfoAfterLayoutInView) clearLayoutOverflow(); invalidateBackgroundObscurationStatus(); } -static RenderBlockRareData* getRareData(const RenderBlock* block) +static RenderBlockRareData* getBlockRareData(const RenderBlock& block) { - return gRareDataMap ? gRareDataMap->get(block) : 0; + return gRareDataMap ? gRareDataMap->get(&block) : nullptr; } -static RenderBlockRareData& ensureRareData(const RenderBlock* block) +static RenderBlockRareData& ensureBlockRareData(const RenderBlock& block) { if (!gRareDataMap) gRareDataMap = new RenderBlockRareDataMap; - auto& rareData = gRareDataMap->add(block, nullptr).iterator->value; + auto& rareData = gRareDataMap->add(&block, nullptr).iterator->value; if (!rareData) rareData = std::make_unique<RenderBlockRareData>(); return *rareData.get(); } -#if ENABLE(CSS_SHAPES) -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->setNormalChildNeedsLayoutBit(true); - child->markShapeInsideDescendantsForLayout(); - child->layoutIfNeeded(); -} - -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 118073: 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(); -} - -ShapeInsideInfo& RenderBlock::ensureShapeInsideInfo() -{ - RenderBlockRareData& rareData = ensureRareData(this); - if (!rareData.m_shapeInsideInfo) - setShapeInsideInfo(std::make_unique<ShapeInsideInfo>(*this)); - return *rareData.m_shapeInsideInfo; -} - -ShapeInsideInfo* RenderBlock::shapeInsideInfo() const -{ - RenderBlockRareData* rareData = getRareData(this); - if (!rareData || !rareData->m_shapeInsideInfo) - return nullptr; - return ShapeInsideInfo::isEnabledFor(*this) ? rareData->m_shapeInsideInfo.get() : nullptr; -} - -void RenderBlock::setShapeInsideInfo(std::unique_ptr<ShapeInsideInfo> value) -{ - ensureRareData(this).m_shapeInsideInfo = std::move(value); -} - -void RenderBlock::markShapeInsideDescendantsForLayout() -{ - if (!everHadLayout()) - return; - if (childrenInline()) { - setNeedsLayout(); - invalidateLineLayoutPath(); - return; - } - - for (auto& childBlock : childrenOfType<RenderBlock>(*this)) - childBlock.markShapeInsideDescendantsForLayout(); -} - -ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const -{ - // This may be called outside layout when switching from SimpleLineLayout to line boxes. This case never has shape info. - if (!view().layoutState()) - return nullptr; - - ShapeInsideInfo* shapeInsideInfo = view().layoutState()->shapeInsideInfo(); - - if (!shapeInsideInfo && flowThreadContainingBlock() && allowsShapeInsideInfoSharing()) { - 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::fromPixel(1); - RenderRegion* region = regionAtBlockOffset(offset); - if (region && region->logicalHeight()) - shapeInsideInfo = region->shapeInsideInfo(); - } - - return shapeInsideInfo; -} - -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(); -} - -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()); - } -} -#endif - -bool RenderBlock::updateShapesBeforeBlockLayout() -{ -#if ENABLE(CSS_SHAPES) - if (!flowThreadContainingBlock() && !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(); - - setLogicalHeight(oldHeight); - setLogicalTop(oldTop); - - return shapeInfoRequiresRelayout(this); -#else - return false; -#endif -} - -void RenderBlock::updateShapesAfterBlockLayout(bool heightChanged) -{ -#if ENABLE(CSS_SHAPES) - // A previous sibling has changed dimension, so we need to relayout the shape with the content - ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); - if (heightChanged && shapeInsideInfo) - shapeInsideInfo->dirtyShapeSize(); -#else - UNUSED_PARAM(heightChanged); -#endif -} - -void RenderBlock::prepareShapesAndPaginationBeforeBlockLayout(bool& relayoutChildren) +void RenderBlock::preparePaginationBeforeBlockLayout(bool& relayoutChildren) { // Regions changing widths can force us to relayout our children. RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (updateShapesBeforeBlockLayout()) - relayoutChildren = true; if (flowThread) flowThread->logicalWidthChangedInRegionsForBlock(this, relayoutChildren); } -bool RenderBlock::updateLogicalWidthAndColumnWidth() +bool RenderBlock::recomputeLogicalWidth() { LayoutUnit oldWidth = logicalWidth(); - LayoutUnit oldColumnWidth = computedColumnWidth(); - + updateLogicalWidth(); - computeColumnCountAndWidth(); - - bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged; - m_hasBorderOrPaddingLogicalWidthChanged = false; + + bool hasBorderOrPaddingLogicalWidthChanged = this->hasBorderOrPaddingLogicalWidthChanged(); + setShouldForceRelayoutChildren(false); - return oldWidth != logicalWidth() || oldColumnWidth != computedColumnWidth() || hasBorderOrPaddingLogicalWidthChanged; + return oldWidth != logicalWidth() || hasBorderOrPaddingLogicalWidthChanged; } void RenderBlock::layoutBlock(bool, LayoutUnit) @@ -1565,43 +1123,32 @@ void RenderBlock::layoutBlock(bool, LayoutUnit) void RenderBlock::addOverflowFromChildren() { - if (!hasColumns()) { - if (childrenInline()) - addOverflowFromInlineChildren(); - else - addOverflowFromBlockChildren(); - - // If this block is flowed inside a flow thread, make sure its overflow is propagated to the containing regions. - if (m_overflow) { - if (RenderFlowThread* containingFlowThread = flowThreadContainingBlock()) - containingFlowThread->addRegionsVisualOverflow(this, m_overflow->visualOverflowRect()); - } - } else { - ColumnInfo* colInfo = columnInfo(); - if (columnCount(colInfo)) { - LayoutRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1); - addLayoutOverflow(lastRect); - if (!hasOverflowClip()) - addVisualOverflow(lastRect); - } + if (childrenInline()) + addOverflowFromInlineChildren(); + else + addOverflowFromBlockChildren(); + + // If this block is flowed inside a flow thread, make sure its overflow is propagated to the containing regions. + if (m_overflow) { + if (RenderFlowThread* containingFlowThread = flowThreadContainingBlock()) + containingFlowThread->addRegionsVisualOverflow(this, m_overflow->visualOverflowRect()); } } +// Overflow is always relative to the border-box of the element in question. +// Therefore, if the element has a vertical scrollbar placed on the left, an overflow rect at x=2px would conceptually intersect the scrollbar. void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) { clearOverflow(); - - // Add overflow from children. addOverflowFromChildren(); - // Add in the overflow from positioned objects. addOverflowFromPositionedObjects(); if (hasOverflowClip()) { // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always // be considered reachable. - LayoutRect clientRect(clientBoxRect()); + LayoutRect clientRect(flippedClientBoxRect()); LayoutRect rectToApply; if (isHorizontalWritingMode()) rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, std::max<LayoutUnit>(0, oldClientAfterEdge - clientRect.y())); @@ -1612,14 +1159,11 @@ void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); } - // Add visual overflow from box-shadow and border-image-outset. + // Add visual overflow from box-shadow, border-image-outset and outline. addVisualEffectOverflow(); // Add visual overflow from theme. addVisualOverflowFromTheme(); - - if (isRenderNamedFlowThread()) - toRenderNamedFlowThread(this)->computeOversetStateForRegions(oldClientAfterEdge); } void RenderBlock::clearLayoutOverflow() @@ -1638,7 +1182,7 @@ void RenderBlock::clearLayoutOverflow() void RenderBlock::addOverflowFromBlockChildren() { - for (auto child = firstChildBox(); child; child = child->nextSiblingBox()) { + for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) { if (!child->isFloatingOrOutOfFlowPositioned()) addOverflowFromChild(child); } @@ -1654,12 +1198,8 @@ void RenderBlock::addOverflowFromPositionedObjects() RenderBox* positionedObject = *it; // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content. - if (positionedObject->style().position() != FixedPosition) { - LayoutUnit x = positionedObject->x(); - if (style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) - x -= verticalScrollbarWidth(); - addOverflowFromChild(positionedObject, LayoutSize(x, positionedObject->y())); - } + if (positionedObject->style().position() != FixedPosition) + addOverflowFromChild(positionedObject, { positionedObject->x(), positionedObject->y() }); } } @@ -1668,183 +1208,14 @@ void RenderBlock::addVisualOverflowFromTheme() if (!style().hasAppearance()) return; - IntRect inflatedRect = pixelSnappedBorderBoxRect(); - theme().adjustRepaintRect(this, inflatedRect); - addVisualOverflow(inflatedRect); + FloatRect inflatedRect = borderBoxRect(); + theme().adjustRepaintRect(*this, inflatedRect); + addVisualOverflow(snappedIntRect(LayoutRect(inflatedRect))); if (RenderFlowThread* flowThread = flowThreadContainingBlock()) flowThread->addRegionsVisualOverflowFromTheme(this); } -bool RenderBlock::isTopLayoutOverflowAllowed() const -{ - bool hasTopOverflow = RenderBox::isTopLayoutOverflowAllowed(); - if (!hasColumns() || style().columnProgression() == NormalColumnProgression) - return hasTopOverflow; - - if (!(isHorizontalWritingMode() ^ !style().hasInlineColumnAxis())) - hasTopOverflow = !hasTopOverflow; - - return hasTopOverflow; -} - -bool RenderBlock::isLeftLayoutOverflowAllowed() const -{ - bool hasLeftOverflow = RenderBox::isLeftLayoutOverflowAllowed(); - if (!hasColumns() || style().columnProgression() == NormalColumnProgression) - return hasLeftOverflow; - - if (isHorizontalWritingMode() ^ !style().hasInlineColumnAxis()) - hasLeftOverflow = !hasLeftOverflow; - - return hasLeftOverflow; -} - -bool RenderBlock::expandsToEncloseOverhangingFloats() const -{ - return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated()) - || hasColumns() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot(); -} - -static void destroyRunIn(RenderBoxModelObject& runIn) -{ - ASSERT(runIn.isRunIn()); - ASSERT(!runIn.firstChild()); - - // Delete our line box tree. This is needed as our children got moved - // and our line box tree is no longer valid. - if (runIn.isRenderBlock()) - toRenderBlock(runIn).deleteLines(); - else if (runIn.isRenderInline()) - toRenderInline(runIn).deleteLines(); - else - ASSERT_NOT_REACHED(); - - runIn.destroy(); -} - -void RenderBlock::placeRunInIfNeeded(RenderObject& newChild) -{ - if (newChild.isRunIn()) - moveRunInUnderSiblingBlockIfNeeded(newChild); - else if (RenderObject* prevSibling = newChild.previousSibling()) { - if (prevSibling->isRunIn()) - moveRunInUnderSiblingBlockIfNeeded(*prevSibling); - } -} - -RenderBoxModelObject& RenderBlock::createReplacementRunIn(RenderBoxModelObject& runIn) -{ - ASSERT(runIn.isRunIn()); - ASSERT(runIn.element()); - - RenderBoxModelObject* newRunIn = 0; - if (!runIn.isRenderBlockFlow()) - newRunIn = new RenderBlockFlow(*runIn.element(), runIn.style()); - else - newRunIn = new RenderInline(*runIn.element(), runIn.style()); - - runIn.element()->setRenderer(newRunIn); - newRunIn->initializeStyle(); - - runIn.moveAllChildrenTo(newRunIn, true); - - return *newRunIn; -} - -void RenderBlock::moveRunInUnderSiblingBlockIfNeeded(RenderObject& runIn) -{ - ASSERT(runIn.isRunIn()); - - // See if we have inline children. If the children aren't inline, - // then just treat the run-in as a normal block. - if (!runIn.childrenInline()) - return; - - // FIXME: We don't handle non-block elements with run-in for now. - if (!runIn.isRenderBlockFlow()) - return; - - // FIXME: We don't support run-ins with or as part of a continuation - // as it makes the back-and-forth placing complex. - if (runIn.isElementContinuation() || runIn.virtualContinuation()) - return; - - // Check if this node is allowed to run-in. E.g. <select> expects its renderer to - // be a RenderListBox or RenderMenuList, and hence cannot be a RenderInline run-in. - if (!runIn.canBeReplacedWithInlineRunIn()) - return; - - RenderObject* curr = runIn.nextSibling(); - if (!curr || !curr->isRenderBlock() || !curr->childrenInline()) - return; - - RenderBlock& nextSiblingBlock = toRenderBlock(*curr); - if (nextSiblingBlock.beingDestroyed()) - return; - - // Per CSS3, "A run-in cannot run in to a block that already starts with a - // run-in or that itself is a run-in". - if (nextSiblingBlock.isRunIn() || (nextSiblingBlock.firstChild() && nextSiblingBlock.firstChild()->isRunIn())) - return; - - if (nextSiblingBlock.isAnonymous() || nextSiblingBlock.isFloatingOrOutOfFlowPositioned()) - return; - - RenderBoxModelObject& oldRunIn = toRenderBoxModelObject(runIn); - RenderBoxModelObject& newRunIn = createReplacementRunIn(oldRunIn); - destroyRunIn(oldRunIn); - - // Now insert the new child under |curr| block. Use addChild instead of insertChildNode - // since it handles correct placement of the children, especially where we cannot insert - // anything before the first child. e.g. details tag. See https://bugs.webkit.org/show_bug.cgi?id=58228. - nextSiblingBlock.addChild(&newRunIn, nextSiblingBlock.firstChild()); - - // Make sure that |this| get a layout since its run-in child moved. - nextSiblingBlock.setNeedsLayoutAndPrefWidthsRecalc(); -} - -bool RenderBlock::runInIsPlacedIntoSiblingBlock(RenderObject& runIn) -{ - ASSERT(runIn.isRunIn()); - - // If we don't have a parent, we can't be moved into our sibling block. - if (!parent()) - return false; - - // An intruded run-in needs to be an inline. - if (!runIn.isRenderInline()) - return false; - - return true; -} - -void RenderBlock::moveRunInToOriginalPosition(RenderObject& runIn) -{ - ASSERT(runIn.isRunIn()); - - if (!runInIsPlacedIntoSiblingBlock(runIn)) - return; - - // FIXME: Run-in that are now placed in sibling block can break up into continuation - // chains when new children are added to it. We cannot easily send them back to their - // original place since that requires writing integration logic with RenderInline::addChild - // and all other places that might cause continuations to be created (without blowing away - // |this|). Disabling this feature for now to prevent crashes. - if (runIn.isElementContinuation() || runIn.virtualContinuation()) - return; - - RenderBoxModelObject& oldRunIn = toRenderBoxModelObject(runIn); - RenderBoxModelObject& newRunIn = createReplacementRunIn(oldRunIn); - destroyRunIn(oldRunIn); - - // Add the run-in block as our previous sibling. - parent()->addChild(&newRunIn, this); - - // Make sure that the parent holding the new run-in gets layout. - parent()->setNeedsLayoutAndPrefWidthsRecalc(); -} - LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox& child, LayoutUnit childMarginStart, RenderRegion* region) { LayoutUnit startPosition = startOffsetForContent(region); @@ -1857,7 +1228,7 @@ LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const Re if (region) blockOffset = std::max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage())); - LayoutUnit startOff = startOffsetForLineInRegion(blockOffset, false, region, logicalHeightForChild(child)); + LayoutUnit startOff = startOffsetForLineInRegion(blockOffset, DoNotIndentText, region, logicalHeightForChild(child)); if (style().textAlign() != WEBKIT_CENTER && !child.style().marginStartUsing(&style()).isAuto()) { if (childMarginStart < 0) @@ -1899,7 +1270,7 @@ void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, R { // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into // an auto value. Add a method to determine this, so that we can avoid the relayout. - if (relayoutChildren || (child.hasRelativeLogicalHeight() && !isRenderView()) || child.hasViewportPercentageLogicalHeight()) + if (relayoutChildren || (child.hasRelativeLogicalHeight() && !isRenderView())) child.setChildNeedsLayout(MarkOnlyThis); // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. @@ -1909,10 +1280,10 @@ void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, R void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants() { - if (!gPercentHeightDescendantsMap) + if (!percentHeightDescendantsMap) return; - TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this); + TrackedRendererListHashSet* descendants = percentHeightDescendantsMap->get(this); if (!descendants) return; @@ -1942,14 +1313,14 @@ void RenderBlock::simplifiedNormalFlowLayout() if (childrenInline()) { ListHashSet<RootInlineBox*> lineBoxes; for (InlineWalker walker(*this); !walker.atEnd(); walker.advance()) { - RenderObject* o = walker.current(); - if (!o->isOutOfFlowPositioned() && (o->isReplaced() || o->isFloating())) { - RenderBox& box = toRenderBox(*o); + RenderObject& renderer = *walker.current(); + if (!renderer.isOutOfFlowPositioned() && (renderer.isReplaced() || renderer.isFloating())) { + RenderBox& box = downcast<RenderBox>(renderer); box.layoutIfNeeded(); if (box.inlineBoxWrapper()) lineBoxes.add(&box.inlineBoxWrapper()->root()); - } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) - o->clearNeedsLayout(); + } else if (is<RenderText>(renderer) || (is<RenderInline>(renderer) && !walker.atEndOfInline())) + renderer.clearNeedsLayout(); } // FIXME: Glyph overflow will get lost in this case, but not really a big deal. @@ -1960,22 +1331,28 @@ void RenderBlock::simplifiedNormalFlowLayout() box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); } } else { - for (auto box = firstChildBox(); box; box = box->nextSiblingBox()) { + for (auto* box = firstChildBox(); box; box = box->nextSiblingBox()) { if (!box->isOutOfFlowPositioned()) box->layoutIfNeeded(); } } } +bool RenderBlock::canPerformSimplifiedLayout() const +{ + return (posChildNeedsLayout() || needsSimplifiedNormalFlowLayout()) && !normalChildNeedsLayout() && !selfNeedsLayout(); +} + bool RenderBlock::simplifiedLayout() { - if ((!posChildNeedsLayout() && !needsSimplifiedNormalFlowLayout()) || normalChildNeedsLayout() || selfNeedsLayout()) + if (!canPerformSimplifiedLayout()) return false; - LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasColumns() || hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); - - if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) + LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); + if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) { + statePusher.pop(); return false; + } // Lay out positioned descendants or objects that just need to recompute overflow. if (needsSimplifiedNormalFlowLayout()) @@ -1983,8 +1360,8 @@ bool RenderBlock::simplifiedLayout() // 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()); + if (is<RenderFlowThread>(*this)) + downcast<RenderFlowThread>(*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 @@ -2015,34 +1392,33 @@ bool RenderBlock::simplifiedLayout() return true; } -void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject& child) +void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderBox& positionedChild) { - if (child.style().position() != FixedPosition) + if (positionedChild.style().position() != FixedPosition) return; - bool hasStaticBlockPosition = child.style().hasStaticBlockPosition(isHorizontalWritingMode()); - bool hasStaticInlinePosition = child.style().hasStaticInlinePosition(isHorizontalWritingMode()); + bool hasStaticBlockPosition = positionedChild.style().hasStaticBlockPosition(isHorizontalWritingMode()); + bool hasStaticInlinePosition = positionedChild.style().hasStaticInlinePosition(isHorizontalWritingMode()); if (!hasStaticBlockPosition && !hasStaticInlinePosition) return; - auto o = child.parent(); - while (o && !o->isRenderView() && o->style().position() != AbsolutePosition) - o = o->parent(); - if (o->style().position() != AbsolutePosition) + auto* parent = positionedChild.parent(); + while (parent && !is<RenderView>(*parent) && parent->style().position() != AbsolutePosition) + parent = parent->parent(); + if (!parent || parent->style().position() != AbsolutePosition) return; - RenderBox& box = toRenderBox(child); if (hasStaticInlinePosition) { LogicalExtentComputedValues computedValues; - box.computeLogicalWidthInRegion(computedValues); + positionedChild.computeLogicalWidthInRegion(computedValues); LayoutUnit newLeft = computedValues.m_position; - if (newLeft != box.logicalLeft()) - box.setChildNeedsLayout(MarkOnlyThis); + if (newLeft != positionedChild.logicalLeft()) + positionedChild.setChildNeedsLayout(MarkOnlyThis); } else if (hasStaticBlockPosition) { - LayoutUnit oldTop = box.logicalTop(); - box.updateLogicalHeight(); - if (box.logicalTop() != oldTop) - box.setChildNeedsLayout(MarkOnlyThis); + LayoutUnit oldTop = positionedChild.logicalTop(); + positionedChild.updateLogicalHeight(); + if (positionedChild.logicalTop() != oldTop) + positionedChild.setChildNeedsLayout(MarkOnlyThis); } } @@ -2061,76 +1437,73 @@ LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox& child) co return margin; } -void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly) +void RenderBlock::layoutPositionedObject(RenderBox& r, bool relayoutChildren, bool fixedPositionObjectsOnly) { - TrackedRendererListHashSet* positionedDescendants = positionedObjects(); - if (!positionedDescendants) - return; - - if (hasColumns()) - view().layoutState()->clearPaginationInformation(); // Positioned objects are not part of the column flow, so they don't paginate with the columns. + estimateRegionRangeForBoxChild(r); - for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) { - RenderBox& r = **it; - - estimateRegionRangeForBoxChild(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); - if (fixedPositionObjectsOnly) { - r.layoutIfNeeded(); - continue; - } + // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So + // if this is a fixed position element, mark it for layout if it has an abspos ancestor and needs to move with that ancestor, i.e. + // it has static position. + markFixedPositionObjectForLayoutIfNeeded(r); + if (fixedPositionObjectsOnly) { + r.layoutIfNeeded(); + return; + } - // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the - // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned - // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is - // positioned explicitly) this should not incur a performance penalty. - if (relayoutChildren || (r.style().hasStaticBlockPosition(isHorizontalWritingMode()) && r.parent() != this)) - r.setChildNeedsLayout(MarkOnlyThis); - - // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. - if (relayoutChildren && r.needsPreferredWidthsRecalculation()) - r.setPreferredLogicalWidthsDirty(true, MarkOnlyThis); + // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the + // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned + // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is + // positioned explicitly) this should not incur a performance penalty. + if (relayoutChildren || (r.style().hasStaticBlockPosition(isHorizontalWritingMode()) && r.parent() != this)) + r.setChildNeedsLayout(MarkOnlyThis); - if (!r.needsLayout()) - r.markForPaginationRelayoutIfNeeded(); + // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. + if (relayoutChildren && r.needsPreferredWidthsRecalculation()) + r.setPreferredLogicalWidthsDirty(true, MarkOnlyThis); + + r.markForPaginationRelayoutIfNeeded(); + + // 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(); - // 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; - bool needsBlockDirectionLocationSetBeforeLayout = r.needsLayout() && view().layoutState()->needsBlockDirectionLocationSetBeforeLayout(); - if (needsBlockDirectionLocationSetBeforeLayout) { - if (isHorizontalWritingMode() == r.isHorizontalWritingMode()) - r.updateLogicalHeight(); - else - r.updateLogicalWidth(); - oldLogicalTop = logicalTopForChild(r); - } + // If we are paginated or in a line grid, compute a vertical position for our object now. + // If it's wrong we'll lay out again. + LayoutUnit oldLogicalTop = 0; + bool needsBlockDirectionLocationSetBeforeLayout = r.needsLayout() && view().layoutState()->needsBlockDirectionLocationSetBeforeLayout(); + if (needsBlockDirectionLocationSetBeforeLayout) { + if (isHorizontalWritingMode() == r.isHorizontalWritingMode()) + r.updateLogicalHeight(); + else + r.updateLogicalWidth(); + oldLogicalTop = logicalTopForChild(r); + } - r.layoutIfNeeded(); + r.layoutIfNeeded(); - // Lay out again if our estimate was wrong. - if (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(r) != oldLogicalTop) { - r.setChildNeedsLayout(MarkOnlyThis); - r.layoutIfNeeded(); - } + // Lay out again if our estimate was wrong. + if (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(r) != oldLogicalTop) { + r.setChildNeedsLayout(MarkOnlyThis); + r.layoutIfNeeded(); + } - if (updateRegionRangeForBoxChild(r)) { - r.setNeedsLayout(MarkOnlyThis); - r.layoutIfNeeded(); - } + if (updateRegionRangeForBoxChild(r)) { + r.setNeedsLayout(MarkOnlyThis); + r.layoutIfNeeded(); } +} + +void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly) +{ + TrackedRendererListHashSet* positionedDescendants = positionedObjects(); + if (!positionedDescendants) + return; - 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. + // Do not cache positionedDescendants->end() in a local variable, since |positionedDescendants| can be mutated + // as it is walked. We always need to fetch the new end() value dynamically. + for (auto it = positionedDescendants->begin(); it != positionedDescendants->end(); ++it) + layoutPositionedObject(**it, relayoutChildren, fixedPositionObjectsOnly); } void RenderBlock::markPositionedObjectsForLayout() @@ -2147,8 +1520,7 @@ void RenderBlock::markPositionedObjectsForLayout() void RenderBlock::markForPaginationRelayoutIfNeeded() { - ASSERT(!needsLayout()); - if (needsLayout()) + if (needsLayout() || !view().layoutState()->isPaginated()) return; if (view().layoutState()->pageLogicalHeightChanged() || (view().layoutState()->pageLogicalHeight() && view().layoutState()->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset())) @@ -2157,21 +1529,20 @@ void RenderBlock::markForPaginationRelayoutIfNeeded() void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - LayoutPoint adjustedPaintOffset = paintOffset + location(); - - PaintPhase phase = paintInfo.phase; - + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); // Check our region range to make sure we need to be painting in this region. - if (paintInfo.renderRegion && !paintInfo.renderRegion->flowThread()->objectShouldPaintInFlowRegion(this, paintInfo.renderRegion)) + if (namedFlowFragment && !namedFlowFragment->flowThread()->objectShouldFragmentInFlowRegion(this, namedFlowFragment)) return; + LayoutPoint adjustedPaintOffset = paintOffset + location(); + PaintPhase phase = paintInfo.phase; + // 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 isDocumentElementRenderer() check if we fix background painting so that the RenderView // paints the root's background. - if (!isRoot()) { - LayoutRect overflowBox = overflowRectForPaintRejection(paintInfo.renderRegion); + if (!isDocumentElementRenderer()) { + LayoutRect overflowBox = overflowRectForPaintRejection(namedFlowFragment); flipForWritingMode(overflowBox); - overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); overflowBox.moveBy(adjustedPaintOffset); if (!overflowBox.intersects(paintInfo.rect) #if PLATFORM(IOS) @@ -2188,177 +1559,11 @@ void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) popContentsClip(paintInfo, phase, adjustedPaintOffset); // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with - // z-index. We paint after we painted the background/border, so that the scrollbars will + // z-index. We paint after we painted the background/border, so that the scrollbars will // sit above the background/border. - if (hasOverflowClip() && style().visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(*this) && !paintInfo.paintRootBackgroundOnly()) - layer()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), pixelSnappedIntRect(paintInfo.rect)); -} - -void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - if (paintInfo.context->paintingDisabled()) - return; - - const Color& ruleColor = style().visitedDependentColor(CSSPropertyWebkitColumnRuleColor); - bool ruleTransparent = style().columnRuleIsTransparent(); - EBorderStyle ruleStyle = style().columnRuleStyle(); - LayoutUnit ruleThickness = style().columnRuleWidth(); - LayoutUnit colGap = columnGap(); - bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; - if (!renderRule) - return; - - ColumnInfo* colInfo = columnInfo(); - unsigned colCount = columnCount(colInfo); - - bool antialias = shouldAntialiasLines(paintInfo.context); - - if (colInfo->progressionIsInline()) { - bool leftToRight = style().isLeftToRightDirection() ^ colInfo->progressionIsReversed(); - LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth(); - LayoutUnit ruleAdd = logicalLeftOffsetForContent(); - LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth(); - LayoutUnit inlineDirectionSize = colInfo->desiredColumnWidth(); - BoxSide boxSide = isHorizontalWritingMode() - ? leftToRight ? BSLeft : BSRight - : leftToRight ? BSTop : BSBottom; - - for (unsigned i = 0; i < colCount; i++) { - // Move to the next position. - if (leftToRight) { - ruleLogicalLeft += inlineDirectionSize + colGap / 2; - currLogicalLeftOffset += inlineDirectionSize + colGap; - } else { - ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); - currLogicalLeftOffset -= (inlineDirectionSize + colGap); - } - - // Now paint the column rule. - if (i < colCount - 1) { - LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft(); - LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth(); - LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd; - LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness; - IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom); - drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); - } - - ruleLogicalLeft = currLogicalLeftOffset; - } - } else { - bool topToBottom = !style().isFlippedBlocksWritingMode() ^ colInfo->progressionIsReversed(); - LayoutUnit ruleLeft = isHorizontalWritingMode() - ? borderLeft() + paddingLeft() - : colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderAndPaddingBefore() : borderAndPaddingAfter()); - LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness; - LayoutUnit ruleTop = isHorizontalWritingMode() - ? colGap / 2 - colGap - ruleThickness / 2 + (!colInfo->progressionIsReversed() ? borderAndPaddingBefore() : borderAndPaddingAfter()) - : borderStart() + paddingStart(); - LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight(); - LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); - - if (!topToBottom) { - if (isHorizontalWritingMode()) - ruleRect.setY(height() - ruleRect.maxY()); - else - ruleRect.setX(width() - ruleRect.maxX()); - } - - ruleRect.moveBy(paintOffset); - - BoxSide boxSide = isHorizontalWritingMode() - ? topToBottom ? BSTop : BSBottom - : topToBottom ? BSLeft : BSRight; - - LayoutSize step(0, topToBottom ? colInfo->columnHeight() + colGap : -(colInfo->columnHeight() + colGap)); - if (!isHorizontalWritingMode()) - step = step.transposedSize(); - - for (unsigned i = 1; i < colCount; i++) { - ruleRect.move(step); - IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect); - drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); - } - } -} - -LayoutUnit RenderBlock::initialBlockOffsetForPainting() const -{ - ColumnInfo* colInfo = columnInfo(); - LayoutUnit result = 0; - if (!colInfo->progressionIsInline() && colInfo->progressionIsReversed()) { - LayoutRect colRect = columnRectAt(colInfo, 0); - result = isHorizontalWritingMode() ? colRect.y() : colRect.x(); - result -= borderAndPaddingBefore(); - if (style().isFlippedBlocksWritingMode()) - result = -result; - } - return result; -} - -LayoutUnit RenderBlock::blockDeltaForPaintingNextColumn() const -{ - ColumnInfo* colInfo = columnInfo(); - LayoutUnit blockDelta = -colInfo->columnHeight(); - LayoutUnit colGap = columnGap(); - if (!colInfo->progressionIsInline()) { - if (!colInfo->progressionIsReversed()) - blockDelta = colGap; - else - blockDelta -= (colInfo->columnHeight() + colGap); - } - if (style().isFlippedBlocksWritingMode()) - blockDelta = -blockDelta; - return blockDelta; -} - -void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats) -{ - // We need to do multiple passes, breaking up our child painting into strips. - GraphicsContext* context = paintInfo.context; - ColumnInfo* colInfo = columnInfo(); - unsigned colCount = columnCount(colInfo); - if (!colCount) - return; - LayoutUnit colGap = columnGap(); - LayoutUnit currLogicalTopOffset = initialBlockOffsetForPainting(); - LayoutUnit blockDelta = blockDeltaForPaintingNextColumn(); - for (unsigned i = 0; i < colCount; i++) { - // For each rect, we clip to the rect, and then we adjust our coords. - LayoutRect colRect = columnRectAt(colInfo, i); - flipForWritingMode(colRect); - - LayoutUnit logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent(); - LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset); - colRect.moveBy(paintOffset); - PaintInfo info(paintInfo); - info.rect.intersect(pixelSnappedIntRect(colRect)); - - if (!info.rect.isEmpty()) { - GraphicsContextStateSaver stateSaver(*context); - LayoutRect clipRect(colRect); - - if (i < colCount - 1) { - if (isHorizontalWritingMode()) - clipRect.expand(colGap / 2, 0); - else - clipRect.expand(0, colGap / 2); - } - // Each strip pushes a clip, since column boxes are specified as being - // 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)); - - // Adjust our x and y when painting. - LayoutPoint adjustedPaintOffset = paintOffset + offset; - if (paintingFloats) - paintFloats(info, adjustedPaintOffset, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip); - else - paintContents(info, adjustedPaintOffset); - } - currLogicalTopOffset += blockDelta; - } + if ((phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && hasOverflowClip() && layer() + && style().visibility() == VISIBLE && paintInfo.shouldPaintWithinRoot(*this) && !paintInfo.paintRootBackgroundOnly()) + layer()->paintOverflowControls(paintInfo.context(), roundedIntPoint(adjustedPaintOffset), snappedIntRect(paintInfo.rect)); } void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) @@ -2389,16 +1594,16 @@ void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOf void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) { - for (auto child = firstChildBox(); child; child = child->nextSiblingBox()) { + for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) { if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect)) return; } } -bool RenderBlock::paintChild(RenderBox& child, PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) +bool RenderBlock::paintChild(RenderBox& child, PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect, PaintBlockType paintType) { // Check for page-break-before: always, and if it's set, break and bail. - bool checkBeforeAlways = !childrenInline() && (usePrintRect && child.style().pageBreakBefore() == PBALWAYS); + bool checkBeforeAlways = !childrenInline() && (usePrintRect && alwaysPageBreak(child.style().breakBefore())); LayoutUnit absoluteChildY = paintOffset.y() + child.y(); if (checkBeforeAlways && absoluteChildY > paintInfo.rect.y() @@ -2419,11 +1624,15 @@ bool RenderBlock::paintChild(RenderBox& child, PaintInfo& paintInfo, const Layou } LayoutPoint childPoint = flipForWritingModeForChild(&child, paintOffset); - if (!child.hasSelfPaintingLayer() && !child.isFloating()) - child.paint(paintInfoForChild, childPoint); + if (!child.hasSelfPaintingLayer() && !child.isFloating()) { + if (paintType == PaintAsInlineBlock) + child.paintAsInlineBlock(paintInfoForChild, childPoint); + else + child.paint(paintInfoForChild, childPoint); + } // Check for page-break-after: always, and if it's set, break and bail. - bool checkAfterAlways = !childrenInline() && (usePrintRect && child.style().pageBreakAfter() == PBALWAYS); + bool checkAfterAlways = !childrenInline() && (usePrintRect && alwaysPageBreak(child.style().breakAfter())); if (checkAfterAlways && (absoluteChildY + child.height()) > paintInfo.rect.y() && (absoluteChildY + child.height()) < paintInfo.rect.maxY()) { @@ -2434,26 +1643,24 @@ bool RenderBlock::paintChild(RenderBox& child, PaintInfo& paintInfo, const Layou return true; } - void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffset, CaretType type) { // Paint the caret if the FrameSelection says so or if caret browsing is enabled - bool caretBrowsing = frame().settings().caretBrowsingEnabled(); - RenderObject* caretPainter; + RenderBlock* caretPainter; bool isContentEditable; if (type == CursorCaret) { - caretPainter = frame().selection().caretRenderer(); - isContentEditable = frame().selection().hasEditableStyle(); + caretPainter = frame().selection().caretRendererWithoutUpdatingLayout(); + isContentEditable = frame().selection().selection().hasEditableStyle(); } else { - caretPainter = frame().page()->dragCaretController().caretRenderer(); - isContentEditable = frame().page()->dragCaretController().isContentEditable(); + caretPainter = page().dragCaretController().caretRenderer(); + isContentEditable = page().dragCaretController().isContentEditable(); } - if (caretPainter == this && (isContentEditable || caretBrowsing)) { + if (caretPainter == this && (isContentEditable || settings().caretBrowsingEnabled())) { if (type == CursorCaret) - frame().selection().paintCaret(paintInfo.context, paintOffset, paintInfo.rect); + frame().selection().paintCaret(paintInfo.context(), paintOffset, paintInfo.rect); else - frame().page()->dragCaretController().paintDragCaret(&frame(), paintInfo.context, paintOffset, paintInfo.rect); + page().dragCaretController().paintDragCaret(&frame(), paintInfo.context(), paintOffset, paintInfo.rect); } } @@ -2463,28 +1670,27 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs // 1. paint background, borders etc if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style().visibility() == VISIBLE) { - if (hasBoxDecorations()) { + if (hasVisibleBoxDecorations()) { bool didClipToRegion = false; - if (paintInfo.paintContainer && paintInfo.renderRegion && paintInfo.paintContainer->isRenderFlowThread()) { + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); + if (namedFlowFragment && is<RenderNamedFlowThread>(paintInfo.paintContainer)) { // If this box goes beyond the current region, then make sure not to overflow the region. // This (overflowing region X altough also fragmented to region X+1) could happen when one of this box's children // overflows region X and is an unsplittable element (like an image). // The same applies for a box overflowing the top of region X when that box is also fragmented in region X-1. - paintInfo.context->save(); + paintInfo.context().save(); didClipToRegion = true; - paintInfo.context->clip(toRenderFlowThread(paintInfo.paintContainer)->decorationsClipRectForBoxInRegion(*this, *paintInfo.renderRegion)); + paintInfo.context().clip(downcast<RenderNamedFlowThread>(*paintInfo.paintContainer).decorationsClipRectForBoxInNamedFlowFragment(*this, *namedFlowFragment)); } paintBoxDecorations(paintInfo, paintOffset); if (didClipToRegion) - paintInfo.context->restore(); + paintInfo.context().restore(); } - if (hasColumns() && !paintInfo.paintRootBackgroundOnly()) - paintColumnRules(paintInfo, paintOffset); } if (paintPhase == PaintPhaseMask && style().visibility() == VISIBLE) { @@ -2492,36 +1698,42 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs return; } - // We're done. We don't bother painting any children. - if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly()) + if (paintPhase == PaintPhaseClippingMask && style().visibility() == VISIBLE) { + paintClippingMask(paintInfo, paintOffset); + return; + } + + // If just painting the root background, then return. + if (paintInfo.paintRootBackgroundOnly()) return; // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div). LayoutPoint scrolledOffset = paintOffset; - if (hasOverflowClip()) - scrolledOffset.move(-scrolledContentOffset()); + scrolledOffset.moveBy(-scrollPosition()); + // Column rules need to account for scrolling and clipping. + // FIXME: Clipping of column rules does not work. We will need a separate paint phase for column rules I suspect in order to get + // clipping correct (since it has to paint as background but is still considered "contents"). + if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style().visibility() == VISIBLE) + paintColumnRules(paintInfo, scrolledOffset); + + // Done with backgrounds, borders and column rules. + if (paintPhase == PaintPhaseBlockBackground) + return; + // 2. paint contents - if (paintPhase != PaintPhaseSelfOutline) { - if (hasColumns()) - paintColumnContents(paintInfo, scrolledOffset); - else - paintContents(paintInfo, scrolledOffset); - } + if (paintPhase != PaintPhaseSelfOutline) + paintContents(paintInfo, scrolledOffset); // 3. paint selection // FIXME: Make this work with multi column layouts. For now don't fill gaps. bool isPrinting = document().printing(); - if (!isPrinting && !hasColumns()) + if (!isPrinting) paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks. // 4. paint floats. - if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) { - if (hasColumns()) - paintColumnContents(paintInfo, scrolledOffset, true); - else - paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); - } + if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) + paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); // 5. paint outline. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style().visibility() == VISIBLE) @@ -2531,11 +1743,11 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { RenderInline* inlineCont = inlineElementContinuation(); if (inlineCont && inlineCont->hasOutline() && inlineCont->style().visibility() == VISIBLE) { - RenderInline* inlineRenderer = toRenderInline(inlineCont->element()->renderer()); - RenderBlock* cb = containingBlock(); + RenderInline* inlineRenderer = downcast<RenderInline>(inlineCont->element()->renderer()); + RenderBlock* containingBlock = this->containingBlock(); bool inlineEnclosedInSelfPaintingLayer = false; - for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = box->parent()->enclosingBoxModelObject()) { + for (RenderBoxModelObject* box = inlineRenderer; box != containingBlock; box = &box->parent()->enclosingBoxModelObject()) { if (box->hasSelfPaintingLayer()) { inlineEnclosedInSelfPaintingLayer = true; break; @@ -2546,7 +1758,7 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being // in the same layer. if (!inlineEnclosedInSelfPaintingLayer && !hasLayer()) - cb->addContinuationWithOutline(inlineRenderer); + containingBlock->addContinuationWithOutline(inlineRenderer); else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer())) inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location()); } @@ -2565,24 +1777,24 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs RenderInline* RenderBlock::inlineElementContinuation() const { RenderBoxModelObject* continuation = this->continuation(); - return continuation && continuation->isRenderInline() ? toRenderInline(continuation) : 0; + return is<RenderInline>(continuation) ? downcast<RenderInline>(continuation) : nullptr; } RenderBlock* RenderBlock::blockElementContinuation() const { RenderBoxModelObject* currentContinuation = continuation(); if (!currentContinuation || currentContinuation->isInline()) - return 0; - RenderBlock* nextContinuation = toRenderBlock(currentContinuation); - if (nextContinuation->isAnonymousBlock()) - return nextContinuation->blockElementContinuation(); - return nextContinuation; + return nullptr; + RenderBlock& nextContinuation = downcast<RenderBlock>(*currentContinuation); + if (nextContinuation.isAnonymousBlock()) + return nextContinuation.blockElementContinuation(); + return &nextContinuation; } static ContinuationOutlineTableMap* continuationOutlineTable() { - DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ()); - return &table; + static NeverDestroyed<ContinuationOutlineTableMap> table; + return &table.get(); } void RenderBlock::addContinuationWithOutline(RenderInline* flow) @@ -2595,7 +1807,7 @@ void RenderBlock::addContinuationWithOutline(RenderInline* flow) ListHashSet<RenderInline*>* continuations = table->get(this); if (!continuations) { continuations = new ListHashSet<RenderInline*>; - table->set(this, adoptPtr(continuations)); + table->set(this, std::unique_ptr<ListHashSet<RenderInline*>>(continuations)); } continuations->add(flow); @@ -2620,7 +1832,7 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& if (table->isEmpty()) return; - OwnPtr<ListHashSet<RenderInline*>> continuations = table->take(this); + std::unique_ptr<ListHashSet<RenderInline*>> continuations = table->take(this); if (!continuations) return; @@ -2640,6 +1852,9 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& bool RenderBlock::shouldPaintSelectionGaps() const { + if (settings().selectionPaintingWithoutSelectionGapsEnabled()) + return false; + return selectionState() != SelectionNone && style().visibility() == VISIBLE && isSelectionRoot(); } @@ -2653,15 +1868,15 @@ bool RenderBlock::isSelectionRoot() const if (isTable()) return false; - if (isBody() || isRoot() || hasOverflowClip() + if (isBody() || isDocumentElementRenderer() || hasOverflowClip() || isPositioned() || isFloating() || isTableCell() || isInlineBlockOrInlineTable() || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot() - || isRenderFlowThread()) + || isRenderFlowThread() || style().columnSpan() == ColumnSpanAll) return true; - if (view().selectionStart()) { - Node* startElement = view().selectionStart()->node(); + if (view().selectionUnsplitStart()) { + Node* startElement = view().selectionUnsplitStart()->node(); if (startElement && startElement->rootEditableElement() == element()) return true; } @@ -2676,12 +1891,8 @@ GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* if (!shouldPaintSelectionGaps()) return GapRects(); - TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); - mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip | UseTransforms); - LayoutPoint offsetFromRepaintContainer = roundedLayoutPoint(transformState.mappedPoint()); - - if (hasOverflowClip()) - offsetFromRepaintContainer -= scrolledContentOffset(); + FloatPoint containerPoint = localToContainerPoint(FloatPoint(), repaintContainer, UseTransforms); + LayoutPoint offsetFromRepaintContainer(containerPoint - toFloatSize(scrollPosition())); LogicalSelectionOffsetCaches cache(*this); LayoutUnit lastTop = 0; @@ -2699,7 +1910,7 @@ void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintO LayoutUnit lastTop = 0; LayoutUnit lastLeft = logicalLeftSelectionOffset(*this, lastTop, cache); LayoutUnit lastRight = logicalRightSelectionOffset(*this, lastTop, cache); - GraphicsContextStateSaver stateSaver(*paintInfo.context); + GraphicsContextStateSaver stateSaver(paintInfo.context()); LayoutRect gapRectsBounds = selectionGaps(*this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, cache, &paintInfo); if (!gapRectsBounds.isEmpty()) { @@ -2709,8 +1920,8 @@ void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintO LayoutRect localBounds(gapRectsBounds); flipForWritingMode(localBounds); gapRectsBounds = localToContainerQuad(FloatRect(localBounds), &layer->renderer()).enclosingBoundingBox(); - if (layer->renderer().hasOverflowClip()) - gapRectsBounds.move(layer->renderBox()->scrolledContentOffset()); + if (layer->renderer().isBox()) + gapRectsBounds.moveBy(layer->renderBox()->scrollPosition()); } layer->addBlockSelectionGapsBounds(gapRectsBounds); } @@ -2730,7 +1941,7 @@ static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoi TrackedRendererListHashSet::const_iterator end = positionedObjects->end(); for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) { RenderBox* r = *it; - paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height())); + paintInfo->context().clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height())); } } @@ -2767,9 +1978,10 @@ 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. - for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock()) + if (isBody() || isDocumentElementRenderer()) { // The <body> must make sure to examine its containingBlock's positioned objects. + for (RenderBlock* cb = containingBlock(); cb && !is<RenderView>(*cb); cb = cb->containingBlock()) clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes. + } clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock); } @@ -2779,21 +1991,28 @@ GapRects RenderBlock::selectionGaps(RenderBlock& rootBlock, const LayoutPoint& r if (!isRenderBlockFlow()) // FIXME: Make multi-column selection gap filling work someday. return result; - if (hasColumns() || hasTransform() || style().columnSpan()) { + if (hasTransform() || style().columnSpan() == ColumnSpanAll || isInFlowRenderFlowThread()) { // FIXME: We should learn how to gap fill multiple columns and transforms eventually. lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache); lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache); return result; } + + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); + if (paintInfo && namedFlowFragment && is<RenderFlowThread>(*paintInfo->paintContainer)) { + // Make sure the current object is actually flowed into the region being painted. + if (!downcast<RenderFlowThread>(*paintInfo->paintContainer).objectShouldFragmentInFlowRegion(this, namedFlowFragment)) + return result; + } if (childrenInline()) result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); else result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); - // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. - if (&rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) { + // Fill the vertical gap all the way to the bottom of our block if the selection extends past our block. + if (&rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd) && !isRubyBase() && !isRubyText()) { result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, logicalHeight(), cache, paintInfo)); } @@ -2812,7 +2031,7 @@ GapRects RenderBlock::blockSelectionGaps(RenderBlock& rootBlock, const LayoutPoi { GapRects result; - // Go ahead and jump right to the first block child that contains some selected objects. + // Jump right to the first block child that contains some selected objects. RenderBox* curr; for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { } @@ -2838,7 +2057,7 @@ GapRects RenderBlock::blockSelectionGaps(RenderBlock& rootBlock, const LayoutPoi } bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this. - bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); + bool fillBlockGaps = (paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone)) && !isRubyBase() && !isRubyText(); if (fillBlockGaps) { // We need to fill the vertical gap above this object. if (childState == SelectionEnd || childState == SelectionInside) { @@ -2867,9 +2086,9 @@ GapRects RenderBlock::blockSelectionGaps(RenderBlock& rootBlock, const LayoutPoi lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom(); lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom(), cache); lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom(), cache); - } else if (childState != SelectionNone) { - // We must be a block that has some selected object inside it. Go ahead and recur. - result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), + } else if (childState != SelectionNone && is<RenderBlock>(*curr)) { + // We must be a block that has some selected object inside it, so recur. + result.unite(downcast<RenderBlock>(*curr).selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), lastLogicalTop, lastLogicalLeft, lastLogicalRight, childCache, paintInfo)); } } @@ -2893,16 +2112,16 @@ LayoutRect RenderBlock::blockSelectionGap(RenderBlock& rootBlock, const LayoutPo LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight)); if (paintInfo) - paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selectionBackgroundColor(), style().colorSpace()); + paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selectionBackgroundColor()); return gapRect; } LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) + RenderBoxModelObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; LayoutUnit rootBlockLogicalLeft = std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); - LayoutUnit rootBlockLogicalRight = std::min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalLeft), + LayoutUnit rootBlockLogicalRight = std::min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalLeft, std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; if (rootBlockLogicalWidth <= 0) @@ -2910,15 +2129,15 @@ LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock& rootBlock, const La LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); if (paintInfo) - paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor(), selObj->style().colorSpace()); + paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor()); return gapRect; } LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) + RenderBoxModelObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; - LayoutUnit rootBlockLogicalLeft = std::max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + floorToInt(logicalRight), + LayoutUnit rootBlockLogicalLeft = std::max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalRight, std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); LayoutUnit rootBlockLogicalRight = std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; @@ -2927,7 +2146,7 @@ LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock& rootBlock, const L LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); if (paintInfo) - paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor(), selObj->style().colorSpace()); + paintInfo->context().fillRect(snapRectToDevicePixels(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor()); return gapRect; } @@ -2944,7 +2163,7 @@ void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) { - LayoutUnit logicalLeft = logicalLeftOffsetForLine(position, false); + LayoutUnit logicalLeft = logicalLeftOffsetForLine(position, DoNotIndentText); if (logicalLeft == logicalLeftOffsetForContent()) { if (&rootBlock != this) // The border can potentially be further extended by our containingBlock(). return cache.containingBlockInfo(*this).logicalLeftSelectionOffset(rootBlock, position + logicalTop()); @@ -2959,6 +2178,8 @@ LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock& rootBlock, Layou ASSERT(currentCache); auto info = currentCache->containingBlockInfo(*cb); cb = info.block(); + if (!cb) + break; currentCache = info.cache(); } return logicalLeft; @@ -2966,7 +2187,7 @@ LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock& rootBlock, Layou LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) { - LayoutUnit logicalRight = logicalRightOffsetForLine(position, false); + LayoutUnit logicalRight = logicalRightOffsetForLine(position, DoNotIndentText); if (logicalRight == logicalRightOffsetForContent()) { if (&rootBlock != this) // The border can potentially be further extended by our containingBlock(). return cache.containingBlockInfo(*this).logicalRightSelectionOffset(rootBlock, position + logicalTop()); @@ -2981,179 +2202,90 @@ LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock& rootBlock, Layo ASSERT(currentCache); auto info = currentCache->containingBlockInfo(*cb); cb = info.block(); + if (!cb) + break; currentCache = info.cache(); } return logicalRight; } -RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot(LayoutSize& offset) const -{ - if (isSelectionRoot()) - return 0; - - const RenderElement* object = this; - RenderObject* sibling; - do { - sibling = object->previousSibling(); - while (sibling && (!sibling->isRenderBlock() || toRenderBlock(sibling)->isSelectionRoot())) - sibling = sibling->previousSibling(); - - offset -= LayoutSize(toRenderBlock(object)->logicalLeft(), toRenderBlock(object)->logicalTop()); - object = object->parent(); - } while (!sibling && object && object->isRenderBlock() && !toRenderBlock(object)->isSelectionRoot()); - - if (!sibling) - return 0; - - RenderBlock* beforeBlock = toRenderBlock(sibling); - - offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); - - RenderObject* child = beforeBlock->lastChild(); - while (child && child->isRenderBlock()) { - beforeBlock = toRenderBlock(child); - offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); - child = beforeBlock->lastChild(); - } - return beforeBlock; -} - -void RenderBlock::insertIntoTrackedRendererMaps(RenderBox& descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) -{ - if (!descendantsMap) { - descendantsMap = new TrackedDescendantsMap; - containerMap = new TrackedContainerMap; - } - - TrackedRendererListHashSet* descendantSet = descendantsMap->get(this); - if (!descendantSet) { - descendantSet = new TrackedRendererListHashSet; - descendantsMap->set(this, adoptPtr(descendantSet)); - } - bool added = descendantSet->add(&descendant).isNewEntry; - if (!added) { - ASSERT(containerMap->get(&descendant)); - ASSERT(containerMap->get(&descendant)->contains(this)); - return; - } - - HashSet<RenderBlock*>* containerSet = containerMap->get(&descendant); - if (!containerSet) { - containerSet = new HashSet<RenderBlock*>; - containerMap->set(&descendant, adoptPtr(containerSet)); - } - ASSERT(!containerSet->contains(this)); - containerSet->add(this); -} - -void RenderBlock::removeFromTrackedRendererMaps(RenderBox& descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) -{ - if (!descendantsMap) - return; - - OwnPtr<HashSet<RenderBlock*>> containerSet = containerMap->take(&descendant); - if (!containerSet) - return; - - for (auto it = containerSet->begin(), end = containerSet->end(); it != end; ++it) { - RenderBlock* container = *it; - - // FIXME: Disabling this assert temporarily until we fix the layout - // bugs associated with positioned objects not properly cleared from - // their ancestor chain before being moved. See webkit bug 93766. - // ASSERT(descendant->isDescendantOf(container)); - - TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container); - ASSERT(descendantsMapIterator != descendantsMap->end()); - if (descendantsMapIterator == descendantsMap->end()) - continue; - TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get(); - ASSERT(descendantSet->contains(&descendant)); - descendantSet->remove(&descendant); - if (descendantSet->isEmpty()) - descendantsMap->remove(descendantsMapIterator); - } -} - TrackedRendererListHashSet* RenderBlock::positionedObjects() const { - if (gPositionedDescendantsMap) - return gPositionedDescendantsMap->get(this); - return 0; + return positionedDescendantsMap().positionedRenderers(*this); } -void RenderBlock::insertPositionedObject(RenderBox& o) +void RenderBlock::insertPositionedObject(RenderBox& positioned) { ASSERT(!isAnonymousBlock()); - - if (o.isRenderFlowThread()) + if (positioned.isRenderFlowThread()) return; - - insertIntoTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); + // FIXME: Find out if we can do this as part of positioned.setChildNeedsLayout(MarkOnlyThis) + if (positioned.needsLayout()) { + // We should turn this bit on only while in layout. + ASSERT(posChildNeedsLayout() || view().frameView().isInLayout()); + setPosChildNeedsLayoutBit(true); + } + positionedDescendantsMap().addDescendant(*this, positioned, isRenderView() ? PositionedDescendantsMap::MoveDescendantToEnd::Yes + : PositionedDescendantsMap::MoveDescendantToEnd::No); } -void RenderBlock::removePositionedObject(RenderBox& o) +void RenderBlock::removePositionedObject(const RenderBox& rendererToRemove) { - removeFromTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); + positionedDescendantsMap().removeDescendant(rendererToRemove); } -void RenderBlock::removePositionedObjects(RenderBlock* o, ContainingBlockState containingBlockState) +void RenderBlock::removePositionedObjects(const RenderBlock* newContainingBlockCandidate, ContainingBlockState containingBlockState) { - TrackedRendererListHashSet* positionedDescendants = positionedObjects(); + auto* positionedDescendants = positionedObjects(); if (!positionedDescendants) return; - Vector<RenderBox*, 16> deadObjects; - - for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) { - RenderBox* r = *it; - if (!o || r->isDescendantOf(o)) { - if (containingBlockState == NewContainingBlock) - r->setChildNeedsLayout(MarkOnlyThis); - - // It is parent blocks job to add positioned child to positioned objects list of its containing block - // Parent layout needs to be invalidated to ensure this happens. - RenderElement* p = r->parent(); - while (p && !p->isRenderBlock()) - p = p->parent(); - if (p) - p->setChildNeedsLayout(); - - deadObjects.append(r); - } + Vector<RenderBox*, 16> renderersToRemove; + for (auto* renderer : *positionedDescendants) { + if (newContainingBlockCandidate && !renderer->isDescendantOf(newContainingBlockCandidate)) + continue; + renderersToRemove.append(renderer); + if (containingBlockState == NewContainingBlock) + renderer->setChildNeedsLayout(MarkOnlyThis); + // It is the parent block's job to add positioned children to positioned objects list of its containing block. + // Dirty the parent to ensure this happens. + auto* parent = renderer->parent(); + while (parent && !parent->isRenderBlock()) + parent = parent->parent(); + if (parent) + parent->setChildNeedsLayout(); } - - for (unsigned i = 0; i < deadObjects.size(); i++) - removePositionedObject(*deadObjects.at(i)); + for (auto* renderer : renderersToRemove) + removePositionedObject(*renderer); } void RenderBlock::addPercentHeightDescendant(RenderBox& descendant) { - insertIntoTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); + insertIntoTrackedRendererMaps(*this, descendant); } void RenderBlock::removePercentHeightDescendant(RenderBox& descendant) { - removeFromTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); + removeFromTrackedRendererMaps(descendant); } TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const { - return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; + return percentHeightDescendantsMap ? percentHeightDescendantsMap->get(this) : nullptr; } bool RenderBlock::hasPercentHeightContainerMap() { - return gPercentHeightContainerMap; + return percentHeightContainerMap; } bool RenderBlock::hasPercentHeightDescendant(RenderBox& descendant) { - // We don't null check gPercentHeightContainerMap since the caller + // We don't null check percentHeightContainerMap since the caller // already ensures this and we need to call this function on every // descendant in clearPercentHeightDescendantsFrom(). - ASSERT(gPercentHeightContainerMap); - return gPercentHeightContainerMap->contains(&descendant); + ASSERT(percentHeightContainerMap); + return percentHeightContainerMap->contains(&descendant); } void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox& descendant) @@ -3172,12 +2304,12 @@ void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox& descendant) void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox& parent) { - ASSERT(gPercentHeightContainerMap); - for (RenderObject* curr = parent.firstChild(); curr; curr = curr->nextInPreOrder(&parent)) { - if (!curr->isBox()) + ASSERT(percentHeightContainerMap); + for (RenderObject* child = parent.firstChild(); child; child = child->nextInPreOrder(&parent)) { + if (!is<RenderBox>(*child)) continue; - RenderBox& box = toRenderBox(*curr); + auto& box = downcast<RenderBox>(*child); if (!hasPercentHeightDescendant(box)) continue; @@ -3188,7 +2320,7 @@ void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox& parent) LayoutUnit RenderBlock::textIndentOffset() const { LayoutUnit cw = 0; - if (style().textIndent().isPercent()) + if (style().textIndent().isPercentOrCalculated()) cw = containingBlock()->availableLogicalWidth(); return minimumValueForLength(style().textIndent(), cw); } @@ -3196,6 +2328,8 @@ LayoutUnit RenderBlock::textIndentOffset() const LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region) const { LayoutUnit logicalLeftOffset = style().isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); + if (shouldPlaceBlockDirectionScrollbarOnLeft()) + logicalLeftOffset += verticalScrollbarWidth(); if (!region) return logicalLeftOffset; LayoutRect boxRect = borderBoxRectInRegion(region); @@ -3205,6 +2339,8 @@ LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region) const LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region) const { LayoutUnit logicalRightOffset = style().isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); + if (shouldPlaceBlockDirectionScrollbarOnLeft()) + logicalRightOffset += verticalScrollbarWidth(); logicalRightOffset += availableLogicalWidth(); if (!region) return logicalRightOffset; @@ -3232,7 +2368,7 @@ LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloa 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(); + float maxCharWidth = lineGrid->style().fontCascade().primaryFont().maxCharWidth(); if (!maxCharWidth) return left; @@ -3272,7 +2408,7 @@ LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFlo 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(); + float maxCharWidth = lineGrid->style().fontCascade().primaryFont().maxCharWidth(); if (!maxCharWidth) return right; @@ -3295,7 +2431,7 @@ LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFlo bool RenderBlock::avoidsFloats() const { // Floats can't intrude into our box if we have a non-auto column count or width. - return RenderBox::avoidsFloats() || !style().hasAutoColumnCount() || !style().hasAutoColumnWidth(); + return RenderBox::avoidsFloats() || style().hasFlowFrom(); } bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset) @@ -3321,6 +2457,13 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu LayoutPoint adjustedLocation(accumulatedOffset + location()); LayoutSize localOffset = toLayoutSize(adjustedLocation); + RenderFlowThread* flowThread = flowThreadContainingBlock(); + RenderNamedFlowFragment* namedFlowFragment = flowThread ? downcast<RenderNamedFlowFragment>(flowThread->currentRegion()) : nullptr; + // If we are now searching inside a region, make sure this element + // is being fragmented into this region. + if (namedFlowFragment && !flowThread->objectShouldFragmentInFlowRegion(this, namedFlowFragment)) + return false; + if (!isRenderView()) { // Check if we need to do anything at all. LayoutRect overflowBox = visualOverflowRect(); @@ -3337,25 +2480,63 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu return true; } + if (style().clipPath()) { + switch (style().clipPath()->type()) { + case ClipPathOperation::Shape: { + auto& clipPath = downcast<ShapeClipPathOperation>(*style().clipPath()); + + LayoutRect referenceBoxRect; + switch (clipPath.referenceBox()) { + case CSSBoxType::MarginBox: + referenceBoxRect = marginBoxRect(); + break; + case CSSBoxType::BorderBox: + referenceBoxRect = borderBoxRect(); + break; + case CSSBoxType::PaddingBox: + referenceBoxRect = paddingBoxRect(); + break; + case CSSBoxType::ContentBox: + referenceBoxRect = contentBoxRect(); + break; + case CSSBoxType::BoxMissing: + case CSSBoxType::Fill: + case CSSBoxType::Stroke: + case CSSBoxType::ViewBox: + referenceBoxRect = borderBoxRect(); + } + if (!clipPath.pathForReferenceRect(referenceBoxRect).contains(locationInContainer.point() - localOffset, clipPath.windRule())) + return false; + break; + } + case ClipPathOperation::Reference: { + const auto& referenceClipPathOperation = downcast<ReferenceClipPathOperation>(*style().clipPath()); + auto* element = document().getElementById(referenceClipPathOperation.fragment()); + if (!element || !element->renderer()) + break; + if (!is<SVGClipPathElement>(*element)) + break; + auto& clipper = downcast<RenderSVGResourceClipper>(*element->renderer()); + if (!clipper.hitTestClipContent(FloatRect(borderBoxRect()), FloatPoint(locationInContainer.point() - localOffset))) + return false; + break; + } + case ClipPathOperation::Box: + 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 || (hasControlClip() ? locationInContainer.intersects(controlClipRect(adjustedLocation)) : locationInContainer.intersects(overflowClipRect(adjustedLocation, namedFlowFragment, IncludeOverlayScrollbarSize))); if (checkChildren) { // Hit test descendants first. - LayoutSize scrolledOffset(localOffset); - if (hasOverflowClip()) - scrolledOffset -= scrolledContentOffset(); - - // Hit test contents if we don't have columns. - if (!hasColumns()) { - if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { - updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); - return true; - } - if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset))) - return true; - } else if (hitTestColumns(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { + LayoutSize scrolledOffset(localOffset - toLayoutSize(scrollPosition())); + + if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset))) + return true; + if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); return true; } @@ -3365,7 +2546,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu if (!isRenderView() && 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; } @@ -3383,98 +2564,6 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu return false; } -class ColumnRectIterator { - WTF_MAKE_NONCOPYABLE(ColumnRectIterator); -public: - ColumnRectIterator(const RenderBlock& block) - : m_block(block) - , m_colInfo(block.columnInfo()) - , m_direction(m_block.style().isFlippedBlocksWritingMode() ? 1 : -1) - , m_isHorizontal(block.isHorizontalWritingMode()) - , m_logicalLeft(block.logicalLeftOffsetForContent()) - { - int colCount = m_colInfo->columnCount(); - m_colIndex = colCount - 1; - - m_currLogicalTopOffset = m_block.initialBlockOffsetForPainting(); - m_currLogicalTopOffset = colCount * m_block.blockDeltaForPaintingNextColumn(); - - update(); - } - - void advance() - { - ASSERT(hasMore()); - m_colIndex--; - update(); - } - - LayoutRect columnRect() const { return m_colRect; } - bool hasMore() const { return m_colIndex >= 0; } - - void adjust(LayoutSize& offset) const - { - LayoutUnit currLogicalLeftOffset = (m_isHorizontal ? m_colRect.x() : m_colRect.y()) - m_logicalLeft; - offset += m_isHorizontal ? LayoutSize(currLogicalLeftOffset, m_currLogicalTopOffset) : LayoutSize(m_currLogicalTopOffset, currLogicalLeftOffset); - } - -private: - void update() - { - if (m_colIndex < 0) - return; - m_colRect = m_block.columnRectAt(const_cast<ColumnInfo*>(m_colInfo), m_colIndex); - m_block.flipForWritingMode(m_colRect); - m_currLogicalTopOffset -= m_block.blockDeltaForPaintingNextColumn(); - } - - const RenderBlock& m_block; - const ColumnInfo* const m_colInfo; - const int m_direction; - const bool m_isHorizontal; - const LayoutUnit m_logicalLeft; - int m_colIndex; - LayoutUnit m_currLogicalTopOffset; - LayoutRect m_colRect; -}; - -bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) -{ - // We need to do multiple passes, breaking up our hit testing into strips. - if (!hasColumns()) - return false; - - for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) { - LayoutRect hitRect = locationInContainer.boundingBox(); - LayoutRect colRect = it.columnRect(); - colRect.moveBy(accumulatedOffset); - if (locationInContainer.intersects(colRect)) { - // The point is inside this column. - // Adjust accumulatedOffset to change where we hit test. - LayoutSize offset; - it.adjust(offset); - LayoutPoint finalLocation = accumulatedOffset + offset; - if (!result.isRectBasedTest() || colRect.contains(hitRect)) - return hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction) || (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, finalLocation)); - - hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction); - } - } - - return false; -} - -void RenderBlock::adjustForColumnRect(LayoutSize& offset, const LayoutPoint& locationInContainer) const -{ - for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) { - LayoutRect colRect = it.columnRect(); - if (colRect.contains(locationInContainer)) { - it.adjust(offset); - return; - } - } -} - bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { if (childrenInline() && !isTable()) @@ -3484,7 +2573,7 @@ bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& HitTestAction childHitTest = hitTestAction; if (hitTestAction == HitTestChildBlockBackgrounds) childHitTest = HitTestChildBlockBackground; - for (auto child = lastChildBox(); child; child = child->previousSiblingBox()) { + for (auto* child = lastChildBox(); child; child = child->previousSiblingBox()) { LayoutPoint childPoint = flipForWritingModeForChild(child, accumulatedOffset); if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest)) return true; @@ -3516,7 +2605,7 @@ VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock& parent, // If this is an anonymous renderer, we just recur normally Element* childElement= child.nonPseudoElement(); if (!childElement) - return child.positionForPoint(pointInChildCoordinates); + return child.positionForPoint(pointInChildCoordinates, nullptr); // Otherwise, first make sure that the editability of the parent and child agree. // If they don't agree, then we return a visible position just before or after the child @@ -3526,26 +2615,17 @@ VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock& parent, // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal if (isEditingBoundary(ancestor, child)) - return child.positionForPoint(pointInChildCoordinates); - -#if PLATFORM(IOS) - // On iOS we want to constrain VisiblePositions to the editable region closest to the input position, so - // we will allow descent from non-edtiable to editable content. - // FIXME: This constraining must be done at a higher level once we implement contentEditable. For now, if something - // is editable, the whole document will be. - if (childElement->isContentEditable() && !ancestor->element()->isContentEditable()) - return child.positionForPoint(pointInChildCoordinates); -#endif + return child.positionForPoint(pointInChildCoordinates, nullptr); // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child LayoutUnit childMiddle = parent.logicalWidthForChild(child) / 2; LayoutUnit logicalLeft = parent.isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y(); if (logicalLeft < childMiddle) - return ancestor->createVisiblePosition(childElement->nodeIndex(), DOWNSTREAM); - return ancestor->createVisiblePosition(childElement->nodeIndex() + 1, UPSTREAM); + return ancestor->createVisiblePosition(childElement->computeNodeIndex(), DOWNSTREAM); + return ancestor->createVisiblePosition(childElement->computeNodeIndex() + 1, UPSTREAM); } -VisiblePosition RenderBlock::positionForPointWithInlineChildren(const LayoutPoint&) +VisiblePosition RenderBlock::positionForPointWithInlineChildren(const LayoutPoint&, const RenderRegion*) { ASSERT_NOT_REACHED(); return VisiblePosition(); @@ -3553,24 +2633,24 @@ VisiblePosition RenderBlock::positionForPointWithInlineChildren(const LayoutPoin static inline bool isChildHitTestCandidate(const RenderBox& box) { - return box.height() && box.style().visibility() == VISIBLE && !box.isFloatingOrOutOfFlowPositioned(); + return box.height() && box.style().visibility() == VISIBLE && !box.isFloatingOrOutOfFlowPositioned() && !box.isInFlowRenderFlowThread(); } // Valid candidates in a FlowThread must be rendered by the region. -static inline bool isChildHitTestCandidate(const RenderBox& box, RenderRegion* region, const LayoutPoint& point) +static inline bool isChildHitTestCandidate(const RenderBox& box, const RenderRegion* region, const LayoutPoint& point) { if (!isChildHitTestCandidate(box)) return false; if (!region) return true; - const RenderBlock& block = box.isRenderBlock() ? toRenderBlock(box) : *box.containingBlock(); + const RenderBlock& block = is<RenderBlock>(box) ? downcast<RenderBlock>(box) : *box.containingBlock(); return block.regionAtBlockOffset(point.y()) == region; } -VisiblePosition RenderBlock::positionForPoint(const LayoutPoint& point) +VisiblePosition RenderBlock::positionForPoint(const LayoutPoint& point, const RenderRegion* region) { if (isTable()) - return RenderBox::positionForPoint(point); + return RenderBox::positionForPoint(point, region); if (isReplaced()) { // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode. @@ -3590,10 +2670,13 @@ VisiblePosition RenderBlock::positionForPoint(const LayoutPoint& point) pointInLogicalContents = pointInLogicalContents.transposedPoint(); if (childrenInline()) - return positionForPointWithInlineChildren(pointInLogicalContents); + return positionForPointWithInlineChildren(pointInLogicalContents, region); - RenderRegion* region = regionAtBlockOffset(pointInLogicalContents.y()); RenderBox* lastCandidateBox = lastChildBox(); + + if (!region) + region = regionAtBlockOffset(pointInLogicalContents.y()); + while (lastCandidateBox && !isChildHitTestCandidate(*lastCandidateBox, region, pointInLogicalContents)) lastCandidateBox = lastCandidateBox->previousSiblingBox(); @@ -3603,7 +2686,7 @@ VisiblePosition RenderBlock::positionForPoint(const LayoutPoint& point) || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopForChild(*lastCandidateBox))) return positionForPointRespectingEditingBoundaries(*this, *lastCandidateBox, pointInContents); - for (auto childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { + for (auto* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { if (!isChildHitTestCandidate(*childBox, region, pointInLogicalContents)) continue; LayoutUnit childLogicalBottom = logicalTopForChild(*childBox) + logicalHeightForChild(*childBox); @@ -3615,428 +2698,25 @@ VisiblePosition RenderBlock::positionForPoint(const LayoutPoint& point) } // We only get here if there are no hit test candidate children below the click. - return RenderBox::positionForPoint(point); + return RenderBox::positionForPoint(point, region); } void RenderBlock::offsetForContents(LayoutPoint& offset) const { offset = flipForWritingMode(offset); - - if (hasOverflowClip()) - offset += scrolledContentOffset(); - - if (hasColumns()) - adjustPointToColumnContents(offset); - + offset += toLayoutSize(scrollPosition()); offset = flipForWritingMode(offset); } -LayoutUnit RenderBlock::availableLogicalWidth() const -{ - // If we have multiple columns, then the available logical width is reduced to our column width. - if (hasColumns()) - return computedColumnWidth(); - return RenderBox::availableLogicalWidth(); -} - -int RenderBlock::columnGap() const -{ - if (style().hasNormalColumnGap()) - return style().fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. - return static_cast<int>(style().columnGap()); -} - -void RenderBlock::computeColumnCountAndWidth() -{ - // Calculate our column width and column count. - // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744 - unsigned desiredColumnCount = 1; - LayoutUnit desiredColumnWidth = contentLogicalWidth(); - - // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination. - if (document().paginated() || (style().hasAutoColumnCount() && style().hasAutoColumnWidth()) || !style().hasInlineColumnAxis()) { - setComputedColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); - return; - } - - LayoutUnit availWidth = desiredColumnWidth; - LayoutUnit colGap = columnGap(); - LayoutUnit colWidth = std::max<LayoutUnit>(1, LayoutUnit(style().columnWidth())); - int colCount = std::max<int>(1, style().columnCount()); - - if (style().hasAutoColumnWidth() && !style().hasAutoColumnCount()) { - desiredColumnCount = colCount; - desiredColumnWidth = std::max<LayoutUnit>(0, (availWidth - ((desiredColumnCount - 1) * colGap)) / desiredColumnCount); - } else if (!style().hasAutoColumnWidth() && style().hasAutoColumnCount()) { - desiredColumnCount = std::max<LayoutUnit>(1, (availWidth + colGap) / (colWidth + colGap)); - desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap; - } else { - desiredColumnCount = std::max<LayoutUnit>(std::min<LayoutUnit>(colCount, (availWidth + colGap) / (colWidth + colGap)), 1); - desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap; - } - setComputedColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); -} - -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()); - - return firstChild() - && (desiredColumnCount != 1 || !style().hasAutoColumnWidth() || !style().hasInlineColumnAxis() || isPaginated) - && !firstChild()->isAnonymousColumnsBlock() - && !firstChild()->isAnonymousColumnSpanBlock(); -} - -void RenderBlock::setComputedColumnCountAndWidth(int count, LayoutUnit width) -{ - bool destroyColumns = !requiresColumns(count); - if (destroyColumns) { - if (hasColumns()) { - gColumnInfoMap->take(this); - setHasColumns(false); - } - } else { - ColumnInfo* info; - if (hasColumns()) - info = gColumnInfoMap->get(this); - else { - if (!gColumnInfoMap) - gColumnInfoMap = new ColumnInfoMap; - info = new ColumnInfo; - gColumnInfoMap->add(this, adoptPtr(info)); - setHasColumns(true); - } - info->setDesiredColumnCount(count); - info->setDesiredColumnWidth(width); - info->setProgressionIsInline(style().hasInlineColumnAxis()); - info->setProgressionIsReversed(style().columnProgression() == ReverseColumnProgression); - } -} - -void RenderBlock::updateColumnProgressionFromStyle(RenderStyle* style) -{ - if (!hasColumns()) - return; - - ColumnInfo* info = gColumnInfoMap->get(this); - - bool needsLayout = false; - bool oldProgressionIsInline = info->progressionIsInline(); - bool newProgressionIsInline = style->hasInlineColumnAxis(); - if (oldProgressionIsInline != newProgressionIsInline) { - info->setProgressionIsInline(newProgressionIsInline); - needsLayout = true; - } - - bool oldProgressionIsReversed = info->progressionIsReversed(); - bool newProgressionIsReversed = style->columnProgression() == ReverseColumnProgression; - if (oldProgressionIsReversed != newProgressionIsReversed) { - info->setProgressionIsReversed(newProgressionIsReversed); - needsLayout = true; - } - - if (needsLayout) - setNeedsLayoutAndPrefWidthsRecalc(); -} - -LayoutUnit RenderBlock::computedColumnWidth() const -{ - if (!hasColumns()) - return contentLogicalWidth(); - return gColumnInfoMap->get(this)->desiredColumnWidth(); -} - -unsigned RenderBlock::computedColumnCount() const -{ - if (!hasColumns()) - return 1; - return gColumnInfoMap->get(this)->desiredColumnCount(); -} - -ColumnInfo* RenderBlock::columnInfo() const -{ - if (!hasColumns()) - return 0; - return gColumnInfoMap->get(this); -} - -unsigned RenderBlock::columnCount(ColumnInfo* colInfo) const -{ - ASSERT(hasColumns()); - ASSERT(gColumnInfoMap->get(this) == colInfo); - return colInfo->columnCount(); -} - -LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const -{ - ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo); - - // Compute the appropriate rect based off our information. - LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth(); - LayoutUnit colLogicalHeight = colInfo->columnHeight(); - LayoutUnit colLogicalTop = borderAndPaddingBefore(); - LayoutUnit colLogicalLeft = logicalLeftOffsetForContent(); - LayoutUnit colGap = columnGap(); - if (colInfo->progressionIsInline()) { - if (style().isLeftToRightDirection() ^ colInfo->progressionIsReversed()) - 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); - } - - if (isHorizontalWritingMode()) - return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight); - return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); -} - -void RenderBlock::adjustPointToColumnContents(LayoutPoint& point) const -{ - // Just bail if we have no columns. - if (!hasColumns()) - return; - - ColumnInfo* colInfo = columnInfo(); - if (!columnCount(colInfo)) - return; - - // Determine which columns we intersect. - LayoutUnit colGap = columnGap(); - LayoutUnit halfColGap = colGap / 2; - LayoutPoint columnPoint(columnRectAt(colInfo, 0).location()); - LayoutUnit logicalOffset = 0; - for (unsigned i = 0; i < colInfo->columnCount(); i++) { - // Add in half the column gap to the left and right of the rect. - LayoutRect colRect = columnRectAt(colInfo, i); - flipForWritingMode(colRect); - if (isHorizontalWritingMode() == colInfo->progressionIsInline()) { - LayoutRect gapAndColumnRect(colRect.x() - halfColGap, colRect.y(), colRect.width() + colGap, colRect.height()); - if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.maxX()) { - if (colInfo->progressionIsInline()) { - // FIXME: The clamping that follows is not completely right for right-to-left - // content. - // Clamp everything above the column to its top left. - if (point.y() < gapAndColumnRect.y()) - point = gapAndColumnRect.location(); - // Clamp everything below the column to the next column's top left. If there is - // no next column, this still maps to just after this column. - else if (point.y() >= gapAndColumnRect.maxY()) { - point = gapAndColumnRect.location(); - point.move(0, gapAndColumnRect.height()); - } - } else { - if (point.x() < colRect.x()) - point.setX(colRect.x()); - else if (point.x() >= colRect.maxX()) - point.setX(colRect.maxX() - 1); - } - - // We're inside the column. Translate the x and y into our column coordinate space. - if (colInfo->progressionIsInline()) - point.move(columnPoint.x() - colRect.x(), (!style().isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset)); - else - point.move((!style().isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.x() + borderLeft() + paddingLeft(), 0); - return; - } - - // Move to the next position. - logicalOffset += colInfo->progressionIsInline() ? colRect.height() : colRect.width(); - } else { - LayoutRect gapAndColumnRect(colRect.x(), colRect.y() - halfColGap, colRect.width(), colRect.height() + colGap); - if (point.y() >= gapAndColumnRect.y() && point.y() < gapAndColumnRect.maxY()) { - if (colInfo->progressionIsInline()) { - // FIXME: The clamping that follows is not completely right for right-to-left - // content. - // Clamp everything above the column to its top left. - if (point.x() < gapAndColumnRect.x()) - point = gapAndColumnRect.location(); - // Clamp everything below the column to the next column's top left. If there is - // no next column, this still maps to just after this column. - else if (point.x() >= gapAndColumnRect.maxX()) { - point = gapAndColumnRect.location(); - point.move(gapAndColumnRect.width(), 0); - } - } else { - if (point.y() < colRect.y()) - point.setY(colRect.y()); - else if (point.y() >= colRect.maxY()) - point.setY(colRect.maxY() - 1); - } - - // We're inside the column. Translate the x and y into our column coordinate space. - if (colInfo->progressionIsInline()) - point.move((!style().isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset), columnPoint.y() - colRect.y()); - else - point.move(0, (!style().isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.y() + borderTop() + paddingTop()); - return; - } - - // Move to the next position. - logicalOffset += colInfo->progressionIsInline() ? colRect.width() : colRect.height(); - } - } -} - -void RenderBlock::adjustRectForColumns(LayoutRect& r) const -{ - // Just bail if we have no columns. - if (!hasColumns()) - return; - - ColumnInfo* colInfo = columnInfo(); - - // Determine which columns we intersect. - unsigned colCount = columnCount(colInfo); - if (!colCount) - return; - - // Begin with a result rect that is empty. - LayoutRect result; - - bool isHorizontal = isHorizontalWritingMode(); - LayoutUnit beforeBorderPadding = borderAndPaddingBefore(); - LayoutUnit colHeight = colInfo->columnHeight(); - if (!colHeight) - return; - - LayoutUnit startOffset = std::max(isHorizontal ? r.y() : r.x(), beforeBorderPadding); - LayoutUnit endOffset = std::max(std::min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight), beforeBorderPadding); - - // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744 - unsigned startColumn = (startOffset - beforeBorderPadding) / colHeight; - unsigned endColumn = (endOffset - beforeBorderPadding) / colHeight; - - if (startColumn == endColumn) { - // The rect is fully contained within one column. Adjust for our offsets - // and repaint only that portion. - LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent(); - LayoutRect colRect = columnRectAt(colInfo, startColumn); - LayoutRect repaintRect = r; - - if (colInfo->progressionIsInline()) { - if (isHorizontal) - repaintRect.move(colRect.x() - logicalLeftOffset, - static_cast<int>(startColumn) * colHeight); - else - repaintRect.move(- static_cast<int>(startColumn) * colHeight, colRect.y() - logicalLeftOffset); - } else { - if (isHorizontal) - repaintRect.move(0, colRect.y() - startColumn * colHeight - beforeBorderPadding); - else - repaintRect.move(colRect.x() - startColumn * colHeight - beforeBorderPadding, 0); - } - repaintRect.intersect(colRect); - result.unite(repaintRect); - } else { - // We span multiple columns. We can just unite the start and end column to get the final - // repaint rect. - result.unite(columnRectAt(colInfo, startColumn)); - result.unite(columnRectAt(colInfo, endColumn)); - } - - r = result; -} - -LayoutPoint RenderBlock::flipForWritingModeIncludingColumns(const LayoutPoint& point) const -{ - ASSERT(hasColumns()); - if (!hasColumns() || !style().isFlippedBlocksWritingMode()) - return point; - ColumnInfo* colInfo = columnInfo(); - LayoutUnit columnLogicalHeight = colInfo->columnHeight(); - LayoutUnit expandedLogicalHeight = borderAndPaddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAndPaddingAfter() + scrollbarLogicalHeight(); - if (isHorizontalWritingMode()) - return LayoutPoint(point.x(), expandedLogicalHeight - point.y()); - return LayoutPoint(expandedLogicalHeight - point.x(), point.y()); -} - -void RenderBlock::adjustStartEdgeForWritingModeIncludingColumns(LayoutRect& rect) const -{ - ASSERT(hasColumns()); - if (!hasColumns() || !style().isFlippedBlocksWritingMode()) - return; - - ColumnInfo* colInfo = columnInfo(); - LayoutUnit columnLogicalHeight = colInfo->columnHeight(); - LayoutUnit expandedLogicalHeight = borderAndPaddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAndPaddingAfter() + scrollbarLogicalHeight(); - - if (isHorizontalWritingMode()) - rect.setY(expandedLogicalHeight - rect.maxY()); - else - rect.setX(expandedLogicalHeight - rect.maxX()); -} - -void RenderBlock::adjustForColumns(LayoutSize& offset, const LayoutPoint& point) const -{ - if (!hasColumns()) - return; - - ColumnInfo* colInfo = columnInfo(); - - LayoutUnit logicalLeft = logicalLeftOffsetForContent(); - unsigned colCount = columnCount(colInfo); - LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth(); - LayoutUnit colLogicalHeight = colInfo->columnHeight(); - - for (unsigned i = 0; i < colCount; ++i) { - // Compute the edges for a given column in the block progression direction. - LayoutRect sliceRect = LayoutRect(logicalLeft, borderAndPaddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight); - if (!isHorizontalWritingMode()) - sliceRect = sliceRect.transposedRect(); - - LayoutUnit logicalOffset = i * colLogicalHeight; - - // Now we're in the same coordinate space as the point. See if it is inside the rectangle. - if (isHorizontalWritingMode()) { - if (point.y() >= sliceRect.y() && point.y() < sliceRect.maxY()) { - if (colInfo->progressionIsInline()) - offset.expand(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset); - else - offset.expand(0, columnRectAt(colInfo, i).y() - logicalOffset - borderAndPaddingBefore()); - return; - } - } else { - if (point.x() >= sliceRect.x() && point.x() < sliceRect.maxX()) { - if (colInfo->progressionIsInline()) - offset.expand(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft); - else - offset.expand(columnRectAt(colInfo, i).x() - logicalOffset - borderAndPaddingBefore(), 0); - return; - } - } - } -} - void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const { - if (childrenInline()) { - // FIXME: Remove this const_cast. - const_cast<RenderBlock*>(this)->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); - } else - computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); + ASSERT(!childrenInline()); + + computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); - adjustIntrinsicLogicalWidthsForColumns(minLogicalWidth, maxLogicalWidth); - - if (!style().autoWrap() && childrenInline()) { - // A horizontal marquee with inline children has no minimum width. - if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) - minLogicalWidth = 0; - } - - if (isTableCell()) { - Length tableCellWidth = toRenderTableCell(this)->styleOrColLogicalWidth(); - if (tableCellWidth.isFixed() && tableCellWidth.value() > 0) - maxLogicalWidth = std::max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth.value())); - } - - int scrollbarWidth = instrinsicScrollbarLogicalWidth(); + int scrollbarWidth = intrinsicScrollbarLogicalWidth(); maxLogicalWidth += scrollbarWidth; minLogicalWidth += scrollbarWidth; } @@ -4045,7 +2725,9 @@ void RenderBlock::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); - updateFirstLetter(); + // FIXME: Do not even try to reshuffle first letter renderers when we are not in layout + // until after webkit.org/b/163848 is fixed. + updateFirstLetter(view().frameView().isInRenderTreeLayout() ? RenderTreeMutationIsAllowed::Yes : RenderTreeMutationIsAllowed::No); m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; @@ -4067,12 +2749,6 @@ void RenderBlock::computePreferredLogicalWidths() m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); } - // Table layout uses integers, ceil the preferred widths to ensure that they can contain the contents. - if (isTableCell()) { - m_minPreferredLogicalWidth = m_minPreferredLogicalWidth.ceil(); - m_maxPreferredLogicalWidth = m_maxPreferredLogicalWidth.ceil(); - } - LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); m_minPreferredLogicalWidth += borderAndPadding; m_maxPreferredLogicalWidth += borderAndPadding; @@ -4080,456 +2756,6 @@ void RenderBlock::computePreferredLogicalWidths() setPreferredLogicalWidthsDirty(false); } -void RenderBlock::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - // FIXME: Move this code to RenderBlockFlow. - - if (!style().hasAutoColumnCount() || !style().hasAutoColumnWidth()) { - // The min/max intrinsic widths calculated really tell how much space elements need when - // laid out inside the columns. In order to eventually end up with the desired column width, - // we need to convert them to values pertaining to the multicol container. - int columnCount = style().hasAutoColumnCount() ? 1 : style().columnCount(); - LayoutUnit columnWidth; - LayoutUnit gapExtra = (columnCount - 1) * columnGap(); - if (style().hasAutoColumnWidth()) - minLogicalWidth = minLogicalWidth * columnCount + gapExtra; - else { - columnWidth = style().columnWidth(); - minLogicalWidth = std::min(minLogicalWidth, columnWidth); - } - // FIXME: If column-count is auto here, we should resolve it to calculate the maximum - // intrinsic width, instead of pretending that it's 1. The only way to do that is by - // performing a layout pass, but this is not an appropriate time or place for layout. The - // good news is that if height is unconstrained and there are no explicit breaks, the - // resolved column-count really should be 1. - maxLogicalWidth = std::max(maxLogicalWidth, columnWidth) * columnCount + gapExtra; - } -} - -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->firstChildSlow(); - 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->isTextOrLineBreak() || 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) -{ - const RenderStyle& childStyle = child->style(); - if (endOfInline) - return getBPMWidth(child->marginEnd(), childStyle.marginEnd()) + - getBPMWidth(child->paddingEnd(), childStyle.paddingEnd()) + - child->borderEnd(); - return getBPMWidth(child->marginStart(), childStyle.marginStart()) + - getBPMWidth(child->paddingStart(), childStyle.paddingStart()) + - child->borderStart(); -} - -static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, - RenderObject* trailingSpaceChild) -{ - if (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(RenderBlock::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 = ceiledLayoutUnit(result); - preferredWidth = std::max(snappedResult, preferredWidth); -} - -// With sub-pixel enabled: 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. -// -// With sub-pixel disabled: values from Lengths or the render tree aren't subject -// to the same loss of precision, as they're always truncated and stored as -// integers. We mirror that behavior here to prevent over-allocating our preferred -// width. -static inline LayoutUnit adjustFloatForSubPixelLayout(float value) -{ -#if ENABLE(SUBPIXEL_LAYOUT) - return ceiledLayoutUnit(value); -#else - return static_cast<int>(value); -#endif -} - - -void RenderBlock::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) -{ - float inlineMax = 0; - float inlineMin = 0; - - const RenderStyle& styleToUse = style(); - RenderBlock* containingBlock = this->containingBlock(); - LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit(); - - // If we are at the start of a line, we want to ignore all white-space. - // Also strip spaces if we previously had text that ended in a trailing space. - bool stripFrontSpaces = true; - RenderObject* trailingSpaceChild = 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); - 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). - const RenderStyle& childStyle = child->style(); - float childMin = 0; - float childMax = 0; - - if (!child->isText()) { - if (child->isLineBreakOpportunity()) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - continue; - } - // 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->setPreferredLogicalWidthsDirty(false); - } else { - // Inline replaced elts add in their margins to their min/max values. - LayoutUnit margins = 0; - Length startMargin = childStyle.marginStart(); - Length endMargin = childStyle.marginEnd(); - if (startMargin.isFixed()) - margins += 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. - childMin += child->minPreferredLogicalWidth().ceilToFloat(); - childMax += child->maxPreferredLogicalWidth().ceilToFloat(); - - bool clearPreviousFloat; - if (child->isFloating()) { - clearPreviousFloat = (prevFloat - && ((prevFloat->style().floating() == LeftFloat && (childStyle.clear() & CLEFT)) - || (prevFloat->style().floating() == RightFloat && (childStyle.clear() & CRIGHT)))); - prevFloat = child; - } else - clearPreviousFloat = false; - - bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; - if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) { - 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()) { - LayoutUnit ceiledIndent = textIndent.ceilToFloat(); - childMin += ceiledIndent; - childMax += ceiledIndent; - - if (childMin < 0) - textIndent = adjustFloatForSubPixelLayout(childMin); - else - addedTextIndent = true; - } - - // Add our width to the max. - inlineMax += std::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->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 beginMin, endMin; - bool beginWS, endWS; - float beginMax, endMax; - t->trimmedPrefWidths(inlineMax, beginMin, beginWS, endMin, endWS, - hasBreakableChar, hasBreak, beginMax, endMax, - childMin, childMax, stripFrontSpaces); - - // This text object will not be rendered, but it may still provide a breaking opportunity. - if (!hasBreak && childMax == 0) { - if (autoWrap && (beginWS || endWS)) { - 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; - beginMin += ti; - - // It the text indent negative and larger than the child minimum, we re-use the remainder - // in future minimum calculations, but using the negative value again on the maximum - // will lead to under-counting the max pref width. - if (!addedTextIndent) { - childMax += ti; - beginMax += ti; - addedTextIndent = true; - } - - if (childMin < 0) { - textIndent = childMin; - hasRemainingNegativeTextIndent = true; - } - } - - // If we have no breakable characters at all, - // then this is the easy case. We add ourselves to the current - // min and max and continue. - if (!hasBreakableChar) { - inlineMin += childMin; - } else { - // We have a breakable character. Now we need to know if - // we start and end with whitespace. - if (beginWS) - // Go ahead and end the current line. - updatePreferredWidth(minLogicalWidth, inlineMin); - else { - inlineMin += beginMin; - updatePreferredWidth(minLogicalWidth, inlineMin); - childMin -= ti; - } - - inlineMin = childMin; - - if (endWS) { - // We end in whitespace, which means we can go ahead - // and end our current line. - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - shouldBreakLineAfterText = false; - } else { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = endMin; - shouldBreakLineAfterText = true; - } - } - - if (hasBreak) { - inlineMax += beginMax; - updatePreferredWidth(maxLogicalWidth, inlineMax); - updatePreferredWidth(maxLogicalWidth, childMax); - inlineMax = endMax; - addedTextIndent = true; - } else - inlineMax += std::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 { const RenderStyle& styleToUse = style(); @@ -4546,7 +2772,7 @@ void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth } const RenderStyle& childStyle = child->style(); - if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { + if (child->isFloating() || (is<RenderBox>(*child) && downcast<RenderBox>(*child).avoidsFloats())) { LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth; if (childStyle.clear() & CLEFT) { maxLogicalWidth = std::max(floatTotalWidth, maxLogicalWidth); @@ -4573,14 +2799,20 @@ void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth margin = marginStart + marginEnd; 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; + if (is<RenderBox>(*child) && child->isHorizontalWritingMode() != isHorizontalWritingMode()) { + auto& childBox = downcast<RenderBox>(*child); + childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = childBox.computeLogicalHeight(childBox.borderAndPaddingLogicalHeight(), 0).m_extent; } else { childMinPreferredLogicalWidth = child->minPreferredLogicalWidth(); childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); + + if (is<RenderBlock>(*child)) { + const Length& computedInlineSize = child->style().logicalWidth(); + if (computedInlineSize.isMaxContent()) + childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth; + else if (computedInlineSize.isMinContent()) + childMaxPreferredLogicalWidth = childMinPreferredLogicalWidth; + } } LayoutUnit w = childMinPreferredLogicalWidth + margin; @@ -4593,7 +2825,7 @@ void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth w = childMaxPreferredLogicalWidth + margin; if (!child->isFloating()) { - if (child->isBox() && toRenderBox(child)->avoidsFloats()) { + if (is<RenderBox>(*child) && downcast<RenderBox>(*child).avoidsFloats()) { // Determine a left and right max value based off whether or not the floats can fit in the // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin // is smaller than the float width. @@ -4641,6 +2873,10 @@ bool RenderBlock::hasLineIfEmpty() const LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const { + // Anonymous inline blocks don't include margins or any real line height. + if (isAnonymousInlineBlock() && linePositionMode == PositionOnContainingLine) + return direction == HorizontalLine ? height() : width(); + // Inline blocks are replaced elements. Otherwise, just pass off to // 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 @@ -4648,16 +2884,13 @@ LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, if (isReplaced() && linePositionMode == PositionOnContainingLine) return RenderBox::lineHeight(firstLine, direction, linePositionMode); - if (firstLine && document().styleSheetCollection().usesFirstLineRules()) { - RenderStyle& s = firstLine ? firstLineStyle() : style(); + if (firstLine && view().usesFirstLineRules()) { + auto& s = firstLineStyle(); if (&s != &style()) - return s.computedLineHeight(&view()); + return s.computedLineHeight(); } - if (m_lineHeight == -1) - m_lineHeight = style().computedLineHeight(&view()); - - return m_lineHeight; + return style().computedLineHeight(); } int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const @@ -4667,28 +2900,39 @@ int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, Lin // box, then the fact that we're an inline-block is irrelevant, and we behave // just like a block. if (isReplaced() && linePositionMode == PositionOnContainingLine) { + if (isAnonymousInlineBlock()) + return direction == HorizontalLine ? height() : width(); + // 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. // FIXME: Need to patch form controls to deal with vertical lines. if (style().hasAppearance() && !theme().isControlContainer(style().appearance())) - return theme().baselinePosition(this); + return theme().baselinePosition(*this); // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in // the normal flow. We make an exception for marquees, since their baselines are meaningless // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them. // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled - // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside - // of our content box. - bool ignoreBaseline = (layer() && (layer()->marquee() || (direction == HorizontalLine ? (layer()->verticalScrollbar() || layer()->scrollYOffset() != 0) - : (layer()->horizontalScrollbar() || layer()->scrollXOffset() != 0)))) || (isWritingModeRoot() && !isRubyRun()); + // vertically (e.g., an overflow:hidden block that has had scrollTop moved). + bool ignoreBaseline = (layer() && (layer()->marquee() || (direction == HorizontalLine ? (layer()->verticalScrollbar() || layer()->scrollOffset().y() != 0) + : (layer()->horizontalScrollbar() || layer()->scrollOffset().x() != 0)))) || (isWritingModeRoot() && !isRubyRun()); - int baselinePos = ignoreBaseline ? -1 : inlineBlockBaseline(direction); + std::optional<int> baselinePos = ignoreBaseline ? std::optional<int>() : inlineBlockBaseline(direction); - LayoutUnit bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth(); - if (baselinePos != -1 && baselinePos <= bottomOfContent) - return direction == HorizontalLine ? marginTop() + baselinePos : marginRight() + baselinePos; - + if (isDeprecatedFlexibleBox()) { + // Historically, we did this check for all baselines. But we can't + // remove this code from deprecated flexbox, because it effectively + // breaks -webkit-line-clamp, which is used in the wild -- we would + // calculate the baseline as if -webkit-line-clamp wasn't used. + // For simplicity, we use this for all uses of deprecated flexbox. + LayoutUnit bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth(); + if (baselinePos && baselinePos.value() > bottomOfContent) + baselinePos = std::optional<int>(); + } + if (baselinePos) + return direction == HorizontalLine ? marginTop() + baselinePos.value() : marginRight() + baselinePos.value(); + return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); } @@ -4709,45 +2953,54 @@ LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, Layou return std::max<LayoutUnit>(replacedHeight, lineHeight(isFirstLine, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); } -int RenderBlock::firstLineBaseline() const +std::optional<int> RenderBlock::firstLineBaseline() const { if (isWritingModeRoot() && !isRubyRun()) - return -1; + return std::optional<int>(); for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { if (!curr->isFloatingOrOutOfFlowPositioned()) { - int result = curr->firstLineBaseline(); - if (result != -1) - return curr->logicalTop() + result; // Translate to our coordinate space. + if (std::optional<int> result = curr->firstLineBaseline()) + return std::optional<int>(curr->logicalTop() + result.value()); // Translate to our coordinate space. } } - return -1; + return std::optional<int>(); } -int RenderBlock::inlineBlockBaseline(LineDirectionMode lineDirection) const +std::optional<int> RenderBlock::inlineBlockBaseline(LineDirectionMode lineDirection) const { if (isWritingModeRoot() && !isRubyRun()) - return -1; + return std::optional<int>(); bool haveNormalFlowChild = false; - for (auto box = lastChildBox(); box; box = box->previousSiblingBox()) { + for (auto* box = lastChildBox(); box; box = box->previousSiblingBox()) { if (box->isFloatingOrOutOfFlowPositioned()) continue; haveNormalFlowChild = true; - int result = box->inlineBlockBaseline(lineDirection); - if (result != -1) - return box->logicalTop() + result; // Translate to our coordinate space. + if (std::optional<int> result = box->inlineBlockBaseline(lineDirection)) + return std::optional<int>(box->logicalTop() + result.value()); // Translate to our coordinate space. } if (!haveNormalFlowChild && hasLineIfEmpty()) { auto& fontMetrics = firstLineStyle().fontMetrics(); - return fontMetrics.ascent() - + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 - + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); + return std::optional<int>(fontMetrics.ascent() + + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 + + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight())); } - return -1; + return std::optional<int>(); +} + +static inline bool isRenderBlockFlowOrRenderButton(RenderElement& renderElement) +{ + // We include isRenderButton in this check because buttons are implemented + // using flex box but should still support first-line|first-letter. + // The flex box and specs require that flex box and grid do not support + // first-line|first-letter, though. + // FIXME: Remove when buttons are implemented with align-items instead of + // flex box. + return renderElement.isRenderBlockFlow() || renderElement.isRenderButton(); } RenderBlock* RenderBlock::firstLineBlock() const @@ -4759,32 +3012,67 @@ RenderBlock* RenderBlock::firstLineBlock() const if (hasPseudo) break; RenderElement* parentBlock = firstLineBlock->parent(); - // We include isRenderButton in this check because buttons are - // implemented using flex box but should still support first-line. The - // flex box spec requires that flex box does not support first-line, - // though. - // FIXME: Remove when buttons are implemented with align-items instead - // of flexbox. if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() - || !parentBlock || parentBlock->firstChild() != firstLineBlock || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) + || !parentBlock || parentBlock->firstChild() != firstLineBlock || !isRenderBlockFlowOrRenderButton(*parentBlock)) break; - firstLineBlock = toRenderBlock(parentBlock); + firstLineBlock = downcast<RenderBlock>(parentBlock); } if (!hasPseudo) - return 0; + return nullptr; return firstLineBlock; } -static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer) +static RenderStyle styleForFirstLetter(const RenderElement& firstLetterBlock, const RenderObject& firstLetterContainer) { - RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, &firstLetterContainer->firstLineStyle()); + auto* containerFirstLetterStyle = firstLetterBlock.getCachedPseudoStyle(FIRST_LETTER, &firstLetterContainer.firstLineStyle()); + // FIXME: There appears to be some path where we have a first letter renderer without first letter style. + ASSERT(containerFirstLetterStyle); + auto firstLetterStyle = RenderStyle::clone(containerFirstLetterStyle ? *containerFirstLetterStyle : firstLetterContainer.firstLineStyle()); + + // If we have an initial letter drop that is >= 1, then we need to force floating to be on. + if (firstLetterStyle.initialLetterDrop() >= 1 && !firstLetterStyle.isFloating()) + firstLetterStyle.setFloating(firstLetterStyle.isLeftToRightDirection() ? LeftFloat : RightFloat); + + // We have to compute the correct font-size for the first-letter if it has an initial letter height set. + auto* paragraph = firstLetterContainer.isRenderBlockFlow() ? &firstLetterContainer : firstLetterContainer.containingBlock(); + if (firstLetterStyle.initialLetterHeight() >= 1 && firstLetterStyle.fontMetrics().hasCapHeight() && paragraph->style().fontMetrics().hasCapHeight()) { + // FIXME: For ideographic baselines, we want to go from line edge to line edge. This is equivalent to (N-1)*line-height + the font height. + // We don't yet support ideographic baselines. + // For an N-line first-letter and for alphabetic baselines, the cap-height of the first letter needs to equal (N-1)*line-height of paragraph lines + cap-height of the paragraph + // Mathematically we can't rely on font-size, since font().height() doesn't necessarily match. For reliability, the best approach is simply to + // compare the final measured cap-heights of the two fonts in order to get to the closest possible value. + firstLetterStyle.setLineBoxContain(LineBoxContainInitialLetter); + int lineHeight = paragraph->style().computedLineHeight(); + + // Set the font to be one line too big and then ratchet back to get to a precise fit. We can't just set the desired font size based off font height metrics + // because many fonts bake ascent into the font metrics. Therefore we have to look at actual measured cap height values in order to know when we have a good fit. + auto newFontDescription = firstLetterStyle.fontDescription(); + float capRatio = firstLetterStyle.fontMetrics().floatCapHeight() / firstLetterStyle.fontSize(); + float startingFontSize = ((firstLetterStyle.initialLetterHeight() - 1) * lineHeight + paragraph->style().fontMetrics().capHeight()) / capRatio; + newFontDescription.setSpecifiedSize(startingFontSize); + newFontDescription.setComputedSize(startingFontSize); + firstLetterStyle.setFontDescription(newFontDescription); + firstLetterStyle.fontCascade().update(firstLetterStyle.fontCascade().fontSelector()); + + int desiredCapHeight = (firstLetterStyle.initialLetterHeight() - 1) * lineHeight + paragraph->style().fontMetrics().capHeight(); + int actualCapHeight = firstLetterStyle.fontMetrics().capHeight(); + while (actualCapHeight > desiredCapHeight) { + auto newFontDescription = firstLetterStyle.fontDescription(); + newFontDescription.setSpecifiedSize(newFontDescription.specifiedSize() - 1); + newFontDescription.setComputedSize(newFontDescription.computedSize() -1); + firstLetterStyle.setFontDescription(newFontDescription); + firstLetterStyle.fontCascade().update(firstLetterStyle.fontCascade().fontSelector()); + actualCapHeight = firstLetterStyle.fontMetrics().capHeight(); + } + } + // Force inline display (except for floating first-letters). - pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); + firstLetterStyle.setDisplay(firstLetterStyle.isFloating() ? BLOCK : INLINE); // CSS2 says first-letter can't be positioned. - pseudoStyle->setPosition(StaticPosition); - return pseudoStyle; + firstLetterStyle.setPosition(StaticPosition); + return firstLetterStyle; } // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter @@ -4804,55 +3092,49 @@ static inline RenderBlock* findFirstLetterBlock(RenderBlock* start) { RenderBlock* firstLetterBlock = start; while (true) { - // We include isRenderButton in these two checks because buttons are - // implemented using flex box but should still support first-letter. - // The flex box spec requires that flex box does not support - // first-letter, though. - // FIXME: Remove when buttons are implemented with align-items instead - // of flexbox. bool canHaveFirstLetterRenderer = firstLetterBlock->style().hasPseudoStyle(FIRST_LETTER) && firstLetterBlock->canHaveGeneratedChildren() - && (!firstLetterBlock->isFlexibleBox() || firstLetterBlock->isRenderButton()); + && isRenderBlockFlowOrRenderButton(*firstLetterBlock); if (canHaveFirstLetterRenderer) return firstLetterBlock; RenderElement* parentBlock = firstLetterBlock->parent(); if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock - || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) - return 0; - firstLetterBlock = toRenderBlock(parentBlock); + || !isRenderBlockFlowOrRenderButton(*parentBlock)) + return nullptr; + firstLetterBlock = downcast<RenderBlock>(parentBlock); } - return 0; + return nullptr; } -void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderObject* currentChild) +void RenderBlock::updateFirstLetterStyle(RenderElement* firstLetterBlock, RenderObject* currentChild) { RenderElement* firstLetter = currentChild->parent(); RenderElement* firstLetterContainer = firstLetter->parent(); - RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); + auto pseudoStyle = styleForFirstLetter(*firstLetterBlock, *firstLetterContainer); ASSERT(firstLetter->isFloating() || firstLetter->isInline()); - if (Style::determineChange(&firstLetter->style(), pseudoStyle) == Style::Detach) { + if (Style::determineChange(firstLetter->style(), pseudoStyle) == Style::Detach) { // The first-letter renderer needs to be replaced. Create a new renderer of the right type. RenderBoxModelObject* newFirstLetter; - if (pseudoStyle->display() == INLINE) - newFirstLetter = new RenderInline(document(), *pseudoStyle); + if (pseudoStyle.display() == INLINE) + newFirstLetter = new RenderInline(document(), WTFMove(pseudoStyle)); else - newFirstLetter = new RenderBlockFlow(document(), *pseudoStyle); + newFirstLetter = new RenderBlockFlow(document(), WTFMove(pseudoStyle)); newFirstLetter->initializeStyle(); // Move the first letter into the new renderer. - LayoutStateDisabler layoutStateDisabler(&view()); + LayoutStateDisabler layoutStateDisabler(view()); while (RenderObject* child = firstLetter->firstChild()) { - if (child->isText()) - toRenderText(child)->removeAndDestroyTextBoxes(); + if (is<RenderText>(*child)) + downcast<RenderText>(*child).removeAndDestroyTextBoxes(); firstLetter->removeChild(*child); - newFirstLetter->addChild(child, 0); + newFirstLetter->addChild(child, nullptr); } RenderObject* nextSibling = firstLetter->nextSibling(); - if (RenderTextFragment* remainingText = toRenderBoxModelObject(firstLetter)->firstLetterRemainingText()) { + if (RenderTextFragment* remainingText = downcast<RenderBoxModelObject>(*firstLetter).firstLetterRemainingText()) { ASSERT(remainingText->isAnonymous() || remainingText->textNode()->renderer() == remainingText); // Replace the old renderer with the new one. remainingText->setFirstLetter(*newFirstLetter); @@ -4865,18 +3147,18 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO firstLetter = newFirstLetter; firstLetterContainer->addChild(firstLetter, nextSibling); } else - firstLetter->setStyle(*pseudoStyle); + firstLetter->setStyle(WTFMove(pseudoStyle)); } -void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderText* currentTextChild) +void RenderBlock::createFirstLetterRenderer(RenderElement* firstLetterBlock, RenderText* currentTextChild) { RenderElement* firstLetterContainer = currentTextChild->parent(); - RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); - RenderBoxModelObject* firstLetter = 0; - if (pseudoStyle->display() == INLINE) - firstLetter = new RenderInline(document(), *pseudoStyle); + auto pseudoStyle = styleForFirstLetter(*firstLetterBlock, *firstLetterContainer); + RenderBoxModelObject* firstLetter = nullptr; + if (pseudoStyle.display() == INLINE) + firstLetter = new RenderInline(document(), WTFMove(pseudoStyle)); else - firstLetter = new RenderBlockFlow(document(), *pseudoStyle); + firstLetter = new RenderBlockFlow(document(), WTFMove(pseudoStyle)); firstLetter->initializeStyle(); firstLetterContainer->addChild(firstLetter, currentTextChild); @@ -4893,8 +3175,8 @@ void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend while (length < oldText.length() && shouldSkipForFirstLetter(oldText[length])) length++; - // Account for first letter. - length++; + // Account for first grapheme cluster. + length += numCharactersInGraphemeClusters(StringView(oldText).substring(length), 1); // Keep looking for whitespace and allowed punctuation, but avoid // accumulating just whitespace into the :first-letter. @@ -4936,129 +3218,223 @@ void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend currentTextChild->destroy(); } } - -void RenderBlock::updateFirstLetter() + +void RenderBlock::getFirstLetter(RenderObject*& firstLetter, RenderElement*& firstLetterContainer, RenderObject* skipObject) { - if (!document().styleSheetCollection().usesFirstLetterRules()) + firstLetter = nullptr; + firstLetterContainer = nullptr; + + if (!view().usesFirstLetterRules()) return; + // Don't recur if (style().styleType() == FIRST_LETTER) return; - + // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find // an efficient way to check for that situation though before implementing anything. - RenderElement* firstLetterBlock = findFirstLetterBlock(this); - if (!firstLetterBlock) + firstLetterContainer = findFirstLetterBlock(this); + if (!firstLetterContainer) return; - + // Drill into inlines looking for our first text descendant. - RenderObject* descendant = firstLetterBlock->firstChild(); - while (descendant) { - if (descendant->isText()) + firstLetter = firstLetterContainer->firstChild(); + while (firstLetter) { + if (is<RenderText>(*firstLetter)) { + if (firstLetter == skipObject) { + firstLetter = firstLetter->nextSibling(); + continue; + } + break; - RenderElement& current = toRenderElement(*descendant); - if (current.isListMarker()) - descendant = current.nextSibling(); + } + + RenderElement& current = downcast<RenderElement>(*firstLetter); + if (is<RenderListMarker>(current)) + firstLetter = current.nextSibling(); else if (current.isFloatingOrOutOfFlowPositioned()) { if (current.style().styleType() == FIRST_LETTER) { - descendant = current.firstChild(); + firstLetter = current.firstChild(); break; } - descendant = current.nextSibling(); - } else if (current.isReplaced() || current.isRenderButton() || current.isMenuList()) + firstLetter = current.nextSibling(); + } else if (current.isReplaced() || is<RenderButton>(current) || is<RenderMenuList>(current)) break; + else if (current.isFlexibleBoxIncludingDeprecated() || current.isRenderGrid()) + firstLetter = current.nextSibling(); else if (current.style().hasPseudoStyle(FIRST_LETTER) && current.canHaveGeneratedChildren()) { // We found a lower-level node with first-letter, which supersedes the higher-level style - firstLetterBlock = ¤t; - descendant = current.firstChild(); + firstLetterContainer = ¤t; + firstLetter = current.firstChild(); } else - descendant = current.firstChild(); + firstLetter = current.firstChild(); } + + if (!firstLetter) + firstLetterContainer = nullptr; +} + +void RenderBlock::updateFirstLetter(RenderTreeMutationIsAllowed mutationAllowedOrNot) +{ + RenderObject* firstLetterObj; + RenderElement* firstLetterContainer; + // FIXME: The first letter might be composed of a variety of code units, and therefore might + // be contained within multiple RenderElements. + getFirstLetter(firstLetterObj, firstLetterContainer); - if (!descendant) + if (!firstLetterObj || !firstLetterContainer) return; // If the child already has style, then it has already been created, so we just want // to update it. - if (descendant->parent()->style().styleType() == FIRST_LETTER) { - updateFirstLetterStyle(firstLetterBlock, descendant); + if (firstLetterObj->parent()->style().styleType() == FIRST_LETTER) { + updateFirstLetterStyle(firstLetterContainer, firstLetterObj); return; } - if (!descendant->isText()) + if (!is<RenderText>(*firstLetterObj)) return; + if (mutationAllowedOrNot != RenderTreeMutationIsAllowed::Yes) + 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()); + LayoutStateDisabler layoutStateDisabler(view()); + + createFirstLetterRenderer(firstLetterContainer, downcast<RenderText>(firstLetterObj)); +} + +RenderFlowThread* RenderBlock::cachedFlowThreadContainingBlock() const +{ + RenderBlockRareData* rareData = getBlockRareData(*this); + + if (!rareData || !rareData->m_flowThreadContainingBlock) + return nullptr; + + return rareData->m_flowThreadContainingBlock.value(); +} + +bool RenderBlock::cachedFlowThreadContainingBlockNeedsUpdate() const +{ + RenderBlockRareData* rareData = getBlockRareData(*this); + + if (!rareData || !rareData->m_flowThreadContainingBlock) + return true; + + return false; +} + +void RenderBlock::setCachedFlowThreadContainingBlockNeedsUpdate() +{ + RenderBlockRareData& rareData = ensureBlockRareData(*this); + rareData.m_flowThreadContainingBlock = std::nullopt; +} + +RenderFlowThread* RenderBlock::updateCachedFlowThreadContainingBlock(RenderFlowThread* flowThread) const +{ + RenderBlockRareData& rareData = ensureBlockRareData(*this); + rareData.m_flowThreadContainingBlock = flowThread; - createFirstLetterRenderer(firstLetterBlock, toRenderText(descendant)); + return flowThread; +} + +RenderFlowThread* RenderBlock::locateFlowThreadContainingBlock() const +{ + RenderBlockRareData* rareData = getBlockRareData(*this); + if (!rareData || !rareData->m_flowThreadContainingBlock) + return updateCachedFlowThreadContainingBlock(RenderBox::locateFlowThreadContainingBlock()); + + ASSERT(rareData->m_flowThreadContainingBlock.value() == RenderBox::locateFlowThreadContainingBlock()); + return rareData->m_flowThreadContainingBlock.value(); +} + +void RenderBlock::resetFlowThreadContainingBlockAndChildInfoIncludingDescendants() +{ + if (flowThreadState() == NotInsideFlowThread) + return; + + if (cachedFlowThreadContainingBlockNeedsUpdate()) + return; + + auto* flowThread = cachedFlowThreadContainingBlock(); + setCachedFlowThreadContainingBlockNeedsUpdate(); + + if (flowThread) + flowThread->removeFlowChildInfo(*this); + + for (auto& child : childrenOfType<RenderElement>(*this)) { + if (flowThread) + flowThread->removeFlowChildInfo(child); + if (is<RenderBlock>(child)) + downcast<RenderBlock>(child).resetFlowThreadContainingBlockAndChildInfoIncludingDescendants(); + } } LayoutUnit RenderBlock::paginationStrut() const { - RenderBlockRareData* rareData = getRareData(this); + RenderBlockRareData* rareData = getBlockRareData(*this); return rareData ? rareData->m_paginationStrut : LayoutUnit(); } LayoutUnit RenderBlock::pageLogicalOffset() const { - RenderBlockRareData* rareData = getRareData(this); + RenderBlockRareData* rareData = getBlockRareData(*this); return rareData ? rareData->m_pageLogicalOffset : LayoutUnit(); } void RenderBlock::setPaginationStrut(LayoutUnit strut) { - RenderBlockRareData* rareData = getRareData(this); + RenderBlockRareData* rareData = getBlockRareData(*this); if (!rareData) { if (!strut) return; - rareData = &ensureRareData(this); + rareData = &ensureBlockRareData(*this); } rareData->m_paginationStrut = strut; } void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset) { - RenderBlockRareData* rareData = getRareData(this); + RenderBlockRareData* rareData = getBlockRareData(*this); if (!rareData) { if (!logicalOffset) return; - rareData = &ensureRareData(this); + rareData = &ensureBlockRareData(*this); } rareData->m_pageLogicalOffset = logicalOffset; } void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const { - // For blocks inside inlines, we go ahead and include margins so that we run right up to the - // inline boxes above and below us (thus getting merged with them to form a single irregular - // shape). + // For blocks inside inlines, we include margins so that we run right up to the inline boxes + // above and below us (thus getting merged with them to form a single irregular shape). if (isAnonymousBlockContinuation()) { // FIXME: This is wrong for block-flows that are horizontal. // https://bugs.webkit.org/show_bug.cgi?id=46781 - rects.append(pixelSnappedIntRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(), + rects.append(snappedIntRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(), width(), height() + collapsedMarginBefore() + collapsedMarginAfter())); continuation()->absoluteRects(rects, accumulatedOffset - toLayoutSize(location() + inlineElementContinuation()->containingBlock()->location())); } else - rects.append(pixelSnappedIntRect(accumulatedOffset, size())); + rects.append(snappedIntRect(accumulatedOffset, size())); } void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const { - // For blocks inside inlines, we go ahead and include margins so that we run right up to the - // inline boxes above and below us (thus getting merged with them to form a single irregular - // shape). - 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()); - quads.append(localToAbsoluteQuad(localRect, 0 /* mode */, wasFixed)); + // For blocks inside inlines, we include margins so that we run right up to the inline boxes + // above and below us (thus getting merged with them to form a single irregular shape). + FloatRect localRect = isAnonymousBlockContinuation() + ? FloatRect(0, -collapsedMarginBefore(), width(), height() + collapsedMarginBefore() + collapsedMarginAfter()) + : FloatRect(0, 0, width(), height()); + + // FIXME: This is wrong for block-flows that are horizontal. + // https://bugs.webkit.org/show_bug.cgi?id=46781 + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread || !flowThread->absoluteQuadsForBox(quads, wasFixed, this, localRect.y(), localRect.maxY())) + quads.append(localToAbsoluteQuad(localRect, UseTransforms, wasFixed)); + + if (isAnonymousBlockContinuation()) continuation()->absoluteQuads(quads, wasFixed); - } else - quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width(), height()), 0 /* mode */, wasFixed)); } LayoutRect RenderBlock::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const @@ -5083,14 +3459,14 @@ void RenderBlock::updateDragState(bool dragOn) const RenderStyle& RenderBlock::outlineStyleForRepaint() const { - return isAnonymousBlockContinuation() ? continuation()->style() : style(); + return isAnonymousBlockContinuation() ? continuation()->style() : RenderElement::outlineStyleForRepaint(); } -void RenderBlock::childBecameNonInline(RenderObject*) +void RenderBlock::childBecameNonInline(RenderElement&) { makeChildrenNonInline(); - if (isAnonymousBlock() && parent() && parent()->isRenderBlock()) - toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); + if (isAnonymousBlock() && is<RenderBlock>(parent())) + downcast<RenderBlock>(*parent()).removeLeftoverAnonymousBlock(this); // |this| may be dead here } @@ -5107,7 +3483,7 @@ void RenderBlock::updateHitTestResult(HitTestResult& result, const LayoutPoint& } } -LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine) +LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, unsigned caretOffset, LayoutUnit* extraWidthToEndOfLine) { // Do the normal calculation in most cases. if (firstChild()) @@ -5122,65 +3498,63 @@ LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, La return caretRect; } -void RenderBlock::addFocusRingRectsForInlineChildren(Vector<IntRect>&, const LayoutPoint&, const RenderLayerModelObject*) +void RenderBlock::addFocusRingRectsForInlineChildren(Vector<LayoutRect>&, const LayoutPoint&, const RenderLayerModelObject*) { ASSERT_NOT_REACHED(); } -void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) +void RenderBlock::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) { - // For blocks inside inlines, we go ahead and include margins so that we run right up to the - // inline boxes above and below us (thus getting merged with them to form a single irregular - // shape). + // For blocks inside inlines, we include margins so that we run right up to the inline boxes + // above and below us (thus getting merged with them to form a single irregular shape). if (inlineElementContinuation()) { // FIXME: This check really isn't accurate. bool nextInlineHasLineBox = inlineElementContinuation()->firstLineBox(); // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block. // FIXME: This is wrong for block-flows that are horizontal. // https://bugs.webkit.org/show_bug.cgi?id=46781 - bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->element()->renderer())->firstLineBox(); + bool prevInlineHasLineBox = downcast<RenderInline>(*inlineElementContinuation()->element()->renderer()).firstLineBox(); float topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : LayoutUnit(); float bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : LayoutUnit(); LayoutRect rect(additionalOffset.x(), additionalOffset.y() - topMargin, width(), height() + topMargin + bottomMargin); if (!rect.isEmpty()) - rects.append(pixelSnappedIntRect(rect)); + rects.append(rect); } else if (width() && height()) - rects.append(pixelSnappedIntRect(additionalOffset, size())); + rects.append(LayoutRect(additionalOffset, size())); if (!hasOverflowClip() && !hasControlClip()) { if (childrenInline()) addFocusRingRectsForInlineChildren(rects, additionalOffset, paintContainer); - 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); - } + for (auto& box : childrenOfType<RenderBox>(*this)) { + if (is<RenderListMarker>(box)) + continue; + + FloatPoint pos; + // FIXME: This doesn't work correctly with transforms. + if (box.layer()) + pos = box.localToContainerPoint(FloatPoint(), paintContainer); + else + pos = FloatPoint(additionalOffset.x() + box.x(), additionalOffset.y() + box.y()); + box.addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer); } } if (inlineElementContinuation()) - inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location()), paintContainer); -} - -RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const -{ - if (isAnonymousColumnsBlock()) - return createAnonymousColumnsWithParentRenderer(parent); - if (isAnonymousColumnSpanBlock()) - return createAnonymousColumnSpanWithParentRenderer(parent); - return createAnonymousWithParentRendererAndDisplay(parent, style().display()); + inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location())), paintContainer); } -ColumnInfo::PaginationUnit RenderBlock::paginationUnit() const +std::unique_ptr<RenderBlock> RenderBlock::createAnonymousBlockWithStyleAndDisplay(Document& document, const RenderStyle& style, EDisplay display) { - return ColumnInfo::Column; + // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ? + std::unique_ptr<RenderBlock> newBox; + if (display == FLEX || display == INLINE_FLEX) + newBox = std::make_unique<RenderFlexibleBox>(document, RenderStyle::createAnonymousStyleWithDisplay(style, FLEX)); + else + newBox = std::make_unique<RenderBlockFlow>(document, RenderStyle::createAnonymousStyleWithDisplay(style, BLOCK)); + + newBox->initializeStyle(); + return newBox; } LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const @@ -5213,66 +3587,95 @@ RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const return flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstPage() + blockOffset, true); } +static bool canComputeRegionRangeForBox(const RenderBlock& parentBlock, const RenderBox& childBox, const RenderFlowThread* flowThreadContainingBlock) +{ + ASSERT(!childBox.isRenderNamedFlowThread()); + + if (!flowThreadContainingBlock) + return false; + + if (!flowThreadContainingBlock->hasRegions()) + return false; + + if (!childBox.canHaveOutsideRegionRange()) + return false; + + return flowThreadContainingBlock->hasCachedRegionRangeForBox(parentBlock); +} + +bool RenderBlock::childBoxIsUnsplittableForFragmentation(const RenderBox& child) const +{ + RenderFlowThread* flowThread = flowThreadContainingBlock(); + bool checkColumnBreaks = flowThread && flowThread->shouldCheckColumnBreaks(); + bool checkPageBreaks = !checkColumnBreaks && view().layoutState()->m_pageLogicalHeight; + bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); + return child.isUnsplittableForPagination() || child.style().breakInside() == AvoidBreakInside + || (checkColumnBreaks && child.style().breakInside() == AvoidColumnBreakInside) + || (checkPageBreaks && child.style().breakInside() == AvoidPageBreakInside) + || (checkRegionBreaks && child.style().breakInside() == AvoidRegionBreakInside); +} + void RenderBlock::computeRegionRangeForBoxChild(const RenderBox& box) const { RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (!flowThread || !flowThread->hasRegions()) - return; + ASSERT(canComputeRegionRangeForBox(*this, box, flowThread)); RenderRegion* startRegion; RenderRegion* endRegion; LayoutUnit offsetFromLogicalTopOfFirstRegion = box.offsetFromLogicalTopOfFirstPage(); - if (box.isUnsplittableForPagination()) + if (childBoxIsUnsplittableForFragmentation(box)) startRegion = endRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion, true); else { startRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion, true); endRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion + logicalHeightForChild(box), true); } - flowThread->setRegionRangeForBox(&box, startRegion, endRegion); + flowThread->setRegionRangeForBox(box, startRegion, endRegion); } void RenderBlock::estimateRegionRangeForBoxChild(const RenderBox& box) const { RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (!flowThread || !flowThread->hasRegions() || !box.canHaveOutsideRegionRange()) + if (!canComputeRegionRangeForBox(*this, box, flowThread)) return; - if (box.isUnsplittableForPagination()) { + if (childBoxIsUnsplittableForFragmentation(box)) { computeRegionRangeForBoxChild(box); return; } - LogicalExtentComputedValues estimatedValues; - box.computeLogicalHeight(RenderFlowThread::maxLogicalHeight(), logicalTopForChild(box), estimatedValues); - + auto estimatedValues = box.computeLogicalHeight(RenderFlowThread::maxLogicalHeight(), logicalTopForChild(box)); LayoutUnit offsetFromLogicalTopOfFirstRegion = box.offsetFromLogicalTopOfFirstPage(); RenderRegion* startRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion, true); RenderRegion* endRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion + estimatedValues.m_extent, true); - flowThread->setRegionRangeForBox(&box, startRegion, endRegion); + flowThread->setRegionRangeForBox(box, startRegion, endRegion); } bool RenderBlock::updateRegionRangeForBoxChild(const RenderBox& box) const { RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (!flowThread || !flowThread->hasRegions() || !box.canHaveOutsideRegionRange()) + if (!canComputeRegionRangeForBox(*this, box, flowThread)) return false; - RenderRegion* startRegion = 0; - RenderRegion* endRegion = 0; + RenderRegion* startRegion = nullptr; + RenderRegion* endRegion = nullptr; flowThread->getRegionRangeForBox(&box, startRegion, endRegion); computeRegionRangeForBoxChild(box); - RenderRegion* newStartRegion = 0; - RenderRegion* newEndRegion = 0; + RenderRegion* newStartRegion = nullptr; + RenderRegion* newEndRegion = nullptr; flowThread->getRegionRangeForBox(&box, newStartRegion, newEndRegion); + + // Changing the start region means we shift everything and a relayout is needed. + if (newStartRegion != startRegion) + return true; + // The region range of the box has changed. Some boxes (e.g floats) may have been positioned assuming // a different range. - // FIXME: Be smarter about this. We don't need to relayout all the time. - if (newStartRegion != startRegion || newEndRegion != endRegion) + if (box.needsLayoutAfterRegionRangeChange() && newEndRegion != endRegion) return true; return false; @@ -5317,12 +3720,12 @@ bool RenderBlock::hasMarginBeforeQuirk(const RenderBox& child) const // If the child has the same directionality as we do, then we can just return its // margin quirk. if (!child.isWritingModeRoot()) - return child.isRenderBlock() ? toRenderBlock(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk(); + return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk(); // The child has a different directionality. If the child is parallel, then it's just // flipped relative to us. We can use the opposite edge. if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) - return child.isRenderBlock() ? toRenderBlock(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk(); + return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk(); // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about // whether or not authors specified quirky ems, since they're an implementation detail. @@ -5334,12 +3737,12 @@ bool RenderBlock::hasMarginAfterQuirk(const RenderBox& child) const // If the child has the same directionality as we do, then we can just return its // margin quirk. if (!child.isWritingModeRoot()) - return child.isRenderBlock() ? toRenderBlock(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk(); + return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk(); // The child has a different directionality. If the child is parallel, then it's just // flipped relative to us. We can use the opposite edge. if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) - return child.isRenderBlock() ? toRenderBlock(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk(); + return is<RenderBlock>(child) ? downcast<RenderBlock>(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk(); // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about // whether or not authors specified quirky ems, since they're an implementation detail. @@ -5355,12 +3758,10 @@ const char* RenderBlock::renderName() const return "RenderBlock (floating)"; if (isOutOfFlowPositioned()) return "RenderBlock (positioned)"; - if (isAnonymousColumnsBlock()) - return "RenderBlock (anonymous multi-column)"; - if (isAnonymousColumnSpanBlock()) - return "RenderBlock (anonymous multi-column span)"; if (isAnonymousBlock()) return "RenderBlock (anonymous)"; + if (isAnonymousInlineBlock()) + return "RenderBlock (anonymous inline-block)"; // FIXME: Temporary hack while the new generated content system is being implemented. if (isPseudoElement()) return "RenderBlock (generated)"; @@ -5370,28 +3771,10 @@ const char* RenderBlock::renderName() const return "RenderBlock (relative positioned)"; if (isStickyPositioned()) return "RenderBlock (sticky positioned)"; - if (isRunIn()) - return "RenderBlock (run-in)"; return "RenderBlock"; } -template <typename CharacterType> -static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) -{ - TextDirection textDirection = LTR; - bool directionalOverride = style.rtlOrdering() == VisualOrder; - - TextRun run(characters, length, 0, 0, expansion, textDirection, directionalOverride); - if (font.isSVGFont()) { - ASSERT(context); // FIXME: Thread a RenderObject& to this point so we don't have to dereference anything. - run.setRenderingContext(SVGTextRunRenderingContext::create(*context)); - } - - return run; -} - -template <typename CharacterType> -static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion, TextRunFlags flags) +TextRun RenderBlock::constructTextRun(StringView stringView, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags) { TextDirection textDirection = LTR; bool directionalOverride = style.rtlOrdering() == VisualOrder; @@ -5401,115 +3784,50 @@ static inline TextRun constructTextRunInternal(RenderObject* context, const Font if (flags & RespectDirectionOverride) directionalOverride |= isOverride(style.unicodeBidi()); } - TextRun run(characters, length, 0, 0, expansion, textDirection, directionalOverride); - if (font.isSVGFont()) { - ASSERT(context); // FIXME: Thread a RenderObject& to this point so we don't have to dereference anything. - run.setRenderingContext(SVGTextRunRenderingContext::create(*context)); - } - - return run; + return TextRun(stringView, 0, 0, expansion, textDirection, directionalOverride); } -#if ENABLE(8BIT_TEXTRUN) -TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const LChar* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) +TextRun RenderBlock::constructTextRun(const String& string, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags) { - return constructTextRunInternal(context, font, characters, length, style, expansion); + return constructTextRun(StringView(string), style, expansion, flags); } -#endif -TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const UChar* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) +TextRun RenderBlock::constructTextRun(const AtomicString& atomicString, const RenderStyle& style, ExpansionBehavior expansion, TextRunFlags flags) { - return constructTextRunInternal(context, font, characters, length, style, expansion); + return constructTextRun(StringView(atomicString), style, expansion, flags); } -TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const RenderText* text, const RenderStyle& style, TextRun::ExpansionBehavior expansion) +TextRun RenderBlock::constructTextRun(const RenderText& text, const RenderStyle& style, ExpansionBehavior expansion) { -#if ENABLE(8BIT_TEXTRUN) - if (text->is8Bit()) - return constructTextRunInternal(context, font, text->characters8(), text->textLength(), style, expansion); - return constructTextRunInternal(context, font, text->characters16(), text->textLength(), style, expansion); -#else - return constructTextRunInternal(context, font, text->deprecatedCharacters(), text->textLength(), style, expansion); -#endif + return constructTextRun(text.stringView(), style, expansion); } -TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const RenderText* text, unsigned offset, unsigned length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) +TextRun RenderBlock::constructTextRun(const RenderText& text, unsigned offset, unsigned length, const RenderStyle& style, ExpansionBehavior expansion) { - ASSERT(offset + length <= text->textLength()); -#if ENABLE(8BIT_TEXTRUN) - if (text->is8Bit()) - return constructTextRunInternal(context, font, text->characters8() + offset, length, style, expansion); - return constructTextRunInternal(context, font, text->characters16() + offset, length, style, expansion); -#else - return constructTextRunInternal(context, font, text->deprecatedCharacters() + offset, length, style, expansion); -#endif + unsigned stop = offset + length; + ASSERT(stop <= text.textLength()); + return constructTextRun(text.stringView(offset, stop), style, expansion); } -TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const String& string, const RenderStyle& style, TextRun::ExpansionBehavior expansion, TextRunFlags flags) +TextRun RenderBlock::constructTextRun(const LChar* characters, unsigned length, const RenderStyle& style, ExpansionBehavior expansion) { - unsigned length = string.length(); - -#if ENABLE(8BIT_TEXTRUN) - if (length && string.is8Bit()) - return constructTextRunInternal(context, font, string.characters8(), length, style, expansion, flags); - return constructTextRunInternal(context, font, string.deprecatedCharacters(), length, style, expansion, flags); -#else - return constructTextRunInternal(context, font, string.deprecatedCharacters(), length, style, expansion, flags); -#endif + return constructTextRun(StringView(characters, length), style, expansion); } -RenderBlock* RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderObject* parent, EDisplay display) +TextRun RenderBlock::constructTextRun(const UChar* characters, unsigned length, const RenderStyle& style, ExpansionBehavior expansion) { - // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ? - RenderBlock* newBox; - if (display == FLEX || display == INLINE_FLEX) - newBox = new RenderFlexibleBox(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), FLEX)); - else - newBox = new RenderBlockFlow(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), BLOCK)); - - newBox->initializeStyle(); - return newBox; -} - -RenderBlock* RenderBlock::createAnonymousColumnsWithParentRenderer(const RenderObject* parent) -{ - auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), BLOCK); - newStyle.get().inheritColumnPropertiesFrom(&parent->style()); - - RenderBlock* newBox = new RenderBlockFlow(parent->document(), std::move(newStyle)); - newBox->initializeStyle(); - return newBox; -} - -RenderBlock* RenderBlock::createAnonymousColumnSpanWithParentRenderer(const RenderObject* parent) -{ - auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), BLOCK); - newStyle.get().setColumnSpan(ColumnSpanAll); - - RenderBlock* newBox = new RenderBlockFlow(parent->document(), std::move(newStyle)); - newBox->initializeStyle(); - return newBox; + return constructTextRun(StringView(characters, length), style, expansion); } #ifndef NDEBUG void RenderBlock::checkPositionedObjectsNeedLayout() { - if (!gPositionedDescendantsMap) - return; - - TrackedRendererListHashSet* positionedDescendantSet = positionedObjects(); - if (!positionedDescendantSet) + auto* positionedDescendants = positionedObjects(); + if (!positionedDescendants) return; - for (auto it = positionedDescendantSet->begin(), end = positionedDescendantSet->end(); it != end; ++it) { - RenderBox* currBox = *it; - ASSERT(!currBox->needsLayout()); - } -} - -void RenderBlock::showLineTreeAndMark(const InlineBox*, const char*, const InlineBox*, const char*, const RenderObject*) const -{ - showRenderObject(); + for (auto* renderer : *positionedDescendants) + ASSERT(!renderer->needsLayout()); } #endif |