summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderBlock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderBlock.cpp')
-rw-r--r--Source/WebCore/rendering/RenderBlock.cpp4038
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 = &current;
- descendant = current.firstChild();
+ firstLetterContainer = &current;
+ 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